Pertanyaan Mengapa autoboxing membuat beberapa panggilan ambigu di Jawa?


Saya perhatikan hari ini bahwa auto-boxing kadang-kadang dapat menyebabkan ambiguitas dalam metode overload resolusi. Contoh paling sederhana tampaknya seperti ini:

public class Test {
    static void f(Object a, boolean b) {}
    static void f(Object a, Object b) {}

    static void m(int a, boolean b) { f(a,b); }
}

Ketika dikompilasi, itu menyebabkan kesalahan berikut:

Test.java:5: reference to f is ambiguous, both method
    f(java.lang.Object,boolean) in Test and method
    f(java.lang.Object,java.lang.Object) in Test match

static void m(int a, boolean b) { f(a, b); }
                                  ^

Perbaikan untuk kesalahan ini sepele: cukup gunakan tinju otomatis eksplisit:

static void m(int a, boolean b) { f((Object)a, b); }

Yang benar memanggil kelebihan pertama seperti yang diharapkan.

Jadi mengapa resolusi overload gagal? Mengapa compiler auto-box argumen pertama, dan menerima argumen kedua secara normal? Mengapa saya harus meminta tinju otomatis secara eksplisit?


32
2018-02-01 19:20


asal


Jawaban:


Ketika Anda melemparkan argumen pertama ke Object sendiri, compiler akan cocok dengan metode tanpa menggunakan autoboxing (JLS3 15.12.2):

Fase pertama (§15.12.2.2) tampil   membebani resolusi tanpa mengizinkan   konversi tinju atau unboxing, atau   penggunaan metode variabel arity   doa. Jika tidak ada metode yang berlaku   ditemukan selama fase ini   pemrosesan berlanjut ke yang kedua   tahap.

Jika Anda tidak mentransmisikannya secara eksplisit, ia akan pergi ke tahap kedua mencoba menemukan metode yang cocok, memungkinkan autoboxing, dan kemudian benar-benar ambigu, karena argumen kedua Anda dapat dicocokkan dengan boolean atau Object.

Fase kedua (§15.12.2.3) tampil   membebani resolusi saat mengizinkan   tinju dan unboxing, tapi tetap saja   menghalangi penggunaan variabel arity   pemanggilan metode.

Mengapa, pada tahap kedua, bukankah compiler memilih metode kedua karena tidak ada autoboxing dari argumen boolean yang diperlukan? Karena setelah menemukan dua metode yang cocok, hanya konversi subtipe yang digunakan untuk menentukan metode yang paling spesifik dari keduanya, terlepas dari tinju atau unboxing yang terjadi untuk mencocokkannya di tempat pertama (§15.12.2.5).

Juga: compiler tidak selalu dapat memilih metode yang paling spesifik berdasarkan pada jumlah auto (un) tinju yang dibutuhkan. Itu masih bisa menghasilkan kasus yang ambigu. Misalnya, ini masih ambigu:

public class Test {
    static void f(Object a, boolean b) {}
    static void f(int a, Object b) {}

    static void m(int a, boolean b) { f(a, b); } // ambiguous
}

Ingat bahwa algoritma untuk memilih metode yang cocok (langkah waktu kompilasi 2) ditetapkan dan dijelaskan dalam JLS. Sekali dalam fase 2 tidak ada autoboxing atau unboxing selektif. Compiler akan mencari semua metode yang dapat diakses (baik metode dalam kasus ini) dan berlaku (lagi dua metode), dan hanya kemudian memilih yang paling spesifik tanpa melihat tinju / unboxing, yang ambigu di sini.


32
2018-02-01 19:47



Kompilator melakukan kotak-otomatis argumen pertama. Setelah itu dilakukan, itu argumen kedua yang ambigu, karena bisa dilihat sebagai boolean atau Object.

Halaman ini menjelaskan aturan untuk autoboxing dan memilih metode mana yang akan diminta. Compiler pertama mencoba memilih metode tanpa menggunakan autoboxing sama sekali, karena tinju dan unboxing membawa penalti kinerja. Jika tidak ada metode yang dapat dipilih tanpa beralih ke tinju, seperti dalam kasus ini, maka tinju ada di meja untuk semua argumen untuk metode itu.


4
2018-02-01 19:38



Ketika kamu berkata f (a, b), compiler bingung tentang fungsi mana yang harus dijadikan referensi.

Hal ini karena Sebuah adalah int, tetapi argumen yang diharapkan dalam f adalah Objek. Jadi pelapor memutuskan untuk berkonversi Sebuah ke Objek. Sekarang masalahnya adalah, jika Sebuah dapat dikonversi menjadi objek, jadi bisa b.

Ini berarti bahwa panggilan fungsi dapat merujuk ke salah satu definisi. Ini membuat panggilan menjadi ambigu.

Ketika Anda mengkonversi Sebuah ke Objek secara manual, kompiler hanya mencari kecocokan terdekat dan kemudian merujuk padanya.

Mengapa kompiler tidak memilih   fungsi yang bisa dihubungi dengan "melakukan   paling sedikit mungkin   konversi tinju / unboxing "?

Lihat kasus berikut:

f(boolean a, Object b)
f(Object a , boolean b)

Jika kita memanggil seperti f (boolean a, boolean b), fungsi apa yang harus dipilih? Itu ambigous kan? Demikian pula, ini akan menjadi lebih kompleks ketika banyak argumen hadir. Jadi kompiler memilih untuk memberi Anda peringatan sebagai gantinya.

Karena tidak ada cara untuk mengetahui yang mana salah satu fungsi yang benar-benar ingin dipanggil oleh programmer, kompilator memberikan kesalahan.


3
2018-02-01 19:36



Jadi mengapa resolusi yang berlebihan   gagal? Mengapa tidak dilakukan compiler auto-box   argumen pertama, dan terima   argumen kedua biasanya? Kenapa aku   harus meminta auto-tinju   secara eksplisit?

Itu tidak menerima argumen kedua biasanya. Ingat bahwa "boolean" dapat dimasukkan ke sebuah Objek juga. Anda bisa secara eksplisit melemparkan argumen boolean ke Object juga dan itu akan berhasil.


2
2018-02-01 19:35



Lihat http://java.sun.com/docs/books/jls/third_edition/html/expressions.html#20448

Para pemain membantu karena tidak ada tinju yang diperlukan untuk menemukan metode untuk menelepon. Tanpa pemain, percobaan kedua adalah mengijinkan tinju dan kemudian boolean dapat dikotak-kotak.

Lebih baik memiliki spesifikasi yang jelas dan bisa dimengerti untuk mengatakan apa yang akan terjadi daripada membuat orang-orang menebak.


2
2018-02-01 19:46



Java compiler menyelesaikan metode dan konstruktor kelebihan beban secara bertahap. Pada fase pertama [§15.12.2.2], itu mengidentifikasi metode yang berlaku dengan subtyping [§4.10]. Dalam contoh ini, tidak ada metode yang berlaku, karena int bukan merupakan subtipe Objek.

Pada fase kedua [§15.12.2.3], kompilator mengidentifikasi metode yang berlaku dengan konversi pemanggilan metode [§5.3], yang merupakan kombinasi dari autoboxing dan subtyping. Argumen int dapat dikonversi menjadi Integer, yang merupakan subtipe dari Object, untuk kedua overload. Argumen boolean tidak memerlukan konversi untuk kelebihan pertama, dan dapat dikonversi ke Boolean, subtipe Objek, untuk yang kedua. Oleh karena itu, kedua metode tersebut berlaku pada fase kedua.

Karena ada lebih dari satu metode yang berlaku, compiler harus menentukan mana yang paling spesifik [§15.12.2.5]. Ini membandingkan jenis parameter, bukan tipe argumen, dan tidak melakukan autobox. Objek dan boolean adalah jenis yang tidak terkait, sehingga dianggap sama spesifiknya. Tidak ada metode yang lebih spesifik dari yang lain, jadi metode panggilannya ambigu.

Salah satu cara untuk menyelesaikan ambiguitas adalah dengan mengubah parameter boolean untuk mengetikkan Boolean, yang merupakan subtipe Obyek. Kelebihan pertama akan selalu lebih spesifik (bila berlaku) daripada yang kedua.


1
2018-01-31 21:42