Java 101: Tur fitur bahasa Java yang penting, Bagian 5

Sebelumnya 1 2 Halaman 2 Halaman 2 dari 2

Ketik inferensi dan konstruktor generik untuk kelas generik dan non-generik

Kelas generik dan non-generik dapat mendeklarasikan konstruktor generik di mana konstruktor memiliki daftar parameter tipe formal. Misalnya, Anda dapat mendeklarasikan kelas generik berikut dengan konstruktor generik:

 public class Box { public  Box(T t) { // ... } } 

Deklarasi ini menentukan kelas generik Boxdengan parameter tipe formal E. Ini juga menentukan konstruktor generik dengan parameter tipe formal T. Anda dapat membuat instance kelas generik dan memanggil konstruktornya sebagai berikut:

 new Box("Aggies") 

Ekspresi ini membuat instance Box, meneruskan Marbleke E. Juga, kompilator menyimpulkan Stringsebagai Ttipe argumen sebenarnya karena argumen konstruktor adalah sebuah Stringobjek.

Compiler Pre-Java 7 menyimpulkan argumen tipe aktual konstruktor generik mirip dengan metode generik. Namun, compiler Java 7 dapat menyimpulkan argumen tipe sebenarnya dari kelas generik yang dibuat dalam konteks operator berlian. Perhatikan contoh berikut:

 Box box = new Box("Aggies"); 

Selain menyimpulkan tipe Marbleuntuk parameter tipe formal Edari kelas generik Box, kompilator menyimpulkan tipe Stringuntuk parameter tipe formal Tdari konstruktor kelas generik ini.

Perubahan kecil Project Coin # 8: Pemanggilan metode vararg yang disederhanakan

Sebelum Java 7, setiap upaya untuk menjalankan metode varargs (argumen variabel, juga dikenal sebagai variabel arity ) dengan jenis varargs yang tidak dapat diubah menyebabkan compiler mengeluarkan peringatan "operasi tidak aman". Untuk menghilangkan potensi banyak pesan peringatan serupa (satu per situs panggilan), Java 7 memindahkan peringatan dari situs panggilan ke deklarasi metode.

Jenis yang dapat diubah dan tidak dapat diubah

Sebuah jenis reifiable mengekspos informasi jenis lengkap pada saat runtime. Contohnya termasuk tipe primitif, tipe non-generik, tipe mentah, dan pemanggilan wildcard tak terikat. Sebaliknya, tipe non-reifiable memiliki informasi tipe dihapus pada waktu kompilasi berdasarkan penghapusan tipe, untuk memastikan kompatibilitas biner dengan pustaka Java dan aplikasi yang dibuat sebelum generik. Contohnya termasuk Setdan Set. Karena jenis yang tidak dapat diubah tidak sepenuhnya tersedia pada waktu proses, JVM tidak dapat membedakan antara Setdan Set; pada waktu proses, hanya jenis mentah Setyang tersedia.

Metode umum yang menyertakan parameter input vararg dapat menyebabkan polusi heap , di mana variabel dari tipe berparameter merujuk ke objek yang bukan dari tipe berparameter tersebut (misalnya jika tipe mentah telah dicampur dengan tipe berparameter). Kompilator melaporkan "peringatan yang tidak dicentang" karena kebenaran operasi yang melibatkan tipe berparameter (seperti panggilan cast atau metode) tidak dapat diverifikasi.

Daftar 13 menunjukkan polusi tumpukan dalam konteks non-vararg.

Daftar 13. Mendemonstrasikan pencemaran tumpukan dalam konteks non-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); } } } 

Variabel ssmemiliki tipe berparameter Set. Ketika java.util.Setyang direferensikan oleh sditetapkan ke ss, kompilator menghasilkan peringatan yang tidak dicentang. Ia melakukannya karena kompilator tidak dapat menentukan yang smerujuk ke suatu Settipe (tidak). Hasilnya adalah polusi tumpukan. (Kompilator memungkinkan penetapan ini untuk mempertahankan kompatibilitas mundur dengan versi Java lama yang tidak mendukung generik. Selain itu, penghapusan tipe berubah Setmenjadi Set, yang menghasilkan satu Setyang ditugaskan ke yang lain Set.)

Compiler menghasilkan peringatan dicentang kedua pada baris yang memanggil Set's add()metode. Itu dilakukan karena tidak dapat menentukan apakah variabel smerujuk ke Setatau Settipe. Ini adalah situasi polusi tumpukan lainnya. (Compiler memungkinkan pemanggilan metode ini karena penghapusan transformasi Set's boolean add(E e)metode untuk boolean add(Object o), yang dapat menambahkan jenis objek untuk mengatur, termasuk java.lang.Integersubtipe java.lang.Object.)

Polusi tumpukan dapat dengan mudah terjadi dalam konteks varargs. Misalnya, pertimbangkan Listing 14.

Daftar 14. Mendemonstrasikan pencemaran tumpukan 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;tugas memperkenalkan kemungkinan pencemaran tumpukan. Nilai yang tidak cocok dengan jenis parameter dari parameter varargs ldapat ditetapkan ke variabel oArray. Namun, kompilator tidak menghasilkan peringatan yang tidak dicentang karena telah melakukannya saat menerjemahkan List... lke List[] l. Penugasan ini valid karena variabel lmemiliki tipe List[], yang merupakan subtipe Object[].

Juga, kompilator tidak mengeluarkan peringatan atau kesalahan saat menugaskan Listobjek jenis apapun ke salah satu oArraykomponen array; misalnya oArray[0] = Arrays.asList(new Double(3.5));,. Penugasan ini diberikan ke komponen array pertama dari oArraysebuah Listobjek yang berisi satu java.lang.Doubleobjek.

The String s = l[0].get(0);tugas bermasalah. Objek yang disimpan dalam komponen array pertama dari variabel lmemiliki tipe List, tetapi tugas ini mengharapkan objek bertipe List. Akibatnya, JVM melempar java.lang.ClassCastException.

Kompilasi kode sumber ini ( javac -Xlint:unchecked UnsafeVarargsDemo.java). Anda harus mengamati keluaran berikut (sedikit diformat ulang agar terbaca) ketika dikompilasi di bawah Java SE 7 update 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 pengantar Java 101 saya untuk generik, saya menyatakan bahwa Anda tidak dapat menggunakan parameter tipe dalam ekspresi pembuatan larik. Misalnya, Anda tidak dapat menentukan elements = new E[size];. Kompilator melaporkan pesan "kesalahan pembuatan larik generik" saat Anda mencoba melakukannya. Namun, masih mungkin untuk membuat larik generik, tetapi hanya dalam konteks varargs, dan itulah yang dilaporkan oleh pesan peringatan pertama. Di balik layar, kompilator berubah List... lmenjadi List[] ldan kemudian menjadi List[] l.

Perhatikan bahwa peringatan polusi heap dihasilkan di unsafe()situs deklarasi metode tersebut. Pesan ini tidak dibuat di situs panggilan metode ini, seperti halnya dengan compiler Java 5 dan 6.

Tidak semua metode varargs akan berkontribusi pada polusi tumpukan. Namun, pesan peringatan akan tetap dikeluarkan di situs deklarasi metode tersebut. Jika Anda tahu bahwa metode Anda tidak berkontribusi pada polusi heap, Anda bisa menahan peringatan ini dengan mendeklarasikannya dengan @SafeVarargsanotasi - Java 7 memperkenalkan java.lang.SafeVarargsjenis anotasi. Misalnya, karena tidak ada cara bagi metode Arrayskelas asList()untuk berkontribusi pada polusi heap, deklarasi metode ini telah diberi anotasi @SafeVarargs, sebagai berikut:

 @SafeVarargs public static  List asList(T... a) 

The @SafeVarargspenjelasan menghilangkan penciptaan array yang generik dan pesan polusi peringatan tumpukan. Ini adalah bagian dari kontrak metode yang terdokumentasi dan menegaskan bahwa penerapan metode tidak akan menangani parameter formal varargs dengan tidak semestinya.

Kesimpulannya

Java 7 meningkatkan produktivitas pengembang dengan memperkenalkan manajemen sumber daya otomatis melalui pernyataan coba-dengan-sumber daya bersama dengan AutoCloseableantarmuka baru , switch-on-string, multi-catch, final rethrow, binary literal, garis bawah dalam literal numerik, perubahan pada jenis kompiler algoritma inferensi yang memperkenalkan apa yang disebut operator berlian, dan pemanggilan metode varargs yang disederhanakan. Selanjutnya di Java 101: Seri generasi berikutnya adalah melihat lambda Java 8 dan fitur bahasa antarmuka fungsional.

Cerita ini, "Java 101: Tur fitur bahasa Java yang penting, Bagian 5" pada awalnya diterbitkan oleh JavaWorld.