Ini adalah contoh dunia nyata dari API pustaka pihak ketiga, tetapi disederhanakan.
Dikompilasi dengan Oracle JDK 8u72
Pertimbangkan dua metode ini:
<X extends CharSequence> X getCharSequence() {
return (X) "hello";
}
<X extends String> X getString() {
return (X) "hello";
}
Keduanya melaporkan peringatan "pemeran yang tidak dicentang" - Saya mengerti alasannya. Hal yang membingungkan saya adalah mengapa saya bisa menelepon
Integer x = getCharSequence();
dan mengkompilasi? Kompiler harus tahu bahwa Integer
tidak diimplementasikan CharSequence
. Panggilan ke
Integer y = getString();
memberikan kesalahan (seperti yang diharapkan)
incompatible types: inference variable X has incompatible upper bounds java.lang.Integer,java.lang.String
Dapatkah seseorang menjelaskan mengapa perilaku ini dianggap sah? Apa manfaatnya?
Klien tidak tahu bahwa panggilan ini tidak aman - kode klien dikompilasi tanpa peringatan. Mengapa kompilasi tidak akan memperingatkan tentang hal itu / mengeluarkan kesalahan?
Juga, apa bedanya dengan contoh ini:
<X extends CharSequence> void doCharSequence(List<X> l) {
}
List<CharSequence> chsL = new ArrayList<>();
doCharSequence(chsL); // compiles
List<Integer> intL = new ArrayList<>();
doCharSequence(intL); // error
Mencoba List<Integer>
memberikan kesalahan, seperti yang diharapkan:
method doCharSequence in class generic.GenericTest cannot be applied to given types; required: java.util.List<X> found: java.util.List<java.lang.Integer> reason: inference variable X has incompatible bounds equality constraints: java.lang.Integer upper bounds: java.lang.CharSequence
Jika itu dilaporkan sebagai kesalahan, mengapa Integer x = getCharSequence();
tidak?
Integer x = getCharSequence();
akan dikompilasi, tetapi casting pada RHSInteger x = (Integer) getCharSequence();
gagal dikompilasiJawaban:
CharSequence
adalah sebuahinterface
. Oleh karena itu bahkan jikaSomeClass
tidak mengimplementasikannyaCharSequence
akan sangat mungkin untuk membuat kelasKarena itu kamu bisa menulis
karena tipe yang disimpulkan
X
adalah tipe persimpanganSomeClass & CharSequence
.Ini agak aneh dalam kasus
Integer
karenaInteger
final, tetapifinal
tidak memainkan peran apa pun dalam aturan ini. Misalnya Anda bisa menulisDi sisi lain,
String
ini bukaninterface
, jadi tidak mungkin untuk memperluasSomeClass
untuk mendapatkan subtipeString
, karena java tidak mendukung multiple-inheritance untuk kelas.Dengan
List
contoh tersebut, Anda perlu mengingat bahwa obat generik tidak bersifat kovarian atau contravarian. Ini berarti bahwa jikaX
merupakan subtipe dariY
,List<X>
bukan merupakan subtipe atau supertipe dariList<Y>
. KarenaInteger
tidak diterapkanCharSequence
, Anda tidak dapat menggunakan metodeList<Integer>
AndadoCharSequence
.Namun, Anda bisa membuatnya untuk dikompilasi
Jika Anda memiliki metode yang mengembalikan sebuah
List<T>
seperti ini:Anda dapat melakukan
Sekali lagi, ini karena tipe yang disimpulkan adalah
Integer & CharSequence
dan ini adalah subtipe dariInteger
.Jenis titik-temu muncul secara implisit ketika Anda menentukan banyak batas (misalnya
<T extends SomeClass & CharSequence>
).Untuk informasi lebih lanjut, di sini adalah bagian dari JLS di mana ia menjelaskan bagaimana jenis batas pekerjaan. Anda dapat menyertakan banyak antarmuka, mis
tetapi hanya batas pertama yang bisa berupa antarmuka.
sumber
&
definisi generik. +1<T extends String & List & Comparator>
ok tapi<T extends String & Integer>
tidak, karenaInteger
bukan antarmuka.Collections.emptyList()
jugaOptional.empty()
. Ini mengembalikan implementasi antarmuka generik, tetapi tidak menyimpan apa pun.final
pada waktu kompilasi akan beradafinal
pada saat runtime.getCharSequence()
berjanji untuk mengembalikan apa punX
yang dibutuhkan penelepon, termasuk mengembalikan jenis yang diperluasInteger
dan diterapkanCharSequence
jika penelepon membutuhkannya dan berdasarkan janji ini, sudah benar untuk memungkinkan menetapkan hasilInteger
. Ini adalah metodegetCharSequence()
yang rusak karena tidak memenuhi janjinya, tapi itu bukan kesalahan kompiler.Jenis yang disimpulkan oleh kompiler Anda sebelum penugasan
X
adalahInteger & CharSequence
. Tipe ini terasa aneh, karenaInteger
final, tetapi ini adalah tipe yang benar-benar valid di Jawa. Ini kemudian dilemparkan keInteger
, yang sangat OK.Ada tepat satu nilai yang mungkin untuk
Integer & CharSequence
jenis:null
. Dengan implementasi sebagai berikut:Tugas berikut akan berhasil:
Karena nilai yang mungkin ini, tidak ada alasan mengapa penugasan itu harus salah, bahkan jika itu jelas tidak berguna. Peringatan akan bermanfaat.
Masalah sebenarnya adalah API, bukan situs panggilan
Bahkan, saya baru-baru ini membuat blog tentang desain anti pola API ini . Anda seharusnya (hampir) tidak pernah mendesain metode generik untuk mengembalikan tipe sewenang-wenang karena Anda dapat (hampir) tidak pernah menjamin bahwa tipe yang disimpulkan akan dikirimkan. Pengecualian adalah metode seperti
Collections.emptyList()
, dalam hal kekosongan daftar (dan penghapusan tipe umum) adalah alasan mengapa setiap kesimpulan untuk<T>
bekerja:sumber