Dalam contoh ini:
import java.util.*;
public class Example {
static void doesntCompile(Map<Integer, List<? extends Number>> map) {}
static <T extends Number> void compiles(Map<Integer, List<T>> map) {}
static void function(List<? extends Number> outer)
{
doesntCompile(new HashMap<Integer, List<Integer>>());
compiles(new HashMap<Integer, List<Integer>>());
}
}
doesntCompile()
gagal dikompilasi dengan:
Example.java:9: error: incompatible types: HashMap<Integer,List<Integer>> cannot be converted to Map<Integer,List<? extends Number>>
doesntCompile(new HashMap<Integer, List<Integer>>());
^
sementara compiles()
diterima oleh kompiler.
Jawaban ini menjelaskan bahwa satu-satunya perbedaan adalah tidak seperti itu <? ...>
, <T ...>
memungkinkan Anda merujuk jenisnya nanti, yang tampaknya tidak menjadi masalah.
Apa perbedaan antara <? extends Number>
dan <T extends Number>
dalam kasus ini dan mengapa tidak dikompilasi pertama?
Jawaban:
Dengan mendefinisikan metode dengan tanda tangan berikut:
dan memohon seperti:
Dalam jls §8.1.2 kita temukan, itu (bagian yang menarik dicetak tebal oleh saya):
Dengan kata lain, tipe
T
ini cocok dengan tipe input dan ditetapkanInteger
. Tanda tangan akan efektif menjadistatic void compiles(Map<Integer, List<Integer>> map)
.Ketika sampai pada
doesntCompile
metode, jls mendefinisikan aturan subtyping ( §4.5.1 , dicetak tebal oleh saya):Ini berarti, yang
? extends Number
memang mengandungInteger
atau bahkanList<? extends Number>
mengandungList<Integer>
, tetapi tidak demikian halnya denganMap<Integer, List<? extends Number>>
danMap<Integer, List<Integer>>
. Lebih lanjut tentang topik itu dapat ditemukan di utas SO ini . Anda masih bisa membuat versi dengan?
wildcard berfungsi dengan mendeklarasikan, bahwa Anda mengharapkan subtipe dariList<? extends Number>
:sumber
? extends Number
bukan? extends Numeric
. [2] Pernyataan Anda bahwa "bukan itu yang terjadi pada Daftar <? Extends Number> dan List <Integer>" tidak benar. Seperti yang telah ditunjukkan oleh @VinceEmigh, Anda dapat membuat metodestatic void demo(List<? extends Number> lst) { }
dan menyebutnya seperti inidemo(new ArrayList<Integer>());
atau inidemo(new ArrayList<Float>());
, dan kode tersebut mengkompilasi dan menjalankan OK. Atau mungkin saya salah membaca atau salah mengerti apa yang Anda katakan?List<? extends Number>
sebagai parameter tipe seluruh peta, bukan dirinya sendiri. Terima kasih banyak atas komentarnya.List<Number>
tidak mengandungList<Integer>
. Misalkan Anda memiliki fungsistatic void check(List<Number> numbers) {}
. Ketika memohoncheck(new ArrayList<Integer>());
padanya tidak dikompilasi, Anda harus mendefinisikan metode sebagaistatic void check(List<? extends Number> numbers) {}
. Dengan peta itu sama tetapi dengan lebih banyak bersarang.Number
jenis parameter daftar dan Anda perlu menambahkan? extends
untuk membuatnya kovarian,List<? extends Number>
adalah tipe parameterMap
dan juga perlu? extends
kovarians.Dalam panggilan:
T cocok dengan Integer, jadi jenis argumennya adalah a
Map<Integer,List<Integer>>
. Ini bukan kasus untuk metodedoesntCompile
: jenis argumen tetapMap<Integer, List<? extends Number>>
apa pun argumen aktual dalam panggilan; dan itu tidak dapat ditugaskan dariHashMap<Integer, List<Integer>>
.MEMPERBARUI
Dalam
doesntCompile
metode ini, tidak ada yang mencegah Anda melakukan hal seperti ini:Jadi jelas, itu tidak dapat menerima
HashMap<Integer, List<Integer>>
argumen.sumber
doesntCompile
itu? Hanya ingin tahu tentang itu.doesntCompile(new HashMap<Integer, List<? extends Number>>());
akan berfungsi, seperti biasadoesntCompile(new HashMap<>());
.HashMap<Integer, List<Integer>>
" bisakah Anda menjelaskan mengapa itu tidak dapat dialihkan dari itu?Contoh demonstrasi yang disederhanakan. Contoh yang sama dapat divisualisasikan seperti di bawah ini.
List<Pair<? extends Number>>
adalah tipe wildcard multi-level sedangkanList<? extends Number>
tipe wildcard standar.Instansiasi konkret yang valid dari tipe wild card
List<? extends Number>
termasukNumber
dan setiap subtipe dariNumber
sedangkan dalam kasusList<Pair<? extends Number>>
yang merupakan argumen tipe dari argumen tipe dan itu sendiri memiliki instantiasi konkret dari tipe generik.Generik tidak tetap sehingga
Pair<? extends Number>
tipe wild card hanya dapat menerimaPair<? extends Number>>
. Tipe dalam? extends Number
sudah kovarian. Anda harus menjadikan tipe penutup sebagai kovarian untuk memungkinkan kovarian.sumber
<Pair<Integer>>
tidak bekerja dengan<Pair<? extends Number>>
tetapi bekerja dengan<T extends Number> <Pair<T>>
?T
vs?
. Sebagian dari masalahnya adalah ketika Andronicus mencapai titik vital dari penjelasannya, ia menolak utas lain yang hanya menggunakan contoh-contoh sepele. Saya berharap mendapat jawaban yang lebih jelas dan lengkap di sini.Saya akan merekomendasikan Anda untuk melihat dokumentasi wildcard generik terutama panduan untuk penggunaan wildcard
Terus terang metode Anda #doesntCompile
dan panggilan seperti
Secara fundamental tidak benar
Mari tambahkan implementasi hukum :
Ini benar-benar baik, karena Double meluas Angka, jadi put
List<Double>
juga benar-benar baikList<Integer>
, kan?Namun, apakah Anda masih menganggap itu legal untuk lulus
new HashMap<Integer, List<Integer>>()
dari contoh Anda di sini ?Compiler tidak berpikir begitu, dan melakukan yang terbaik untuk menghindari situasi seperti itu.
Cobalah untuk melakukan implementasi yang sama dengan metode #compile dan kompiler jelas tidak akan memungkinkan Anda untuk memasukkan daftar ganda ke dalam peta.
Pada dasarnya Anda dapat menempatkan apa-apa kecuali
List<T>
bahwa ini mengapa itu aman untuk memanggil metode yang dengannew HashMap<Integer, List<Integer>>()
ataunew HashMap<Integer, List<Double>>()
ataunew HashMap<Integer, List<Long>>()
ataunew HashMap<Integer, List<Number>>()
.Jadi singkatnya, Anda mencoba menipu dengan kompiler dan itu cukup bertahan melawan kecurangan tersebut.
NB: jawaban yang diposting oleh Maurice Perry benar sekali. Saya hanya tidak yakin itu cukup jelas, jadi saya mencoba (sangat berharap saya berhasil) untuk menambahkan posting yang lebih luas.
sumber