Java 101: Bahasa Java yang penting menampilkan lawatan, Bahagian 5
Taip inferensi dan pembina generik untuk kelas generik dan bukan generik
Kelas generik dan bukan generik boleh menyatakan pembina generik di mana konstruktor mempunyai senarai parameter jenis formal. Sebagai contoh, anda boleh menyatakan kelas generik berikut dengan pembina generik:
public class Box { public Box(T t) { // ... } }
Deklarasi ini menentukan kelas generik Box
dengan parameter jenis formal E
. Ia juga menentukan konstruktor generik dengan parameter jenis formal T
. Anda boleh memberi contoh kelas generik dan menggunakan pembentuknya seperti berikut:
new Box("Aggies")
Ungkapan ini mencipta contoh Box
, berlalu Marble
ke E
. Juga, penyusun menyimpulkan String
sebagai T
argumen jenis sebenar kerana argumen pembina adalah String
objek.
Penyusun Pra-Java 7 menyimpulkan argumen jenis sebenar pembina generik sama dengan kaedah generik. Walau bagaimanapun, penyusun Java 7 dapat menyimpulkan jenis argumen sebenar kelas generik yang dibuat dalam konteks operator berlian. Pertimbangkan contoh berikut:
Box box = new Box("Aggies");
Serta menyimpulkan jenis Marble
untuk parameter jenis formal E
kelas generik Box
, penyusun menyimpulkan jenis String
untuk parameter jenis formal T
pembina kelas generik ini.
Perubahan kecil Project Coin # 8: Permintaan kaedah varargs yang dipermudahkan
Sebelum Java 7, setiap usaha untuk menggunakan kaedah varargs (variabel argumen, juga dikenali sebagai variabel arity ) dengan jenis varargs yang tidak dapat diubah semula menyebabkan penyusun mengeluarkan amaran "operasi tidak selamat". Untuk menghilangkan potensi banyak pesan amaran serupa (satu per laman panggilan), Java 7 memindahkan amaran dari laman panggilan ke deklarasi metode.
Jenis yang boleh ditukar dan tidak boleh disahkan semula
A Jenis reifiable mendedahkan jenis maklumat yang lengkap pada masa jalanan. Contohnya termasuk jenis primitif, jenis bukan generik, jenis mentah, dan panggilan kad liar yang tidak terikat. Sebaliknya, jenis yang tidak dapat disahkan semula mempunyai maklumat jenis yang dikeluarkan pada waktu kompilasi dengan penghapusan jenis, untuk memastikan keserasian binari dengan perpustakaan Java dan aplikasi yang dibuat sebelum generik. Contohnya merangkumi Set
dan Set
. Kerana jenis yang tidak dapat disahkan tidak tersedia sepenuhnya pada waktu runtime, JVM tidak dapat membezakan antara Set
dan Set
; pada waktu runtime, hanya jenis mentah Set
yang ada.
Kaedah generik yang merangkumi parameter input vararg dapat menyebabkan pencemaran timbunan , di mana pemboleh ubah dari jenis parameter merujuk pada objek yang bukan dari jenis parameter tersebut (misalnya jika jenis mentah telah dicampur dengan jenis parameter). Pengkompilasi melaporkan "amaran yang tidak diperiksa" kerana kebenaran operasi yang melibatkan jenis parameter (seperti panggilan atau kaedah panggilan) tidak dapat disahkan.
Penyenaraian 13 menunjukkan pencemaran timbunan dalam konteks bukan-varargs.
Penyenaraian 13. Menunjukkan pencemaran timbunan dalam konteks bukan-varargs
import java.util.Iterator; import java.util.Set; import java.util.TreeSet; public class HeapPollutionDemo { public static void main(String[] args) { Set s = new TreeSet(); Set ss = s; // unchecked warning s.add(new Integer(42)); // another unchecked warning Iterator iter = ss.iterator(); while (iter.hasNext()) { String str = iter.next(); // ClassCastException thrown System.out.println(str); } } }
Pemboleh ubah ss
mempunyai jenis parameter Set
. Apabila java.util.Set
yang dirujuk s
ditugaskan ss
, penyusun menghasilkan amaran yang tidak dicentang. Ia melakukannya kerana penyusun tidak dapat menentukan yang s
merujuk kepada Set
jenis (tidak). Hasilnya ialah pencemaran timbunan. (Pengompil membenarkan tugas ini untuk mengekalkan keserasian ke belakang dengan versi Java lama yang tidak menyokong generik. Tambahan pula, penghapusan jenis berubah Set
menjadi Set
, yang mengakibatkan seseorang Set
ditugaskan kepada yang lain Set
.)
Pengkompil menjana amaran dibiarkan kedua pada baris yang menyembah sesuatu Set
's add()
kaedah. Ia melakukannya kerana tidak dapat menentukan sama ada pemboleh ubah s
merujuk kepada Set
atau Set
jenis. Ini adalah satu lagi keadaan pencemaran timbunan. (Pengkompil ini membolehkan kaedah panggilan ini kerana jelmaan pemadaman Set
's boolean add(E e)
kaedah untuk boolean add(Object o)
, yang boleh menambah apa-apa jenis objek yang ditetapkan, termasuk java.lang.Integer
subjenis daripada java.lang.Object
.)
Pencemaran timbunan boleh berlaku dengan mudah dalam konteks varargs. Sebagai contoh, pertimbangkan Penyenaraian 14.
Penyenaraian 14. Menunjukkan pencemaran timbunan dalam konteks varargs
import java.util.Arrays; import java.util.List; public class UnsafeVarargsDemo { public static void main(String[] args) { unsafe(Arrays.asList("A", "B", "C"), Arrays.asList("D", "E", "F")); } static void unsafe(List... l) { Object[] oArray = l; oArray[0] = Arrays.asList(new Double(3.5)); String s = l[0].get(0); } }
The Object[] oArray = l;
tugasan memperkenalkan kemungkinan pencemaran timbunan. Nilai yang tidak sepadan dengan jenis parameter parameter varargs l
dapat diberikan kepada pemboleh ubah oArray
. Walau bagaimanapun, penyusun tidak menghasilkan amaran yang tidak dicentang kerana telah melakukannya semasa menerjemahkan List... l
ke List[] l
. Tugasan ini sah kerana pemboleh ubah l
mempunyai jenis List[]
, yang subtipe Object[]
.
Juga, pengkompil tidak mengeluarkan amaran atau kesalahan semasa memberikan List
objek jenis apa pun kepada oArray
komponen array; sebagai contoh , oArray[0] = Arrays.asList(new Double(3.5));
. Tugasan ini menyerah hak kepada komponen lokasi pertama oArray
yang List
objek yang mengandungi satu java.lang.Double
objek.
The String s = l[0].get(0);
tugasan adalah bermasalah. Objek yang disimpan dalam komponen array pertama pemboleh ubah l
mempunyai jenis List
, tetapi tugasan ini mengharapkan objek jenis List
. Akibatnya, JVM melemparkan java.lang.ClassCastException
.
Kumpulkan kod sumber ini ( javac -Xlint:unchecked UnsafeVarargsDemo.java
). Anda harus melihat output berikut (sedikit diformat ulang untuk dibaca) ketika disusun di bawah Java SE 7 kemas kini 6:
UnsafeVarargsDemo.java:8: warning: [unchecked] unchecked generic array creation for varargs parameter of type List[] unsafe(Arrays.asList("A", "B", "C"), ^ UnsafeVarargsDemo.java:12: warning: [unchecked] Possible heap pollution from parameterized vararg type List static void unsafe(List... l) ^ 2 warnings
Dalam pengenalan Java 101 saya untuk generik, saya menyatakan bahawa anda tidak boleh menggunakan parameter jenis dalam ekspresi penciptaan array. Contohnya, anda tidak dapat menentukan elements = new E[size];
. Pengkompilasi melaporkan mesej "ralat penciptaan array generik" apabila anda cuba melakukannya. Walau bagaimanapun, masih mungkin untuk membuat susunan generik, tetapi hanya dalam konteks varargs, dan itulah yang dilaporkan oleh mesej amaran pertama. Di sebalik tabir, penyusun berubah List... l
menjadi List[] l
dan kemudian menjadi List[] l
.
Perhatikan bahawa amaran pencemaran timbunan dihasilkan di unsafe()
laman pengisytiharan kaedah. Mesej ini tidak dihasilkan di laman panggilan kaedah ini, seperti halnya penyusun Java 5 dan 6.
Tidak semua kaedah varargs akan menyumbang kepada pencemaran timbunan. Walau bagaimanapun, mesej amaran masih akan dikeluarkan di laman web deklarasi kaedah. Sekiranya anda tahu bahawa kaedah anda tidak menyumbang kepada pencemaran timbunan, anda boleh menekan amaran ini dengan menyatakannya dengan @SafeVarargs
anotasi - Java 7 memperkenalkan java.lang.SafeVarargs
jenis anotasi. Sebagai contoh, kerana tidak ada kaedah kaedah Arrays
kelas asList()
untuk menyumbang kepada pencemaran timbunan, deklarasi kaedah ini telah diberi penjelasan dengan @SafeVarargs
, seperti berikut:
@SafeVarargs public static List asList(T... a)
The @SafeVarargs
anotasi menghapuskan penciptaan array generik dan mesej amaran pencemaran timbunan. Ini adalah bahagian yang didokumentasikan dari kontrak kaedah dan menegaskan bahawa pelaksanaan kaedah tersebut tidak akan menangani parameter formal varargs secara tidak betul.
Kesimpulannya
Java 7 meningkatkan produktiviti pembangun dengan memperkenalkan pengurusan sumber automatik melalui pernyataan cuba-dengan-sumber bersama dengan AutoCloseable
antaramuka baru , beralih-rentetan, multi-tangkapan, rethrow akhir, literal binari, garis bawah dalam literal numerik, perubahan pada jenis kompiler algoritma inferensi yang memperkenalkan pengendali berlian yang disebut, dan pemanggilan kaedah varargs yang dipermudahkan. Seterusnya di Java 101: Siri generasi seterusnya adalah melihat ciri-ciri bahasa antaramuka lambda dan fungsi Java 8's.
Kisah ini, "Java 101: Bahasa Jawa yang penting menampilkan lawatan, Bahagian 5" pada awalnya diterbitkan oleh JavaWorld.