วิธีการโอเวอร์โหลดสำหรับอาร์กิวเมนต์ว่าง


138

ฉันได้เพิ่มสามวิธีพร้อมพารามิเตอร์:

public static  void doSomething(Object obj) {
    System.out.println("Object called");
}

public static  void doSomething(char[] obj) {
    System.out.println("Array called");
}

public static  void doSomething(Integer obj) {
    System.out.println("Integer called");
}

เมื่อผมโทรdoSomething(null)แล้วคอมไพเลอร์โยนข้อผิดพลาดเป็นวิธีการที่ไม่ชัดเจน ดังนั้นเป็นปัญหาเพราะIntegerและchar[]วิธีการหรือIntegerและObjectวิธีการ?


3
เพียงแค่เปลี่ยนไปInteger int
Mudassir

2
@Mudassir: แล้วมันจะแก้ปัญหาอะไรได้?
Joachim Sauer

2
@ Joachim Sauer: หากเปลี่ยนจาก Integer เป็น int แล้ว null จะไม่ถูกอ้างถึงประเภทดั้งเดิมใน Java ดังนั้นคอมไพเลอร์จะไม่แสดงข้อผิดพลาด
Phani

@ Joachim Sauer: จะไม่ทำให้เกิดreference to doSomething is ambiguousข้อผิดพลาด
Mudassir

คำตอบ:


220

Java จะพยายามใช้เมธอดเวอร์ชันที่เฉพาะเจาะจงที่สุดที่มีอยู่เสมอ (ดูJLS §15.12.2 )

Object, char[]และIntegerทุกคนสามารถใช้nullเป็นค่าที่ถูกต้อง ดังนั้นทั้ง 3 เวอร์ชันจึงใช้ได้ดังนั้น Java จะต้องหาเวอร์ชันที่เจาะจงที่สุด

เนื่องจากObjectเป็น super-type char[]เวอร์ชันอาร์เรย์จึงมีความเฉพาะเจาะจงมากกว่าObject-version ดังนั้นหากมีเพียงสองวิธีนี้ระบบchar[]จะเลือกเวอร์ชัน

เมื่อทั้งสองchar[]และIntegerรุ่นที่มีอยู่แล้วทั้งสองของพวกเขาเป็นที่เฉพาะเจาะจงมากขึ้นกว่าObjectแต่ไม่มีผู้ใดเป็นเฉพาะเจาะจงมากขึ้นกว่าที่อื่น ๆ ดังนั้น Java ไม่สามารถตัดสินใจที่หนึ่งที่จะโทร ในกรณีนี้คุณจะต้องระบุอย่างชัดเจนว่าคุณต้องการเรียกใช้อาร์กิวเมนต์ใดโดยการส่งอาร์กิวเมนต์เป็นประเภทที่เหมาะสม

โปรดทราบว่าในทางปฏิบัติปัญหานี้เกิดขึ้นไม่บ่อยนัก เหตุผลนี้จะเกิดขึ้นเฉพาะเมื่อคุณกำลังเรียกใช้เมธอดอย่างชัดเจนโดยมีnullหรือด้วยตัวแปรประเภทที่ค่อนข้างไม่เจาะจง (เช่นObject)

ในทางตรงกันข้ามการเรียกร้องต่อไปนี้จะไม่คลุมเครืออย่างสมบูรณ์แบบ:

char[] x = null;
doSomething(x);

แม้ว่าคุณจะยังคงส่งผ่านค่าอยู่nullแต่ Java ก็รู้ดีว่าจะเรียกใช้เมธอดใดเนื่องจากจะพิจารณาประเภทของตัวแปร


1
สิ่งนี้ระบุไว้สำหรับ Java 7 นั่นใช้กับ Java เวอร์ชันก่อนหน้าด้วยหรือไม่ ฉันหมายถึง: หากคุณมีลายเซ็นเมธอดหลายตัวที่มีพารามิเตอร์ตามลำดับชั้นประเภทเท่านั้นคุณจะอยู่ที่ด้านบันทึกโดยมีค่า null เป็นค่าจริงหรือไม่? และถ้าคุณสร้าง "ลำดับชั้นสำหรับ" เหมือนในตัวอย่างที่นี่แสดงว่าคุณไม่?
Christian Gosch

3
ฉันค่อนข้างแน่ใจว่ากฎเหล่านั้นเหมือนกันอย่างน้อยทุกอย่างตั้งแต่ Java 1.1 (ยกเว้นการเพิ่ม generics อย่างเห็นได้ชัด)
Joachim Sauer

นี่หมายความว่าถ้าคอมไพเลอร์ต้องเลือกระหว่าง doSomething (String str) และ doSomething (Object obj) ระหว่างรันไทม์ด้วย doSomething (null) doSomething (String str) จะถูกเรียก
Sameer

43

แต่ละคู่ของวิธีการทั้งสามนี้มีความคลุมเครือในตัวเองเมื่อเรียกด้วยnullอาร์กิวเมนต์ เนื่องจากพารามิเตอร์แต่ละประเภทเป็นประเภทอ้างอิง

ต่อไปนี้เป็นสามวิธีในการเรียกวิธีการเฉพาะของคุณด้วย null

doSomething( (Object) null);
doSomething( (Integer) null);
doSomething( (char[]) null);

ฉันขอแนะนำให้ลบความคลุมเครือนี้ออกหากคุณวางแผนที่จะเรียกวิธีการเหล่านี้ด้วยnullอาร์กิวเมนต์ การออกแบบดังกล่าวเชิญชวนให้เกิดข้อผิดพลาดในอนาคต


1
เท่านั้นInteger- char[]คู่มีความคลุมเครือเนื่องจากในอีกสองกรณีคอมไพเลอร์ Java สามารถเลือกตัวเลือกที่เฉพาะเจาะจงมากที่สุดเช่น @JoachimSauer อธิบายไว้
kajacx

1
@kajacx: ​​คำถามเดิมของ OPs เกี่ยวกับการเรียกเมธอดเหล่านี้โดยใช้nullเป็นพารามิเตอร์ ภายใต้เงื่อนไขเบื้องต้นนั้นทั้งสามคู่มีความคลุมเครือ สำหรับกรณีทั่วไปฉันยอมรับว่ามีเพียงInteger - char[]คู่ที่คลุมเครือ
jmg

สิ่งที่เกี่ยวกับdoSomething(null)สำหรับการ public static void doSomething(String str) { System.out.println("String called"); } นี้จะกลับสตริงที่เรียกว่า
Sameer

เป็นไปได้หรือไม่ที่จะใช้ anotation เช่น "nullable" หรือ "not nullable" และตั้งค่าการประกาศเพื่อให้มีเพียงวิธีเดียวที่คุณต้องการรับค่าว่างเพื่อให้อาร์กิวเมนต์ "null" ที่ชัดเจน (แต่โดยนัยโดยนัย) จะเลือกอย่างไม่คลุมเครือเสมอ โอเวอร์โหลดเฉพาะ ??
peterk

5

nullเป็นค่าที่ถูกต้องสำหรับหนึ่งในสามประเภท ดังนั้นคอมไพเลอร์จึงไม่สามารถตัดสินใจได้ว่าจะใช้ฟังก์ชันใด ใช้สิ่งที่เหมือนdoSomething((Object)null)หรือdoSomething((Integer)null)แทน


ฉันลบเมธอดด้วยพารามิเตอร์ Integer มันเรียกใช้ฟังก์ชันและส่งคืนเอาต์พุตเป็น"Array called"ดังนั้นสัญญาระหว่างArray กับ Objectคืออะไร?
Phani

3
อาร์เรย์ Java ยังเป็นวัตถุ
Zds

2

ทุกคลาสใน Java ขยายคลาส Object แม้แต่คลาส Integer ยังขยาย Object ดังนั้นทั้ง Object และ Integer จึงถือเป็นอินสแตนซ์ของ Object ดังนั้นเมื่อคุณส่งค่า null เป็นพารามิเตอร์กว่าคอมไพลเลอร์จะสับสนว่าเมธอดอ็อบเจ็กต์ใดที่จะเรียกเช่น With parameter Object หรือ parameter Integer เนื่องจากทั้งสองเป็นอ็อบเจ็กต์และการอ้างอิงของมันอาจเป็นค่าว่าง แต่ primitives ใน java ไม่ขยาย Object


1

ฉันได้ลองสิ่งนี้แล้วและเมื่อมีเมธอดที่โอเวอร์โหลดหนึ่งคู่และหนึ่งในนั้นมีประเภทพารามิเตอร์ Object จากนั้นคอมไพเลอร์จะเลือกเมธอดที่มีประเภทที่เฉพาะเจาะจงมากขึ้นเสมอ แต่เมื่อมีมากกว่าหนึ่งประเภทคอมไพเลอร์จะแสดงข้อผิดพลาดของเมธอดที่ไม่ชัดเจน

เนื่องจากนี่เป็นเหตุการณ์เวลาคอมไพล์สิ่งนี้จะเกิดขึ้นได้ก็ต่อเมื่อมีเจตนาส่งค่าว่างไปยังเมธอดนี้ หากทำโดยเจตนาจะเป็นการดีกว่าที่จะโอเวอร์โหลดเมธอดนี้อีกครั้งโดยไม่มีพารามิเตอร์หรือสร้างวิธีอื่นเลย


0

มีความคลุมเครือเนื่องจาก doSomething (char [] obj) และ doSomething (Integer obj)

ถ่าน [] และจำนวนเต็มต่างกันที่เหนือกว่าสำหรับค่าว่างนั่นจึงเป็นเหตุให้มีความคลุมเครือ


0
class Sample{
  public static void main (String[] args) {
       Sample s = new Sample();
       s.printVal(null);

    } 
    public static void printVal(Object i){
        System.out.println("obj called "+i);
    }

    public static void printVal(Integer i){
        System.out.println("Int called "+i);
    }
}

ผลลัพธ์คือ Int เรียกว่า null ดังนั้นความคลุมเครือจึงอยู่กับ char [] และ Integer

โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.