Bagaimana metode kelebihan beban dipilih ketika parameter adalah nilai nol literal?

98

Saya menemukan pertanyaan ini dalam sebuah kuis,

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);
   }
}

Output dari program ini adalah "Versi String". Tetapi saya tidak dapat memahami mengapa meneruskan null ke metode yang kelebihan beban memilih versi string. Apakah null variabel String tidak mengarah ke apa-apa?

Namun ketika kode diubah menjadi,

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);
   }
}

itu memberikan kesalahan kompilasi yang mengatakan "Metode metode (StringBuffer) ambigu untuk jenis MoneyCalc"

zakSyed
sumber
Anda dapat menetapkan string ke nilai null sehingga valid dan urutan untuk java dan sebagian besar bahasa pemrograman sesuai dengan tipe terdekat dan kemudian ke objek.
JonH
6
Rupanya ini ditutup sebagai duplikat oleh orang-orang yang hanya membaca judulnya. Pertanyaan sebenarnya di sini adalah tentang mengapa kelebihan beban tertentu dipilih, bukan "apa yang nol".
interjay

Jawaban:

102

Apakah null variabel String tidak mengarah ke apa-apa?

Referensi null dapat diubah menjadi ekspresi jenis kelas apa pun. Jadi dalam kasus String, ini bagus:

String x = null;

The Stringkelebihan disini dipilih karena compiler Java mengambil yang paling spesifik overload, sesuai bagian 15.12.2.5 dari JLS . Khususnya:

Intuisi informal adalah bahwa satu metode lebih spesifik daripada yang lain jika pemanggilan apa pun yang ditangani oleh metode pertama dapat diteruskan ke metode lain tanpa kesalahan jenis waktu kompilasi.

Dalam kasus kedua, kedua metode masih berlaku, tetapi tidak Stringjuga StringBufferlebih spesifik dari yang lain, oleh karena itu metode tidak lebih spesifik dari yang lain, maka kesalahan kompilator.

Jon Skeet
sumber
3
Dan bagaimana String overload lebih spesifik daripada Object overload?
pengguna1610015
12
Karena seorang Objectdapat mengambil jenis apa saja dan membungkusnya dalam sebuah Object, sedangkan a Stringhanya dapat mengambil String. Dalam hal ini, Stringadalah tipe yang lebih spesifik dibandingkan dengan Objecttipenya.
JonH
10
@zakSyed Jika Anda ditanyai apa yang lebih khusus "String" atau "Object", apa yang akan Anda katakan? Ternyata "String", bukan? Jika Anda ditanya: apa yang lebih khusus "String" atau "StringBuffer"? Tidak ada jawaban, keduanya adalah spesialisasi ortogonal, bagaimana Anda bisa memilih di antara keduanya? Maka Anda harus eksplisit mengenai mana yang Anda inginkan (yaitu dengan melemparkan referensi nol Anda question.method((String)null))
Edwin Dalorzo
1
@ JonSkeet: Itu masuk akal. Jadi pada dasarnya ia mencari metode sesuai dengan aturan yang paling spesifik dan jika tidak dapat memutuskan mana yang lebih spesifik maka itu akan menimbulkan kesalahan waktu kompilasi.
zakSyed
3
@JonH "null" adalah tipe referensi, jika salah satu metode menerima tipe primitif sebagai parameter (yaitu int), metode ini bahkan tidak akan dipertimbangkan oleh compiler ketika memilih metode yang tepat untuk memanggil referensi tipe null. Ini membingungkan jika yang Anda maksud dengan "Int" adalah java.lang.Integer atau jika yang Anda maksud adalah tipe primitif int.
Edwin Dalorzo
9

Selain itu, JLS 3.10.7 juga menyatakan bahwa "null" adalah nilai literal dari "tipe null". Oleh karena itu ada tipe yang disebut "null".

Kemudian, JLS 4.1 menyatakan bahwa terdapat tipe null yang tidak mungkin untuk mendeklarasikan variabel, tetapi Anda dapat menggunakannya hanya melalui literal null. Kemudian dikatakan:

Referensi null selalu dapat menjalani konversi referensi yang melebar ke jenis referensi apa pun.

Mengapa compiler memilih untuk melebarkannya ke String mungkin bisa dijelaskan dengan baik dalam jawaban Jon .

Edwin Dalorzo
sumber
Saya cukup yakin tipe itu adalah Void.
xavierm02
2
@ xavierm02 Tidak disebutkan itu dalam Spesifikasi Bahasa Java. Dapatkah Anda mengutip referensi Anda sehingga kami semua dapat memverifikasi klaim Anda?
Edwin Dalorzo
Saya tidak pernah membaca hal itu. Tetapi saya melakukan beberapa Java musim panas ini dan Anda dapat membebani metode mengambil Objek dengan metode mengambil objek Void.
xavierm02
Ini lebih dari sebuah kelas daripada tipe.
xavierm02
4
@ xavierm02 Tentu saja Anda bisa. Anda dapat membebani metode di Java dengan jenis lain apa pun yang Anda inginkan. Itu, bagaimanapun, tidak ada hubungannya dengan pertanyaan, atau jawaban saya.
Edwin Dalorzo
2

Anda dapat menetapkan stringke nullnilai sehingga berlaku dan agar java dan kebanyakan bahasa pemrograman cocok dengan jenis yang paling dekat dan kemudian ke objek.

JonH
sumber
2

Untuk menjawab pertanyaan dalam judul: nullbukan a Stringatau an Object, tapi referensi ke salah satunya dapat diberikan null.

Saya benar-benar terkejut kode ini bahkan dikompilasi. Saya mencoba sesuatu yang serupa sebelumnya dan saya mendapat kesalahan kompiler yang mengatakan bahwa panggilan itu ambigu.

Namun, dalam kasus ini, tampaknya penyusun memilih metode yang paling rendah dalam rantai makanan. Ini mengasumsikan bahwa Anda menginginkan versi metode yang paling tidak umum untuk membantu Anda.

Saya harus melihat apakah saya dapat menggali contoh di mana saya mendapat kesalahan kompiler dalam skenario yang (tampaknya) persis sama ini, meskipun ...]

EDIT: Saya mengerti. Dalam versi yang saya buat, saya memiliki dua metode kelebihan beban, menerima a Stringdan Integer. Dalam skenario ini, tidak ada parameter "paling spesifik" (seperti dalam ObjectdanString ), sehingga tidak dapat memilih di antara , tidak seperti di kode Anda.

Pertanyaan yang sangat keren!

asteri
sumber
null bukan keduanya tetapi dapat ditugaskan ke keduanya, itulah perbedaannya dan itu akan mengkompilasi mengapa tidak - itu kode yang benar-benar valid.
JonH
0

Karena tipe String lebih spesifik daripada tipe Object. Katakanlah Anda menambahkan satu metode lagi yang menggunakan tipe Integer.

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

Kemudian Anda akan mendapatkan kesalahan kompiler yang mengatakan bahwa panggilan tersebut ambigu. Seperti sekarang kami dua metode yang sama-sama spesifik dengan prioritas yang sama.

BSingh
sumber
0

Kompiler Java memberikan sebagian besar tipe kelas turunan untuk menetapkan null.

Berikut contoh untuk memahaminya:

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);
    }
}

keluaran: Kelas B.

di samping itu:

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);
    }
}

Hasil: Metode fun (C) ambigu untuk tipe MyTest

Semoga dapat membantu memahami kasus ini dengan lebih baik.

Ravi
sumber
0

Sumber: https://docs.oracle.com/javase/specs/jls/se8/html/jls-15.html#jls-15.12.2.5
Konsep: Metode paling spesifik
Penjelasan: Jika lebih dari satu metode anggota dapat diakses dan berlaku untuk pemanggilan metode, Anda harus memilih salah satu untuk menyediakan deskriptor untuk pengiriman metode run-time. Bahasa pemrograman Java menggunakan aturan bahwa metode yang paling spesifik dipilih. Cobalah mentransmisikan null ke tipe tertentu dan metode yang Anda inginkan akan dipanggil secara otomatis.

MP
sumber
Dalam contoh pertama, String memperluas Object, jadi "paling spesifik" adalah metode yang mengambil String, tetapi pada contoh kedua, String dan StringBuffer keduanya memperluas Object, sehingga keduanya sama-sama spesifik dan oleh karena itu kompiler tidak dapat membuat pilihan
David Kerr
-1

Saya tidak akan mengatakan keduanya. NULL adalah keadaan bukan nilai. Lihat tautan ini untuk info lebih lanjut tentang ini (artikel ini berlaku untuk SQL, tetapi saya pikir ini juga membantu pertanyaan Anda).

kutub Utara
sumber
nulladalah nilai yang sangat pasti, sebagaimana didefinisikan dalam JLS.
Sotirios Delimanolis