From Java Efektif oleh Joshua Bloch,
- Array berbeda dari tipe generik dalam dua cara penting. Array pertama adalah kovarian. Obat generik tidak berubah-ubah.
Kovarian berarti jika X adalah subtipe Y maka X [] juga akan menjadi sub tipe Y []. Array adalah kovarian Karena string adalah subtipe dari Objek Jadi
String[] is subtype of Object[]
Invarian berarti tidak peduli X menjadi subtipe Y atau tidak,
List<X> will not be subType of List<Y>.
Pertanyaan saya adalah mengapa keputusan untuk membuat array kovarian di Jawa? Ada posting SO lainnya seperti Mengapa Array invarian, tetapi Daftar kovarian? , tetapi mereka tampaknya berfokus pada Scala dan saya tidak dapat mengikuti.
java
arrays
generics
language-design
covariance
semangat belajar
sumber
sumber
LinkedList
.Jawaban:
Melalui wikipedia :
Ini menjawab pertanyaan "Mengapa array kovarian?", Atau lebih tepatnya, "Mengapa tidak array dibuat kovarian pada waktu itu ?"
Ketika obat generik diperkenalkan, mereka sengaja tidak dibuat kovarian karena alasan yang ditunjukkan dalam jawaban ini oleh Jon Skeet :
Motivasi asli untuk membuat array kovarian yang dijelaskan dalam artikel wikipedia tidak berlaku untuk obat generik karena wildcard memungkinkan ekspresi kovarians (dan contravariance) menjadi mungkin, misalnya:
sumber
Object[] num = new Number[4]; num[1]= 5; num[2] = 5.0f; num[3]=43.4; System.out.println(Arrays.toString(num)); num[0]="hello";
ArrayStoreException
sesuai kebutuhan. Jelas ini dianggap kompromi yang layak pada saat itu. Bandingkan dengan hari ini: banyak yang menganggap kovarian array sebagai kesalahan, jika dipikir-pikir.Alasannya adalah bahwa setiap array tahu tipe elemennya selama runtime, sedangkan koleksi generik tidak karena penghapusan tipe.
Sebagai contoh:
Jika ini diizinkan dengan koleksi umum:
Tetapi ini akan menimbulkan masalah nanti ketika seseorang mencoba mengakses daftar:
sumber
Mungkin bantuan ini : -
Generik bukan kovarian
Array dalam bahasa Java adalah kovarian - yang berarti bahwa jika Integer memperluas Number (yang memang), maka tidak hanya Integer juga Number, tetapi Integer [] juga a
Number[]
, dan Anda bebas untuk lulus atau menetapkanInteger[]
di mana aNumber[]
dipanggil. (Lebih formal, jika Number adalah supertype dari Integer, makaNumber[]
adalah supertype dariInteger[]
.) Anda mungkin berpikir hal yang sama berlaku untuk tipe generik juga - ituList<Number>
adalah supertype dariList<Integer>
, dan bahwa Anda dapat melewati diList<Integer>
mana aList<Number>
diharapkan. Sayangnya, tidak berfungsi seperti itu.Ternyata ada alasan bagus mengapa hal itu tidak bekerja seperti itu: Ini akan memecah tipe keselamatan yang seharusnya disediakan oleh generik. Bayangkan Anda bisa menetapkan a
List<Integer>
keList<Number>
. Kemudian kode berikut akan memungkinkan Anda untuk memasukkan sesuatu yang bukan bilangan bulat ke dalamList<Integer>
:Karena ln adalah a
List<Number>
, menambahkan Float ke tampak legal. Tetapi jika dalam aliased denganli
, maka itu akan melanggar janji keselamatan-jenis yang tersirat dalam definisi li - bahwa itu adalah daftar bilangan bulat, itulah sebabnya tipe generik tidak bisa kovarian.sumber
ArrayStoreException
runtime.WHY
apakah Array dibuat kovarian. seperti yang disebutkan Sotirios, dengan Array kita akan mendapatkan ArrayStoreException saat runtime, jika Array dibuat invarian, maka kita dapat mendeteksi kesalahan ini pada waktu kompilasi itu sendiri, benar?Animal
, yang tidak harus menerima barang yang diterima dari tempat lain" dari "Array yang tidak boleh mengandung apa pun kecualiAnimal
, dan harus bersedia menerima referensi yang disediakan secara eksternal untukAnimal
. Kode yang membutuhkan yang pertama harus menerima arrayCat
, tetapi kode yang membutuhkan yang terakhir tidak boleh. Jika kompiler dapat membedakan kedua jenis, itu dapat menyediakan waktu kompilasi memeriksa. Sayangnya, satu-satunya hal yang membedakan mereka ...Array adalah kovarian untuk setidaknya dua alasan:
Berguna untuk koleksi yang menyimpan informasi yang tidak akan pernah berubah menjadi kovarian. Agar koleksi T menjadi kovarian, toko pendukungnya juga harus kovarian. Sementara orang dapat merancang
T
koleksi abadi yang tidak menggunakanT[]
sebagai backing store (mis. Menggunakan pohon atau daftar tertaut), koleksi seperti itu tidak akan berkinerja sebaik koleksi yang didukung oleh array. Orang mungkin berpendapat bahwa cara yang lebih baik untuk menyediakan koleksi yang tidak dapat diubah kovarian adalah dengan mendefinisikan tipe "array tidak berubah yang kovarian" yang bisa mereka gunakan di backing store, tetapi dengan membiarkan array kovarians mungkin lebih mudah.Array akan sering dimutasi oleh kode yang tidak tahu apa yang akan ada di dalamnya, tetapi tidak akan memasukkan ke dalam array apa pun yang tidak dibaca dari array yang sama. Contoh utama dari ini adalah kode penyortiran. Secara konseptual, dimungkinkan untuk tipe array untuk memasukkan metode untuk bertukar atau mengubah elemen (metode seperti itu bisa sama-sama berlaku untuk semua tipe array), atau mendefinisikan objek "manipulator array" yang menyimpan referensi ke array dan satu atau lebih hal yang telah dibaca dari itu, dan dapat mencakup metode untuk menyimpan item yang sebelumnya dibaca ke dalam array dari mana mereka datang. Jika array bukan kovarian, kode pengguna tidak akan dapat mendefinisikan tipe seperti itu, tetapi runtime bisa menyertakan beberapa metode khusus.
Fakta bahwa array adalah kovarian dapat dipandang sebagai peretasan yang buruk, tetapi dalam kebanyakan kasus itu memfasilitasi pembuatan kode kerja.
sumber
The fact that arrays are covariant may be viewed as an ugly hack, but in most cases it facilitates the creation of working code.
- poin bagusFitur penting dari tipe parametrik adalah kemampuan untuk menulis algoritma polimorfik, yaitu algoritma yang beroperasi pada struktur data terlepas dari nilai parameternya, seperti
Arrays.sort()
.Dengan obat generik, itu dilakukan dengan tipe wildcard:
Agar benar-benar bermanfaat, tipe wildcard memerlukan penangkapan wildcard, dan itu memerlukan gagasan tentang parameter tipe. Tidak ada yang tersedia pada saat array ditambahkan ke Jawa, dan membuat array tipe referensi kovarian memungkinkan cara yang jauh lebih sederhana untuk mengizinkan algoritma polimorfik:
Namun, kesederhanaan itu membuka celah pada sistem tipe statis:
membutuhkan pemeriksaan runtime dari setiap akses tulis ke berbagai jenis referensi.
Singkatnya, pendekatan yang lebih baru yang diwujudkan oleh obat generik membuat sistem tipe lebih kompleks, tetapi juga tipe yang lebih aman, sedangkan pendekatan yang lebih lama lebih sederhana, dan tipe yang kurang aman secara statis. Para perancang bahasa memilih untuk pendekatan yang lebih sederhana, memiliki hal-hal yang lebih penting untuk dilakukan daripada menutup celah kecil dalam sistem tipe yang jarang menyebabkan masalah. Kemudian, ketika Java didirikan, dan kebutuhan mendesak diatasi, mereka memiliki sumber daya untuk melakukannya dengan benar untuk generik (tetapi mengubahnya untuk array akan merusak program Java yang ada).
sumber
Generik tidak berubah : dari JSL 4.10 :
dan beberapa baris lebih jauh, JLS juga menjelaskan bahwa
Array adalah kovarian (peluru pertama):
4.10.3 Subtipe di antara Jenis Array
sumber
Saya pikir mereka membuat keputusan yang salah pada awalnya yang membuat array kovarian. Itu merusak keamanan jenis seperti yang dijelaskan di sini dan mereka terjebak dengan itu karena kompatibilitas ke belakang dan setelah itu mereka mencoba untuk tidak membuat kesalahan yang sama untuk generik. Dan itulah salah satu alasan mengapa Joshua Bloch lebih memilih daftar daripada di Item 25 buku "Java Efektif (edisi kedua)"
sumber
Saya ambil: Ketika kode mengharapkan array A [] dan Anda memberinya B [] di mana B adalah subkelas A, hanya ada dua hal yang perlu dikhawatirkan: apa yang terjadi ketika Anda membaca elemen array, dan apa yang terjadi jika Anda menulis Itu. Jadi tidak sulit untuk menulis aturan bahasa untuk memastikan bahwa keamanan tipe dipertahankan dalam semua kasus (aturan utama adalah bahwa pelemparan huruf
ArrayStoreException
bisa dilemparkan jika Anda mencoba memasukkan A ke dalam B []). Namun, untuk generik, ketika Anda mendeklarasikan sebuah kelasSomeClass<T>
, mungkin ada sejumlah caraT
yang digunakan dalam tubuh kelas, dan saya menduga itu terlalu rumit untuk mengerjakan semua kombinasi yang mungkin untuk menulis aturan tentang kapan hal-hal diperbolehkan dan ketika tidak.sumber