วิธีการโอเวอร์โหลดถูกเลือกอย่างไรเมื่อพารามิเตอร์เป็นค่า null ตามตัวอักษร


98

ฉันเจอคำถามนี้ในแบบทดสอบ

public class MoneyCalc {

   public void method(Object o) {
      System.out.println("Object Verion");
   }

   public void method(String s) {
      System.out.println("String Version");
   }

   public static void main(String args[]) {
      MoneyCalc question = new MoneyCalc();
      question.method(null);
   }
}

ผลลัพธ์ของโปรแกรมนี้คือ "String Version" แต่ฉันไม่เข้าใจว่าทำไมการส่ง null ไปยังเมธอดที่โอเวอร์โหลดจึงเลือกเวอร์ชันสตริง เป็น null ตัวแปร String ชี้ไปที่อะไร?

อย่างไรก็ตามเมื่อเปลี่ยนรหัสเป็น

public class MoneyCalc {

   public void method(StringBuffer sb) {
      System.out.println("StringBuffer Verion");
   }

   public void method(String s) {
      System.out.println("String Version");
   }

   public static void main(String args[]) {
      MoneyCalc question = new MoneyCalc();
      question.method(null);
   }
}

มันทำให้เกิดข้อผิดพลาดในการคอมไพล์ว่า "method method (StringBuffer) ไม่ชัดเจนสำหรับประเภท MoneyCalc"


คุณสามารถกำหนดสตริงให้เป็นค่า null เพื่อให้ถูกต้องและลำดับสำหรับ java และภาษาโปรแกรมส่วนใหญ่จะพอดีกับประเภทที่ใกล้เคียงที่สุดแล้วจึงจะคัดค้าน
JonH


6
เห็นได้ชัดว่าสิ่งนี้ถูกปิดเนื่องจากซ้ำกันโดยผู้ที่อ่านเฉพาะชื่อเรื่อง คำถามที่แท้จริงที่นี่คือสาเหตุที่มีการเลือกโอเวอร์โหลดเฉพาะไม่ใช่ "ค่าว่าง"
interjay

คำตอบ:


102

เป็น null ตัวแปร String ชี้ไปที่อะไร?

การอ้างอิง null สามารถแปลงเป็นนิพจน์ของคลาสประเภทใดก็ได้ ดังนั้นในกรณีStringนี้ก็ใช้ได้:

String x = null;

Stringเกินพิกัดที่นี่จะเลือกเพราะคอมไพเลอร์ Java หยิบเฉพาะเจาะจงมากที่สุดเกินตามส่วน 15.12.2.5 ของ JLS โดยเฉพาะอย่างยิ่ง:

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

ในกรณีที่สองของคุณทั้งสองวิธีจะยังคงบังคับ แต่ไม่Stringว่ามิได้StringBufferเป็นเฉพาะเจาะจงมากขึ้นกว่าที่อื่น ๆ จึงไม่เป็นวิธีการเฉพาะเจาะจงมากขึ้นกว่าที่อื่น ๆ ด้วยเหตุนี้ข้อผิดพลาดคอมไพเลอร์


3
และ String overload มีความเฉพาะเจาะจงมากกว่า Object overload อย่างไร?
user1610015

12
เนื่องจากObjectสามารถใช้ประเภทใดก็ได้และห่อไว้ในObjectขณะที่Stringสามารถใช้ไฟล์String. ในกรณีนี้Stringมีความเฉพาะเจาะจงของประเภทมากกว่าเมื่อเทียบกับObjectประเภท
JonH

10
@zakSyed หากคุณถูกถามว่า "String" หรือ "Object" มีความเชี่ยวชาญมากขึ้นคุณจะตอบว่าอย่างไร? เห็นได้ชัดว่า "สตริง" ใช่ไหม? หากคุณถูกถามว่า "String" หรือ "StringBuffer" มีความเชี่ยวชาญกว่าอย่างไร ไม่มีคำตอบพวกเขาทั้งสองมีความเชี่ยวชาญด้านมุมฉากคุณจะเลือกระหว่างพวกเขาได้อย่างไร? ถ้าอย่างนั้นคุณต้องชัดเจนเกี่ยวกับสิ่งที่คุณต้องการ (เช่นโดยการส่งข้อมูลอ้างอิงว่างของคุณquestion.method((String)null))
Edwin Dalorzo

1
@JonSkeet: นั่นก็สมเหตุสมผลแล้ว โดยพื้นฐานแล้วมันจะมองหาวิธีการตามกฎที่เฉพาะเจาะจงที่สุดและหากไม่สามารถตัดสินใจได้ว่าวิธีใดที่เฉพาะเจาะจงมากกว่านั้นก็จะทำให้เกิดข้อผิดพลาดในการคอมไพล์
zakSyed

3
@JonH "null" เป็นชนิดการอ้างอิงหากวิธีใดวิธีหนึ่งได้รับประเภทดั้งเดิมเป็นพารามิเตอร์ (เช่น int) คอมไพเลอร์จะไม่พิจารณาด้วยซ้ำเมื่อเลือกวิธีการที่เหมาะสมในการเรียกใช้สำหรับการอ้างอิงประเภท null เป็นเรื่องที่น่าสับสนหาก "Int" คุณหมายถึง java.lang.Integer หรือหากคุณหมายถึงประเภท int ดั้งเดิม
Edwin Dalorzo

9

นอกจากนี้JLS 3.10.7ยังประกาศว่า "null" เป็นค่าตามตัวอักษรของ "null type" ดังนั้นจึงมีประเภทที่เรียกว่า "null"

ต่อมาJLS 4.1ระบุว่ามีประเภท null ซึ่งไม่สามารถประกาศตัวแปรได้ แต่คุณสามารถใช้ผ่านลิเทอรัล null เท่านั้น ต่อมามีข้อความว่า:

การอ้างอิงที่เป็นโมฆะสามารถแปลงการอ้างอิงที่กว้างขึ้นเป็นประเภทการอ้างอิงใด ๆ ได้เสมอ

ทำไมคอมไพเลอร์เลือกที่จะขยายไปยัง String อาจจะได้อธิบายในคำตอบของจอน


ฉันค่อนข้างแน่ใจว่าประเภทนั้นเป็นโมฆะ
xavierm02

2
@ xavierm02 ไม่มีการกล่าวถึงสิ่งนั้นในข้อกำหนดภาษา Java คุณสามารถอ้างอิงข้อมูลอ้างอิงของคุณเพื่อให้เราทุกคนสามารถตรวจสอบการอ้างสิทธิ์ของคุณได้หรือไม่?
Edwin Dalorzo

ฉันไม่เคยอ่านสิ่งนั้น แต่ฉันทำ Java ในฤดูร้อนนี้และคุณสามารถโอเวอร์โหลดเมธอดที่ใช้ Object ด้วยเมธอดที่ใช้ Void object ได้
xavierm02

เป็นชั้นเรียนมากกว่าประเภท
xavierm02

4
@ xavierm02 แน่นอนคุณทำได้ คุณสามารถโอเวอร์โหลดเมธอดใน Java ด้วยชนิดอื่นใดก็ได้ที่คุณต้องการ อย่างไรก็ตามไม่มีส่วนเกี่ยวข้องกับคำถามหรือคำตอบของฉัน
Edwin Dalorzo

2

คุณสามารถกำหนด a stringให้กับnullค่าเพื่อให้ถูกต้องและลำดับของ java และภาษาโปรแกรมส่วนใหญ่จะพอดีกับประเภทที่ใกล้เคียงที่สุดแล้วจึงจะคัดค้าน


2

ในการตอบคำถามในชื่อเรื่อง: nullไม่ใช่ a Stringหรือ an Objectแต่สามารถกำหนดให้nullอ้างอิงได้

ฉันแปลกใจจริง ๆ ที่รหัสนี้รวบรวมได้ ฉันลองทำสิ่งที่คล้ายกันก่อนหน้านี้และฉันได้รับข้อผิดพลาดของคอมไพเลอร์แจ้งว่าการโทรไม่ชัดเจน

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

ฉันจะต้องดูว่าฉันสามารถขุดตัวอย่างที่ฉันได้รับข้อผิดพลาดของคอมไพเลอร์ในสถานการณ์เดียวกันนี้ (ดูเหมือน) แน่นอนหรือไม่แม้ว่า ... ]

แก้ไข: ฉันเข้าใจ ในเวอร์ชันที่ฉันทำฉันมีสองวิธีที่มากเกินไปในการยอมรับ a StringและInteger. ในสถานการณ์นี้ไม่มีพารามิเตอร์ที่ "เฉพาะเจาะจงที่สุด" (เช่นเดียวกับObjectและString) ดังนั้นจึงไม่สามารถเลือกระหว่างพารามิเตอร์เหล่านี้ได้ซึ่งแตกต่างจากในโค้ดของคุณ

คำถามเด็ดมาก!


null ไม่ได้เป็นเช่นนั้น แต่สามารถกำหนดให้กับทั้งสองได้นั่นคือความแตกต่างและจะรวบรวมว่าทำไมถึงไม่เป็นเช่นนั้น - เป็นรหัสที่ถูกต้อง
JonH

0

เนื่องจากประเภท String มีความเฉพาะเจาะจงมากกว่าประเภท Object สมมติว่าคุณเพิ่มอีกหนึ่งวิธีที่ใช้ประเภทจำนวนเต็ม

public void method(Integer i) {
      System.out.println("Integer Version");
   }

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


0

คอมไพเลอร์ Java ให้ประเภทคลาสที่ได้รับส่วนใหญ่เพื่อกำหนด null

นี่คือตัวอย่างเพื่อทำความเข้าใจ:

class A{

    public void methodA(){
        System.out.println("Hello methodA");
    }
}

class B extends A{
    public void methodB(){
        System.out.println("Hello methodB");
    }
}

class C{
    public void methodC(){
        System.out.println("Hello methodC");
    }
}

public class MyTest {

     public static void fun(B Obj){
         System.out.println("B Class.");
     }
     public static void fun(A Obj){
         System.out.println("A Class.");
     }

    public static void main(String[] args) {
        fun(null);
    }
}

เอาต์พุต: B Class

ในทางกลับกัน:

public class MyTest {

     public static void fun(C Obj){
         System.out.println("B Class.");
     }
     public static void fun(A Obj){
         System.out.println("A Class.");
     }

    public static void main(String[] args) {
        fun(null);
    }
}

ผลลัพธ์:วิธีการสนุก (C) ไม่ชัดเจนสำหรับประเภท MyTest

หวังว่าจะช่วยให้เข้าใจกรณีนี้ดีขึ้น


0

ที่มา: https://docs.oracle.com/javase/specs/jls/se8/html/jls-15.html#jls-15.12.2.5
แนวคิด:วิธีการที่เฉพาะเจาะจงที่สุด
คำอธิบาย: หากมีเมธอดสมาชิกมากกว่าหนึ่งวิธีสามารถเข้าถึงได้และ ใช้ได้กับการเรียกใช้เมธอดจำเป็นต้องเลือกอย่างใดอย่างหนึ่งเพื่อระบุตัวอธิบายสำหรับการจัดส่งเมธอดรันไทม์ ภาษาโปรแกรม Java ใช้กฎที่เลือกวิธีที่เฉพาะเจาะจงที่สุด ลองแคสต์ null ไปยังประเภทที่ต้องการและวิธีที่คุณต้องการจะถูกเรียกโดยอัตโนมัติ


ในตัวอย่างแรก String ขยาย Object ดังนั้น "ที่เฉพาะเจาะจงที่สุด" คือวิธีการที่ใช้ String แต่ในตัวอย่างที่สอง String และ StringBuffer ทั้งสองขยาย Object ดังนั้นจึงมีความเฉพาะเจาะจงเท่ากันดังนั้นคอมไพเลอร์จึงไม่สามารถเลือกได้
David Kerr

-1

ฉันจะบอกว่าไม่ NULL เป็นสถานะไม่ใช่ค่า ดูลิงค์นี้เพื่อดูข้อมูลเพิ่มเติมเกี่ยวกับเรื่องนี้ (บทความนี้ใช้กับ SQL แต่ฉันคิดว่ามันช่วยในคำถามของคุณได้เช่นกัน)


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