Perhatikan bahwa alasan mengapa tipe mentah ada adalah untuk kompatibilitas mundur dengan Java 1.4 dan lebih lama, yang tidak memiliki obat generik sama sekali.
Jesper
Jawaban:
744
Apa itu jenis mentah?
Spesifikasi Bahasa Jawa mendefinisikan jenis mentah sebagai berikut:
Jenis mentah didefinisikan sebagai salah satu dari:
Tipe referensi yang dibentuk dengan mengambil nama deklarasi tipe generik tanpa daftar argumen tipe yang menyertainya.
Tipe array yang tipe elemennya adalah tipe mentah.
staticTipe non- anggota dari tipe mentah Ryang tidak diwarisi dari superclass atau superinterface dari R.
Berikut ini contoh untuk diilustrasikan:
publicclassMyType<E>{classInner{}staticclassNested{}publicstaticvoid main(String[] args){MyType mt;// warning: MyType is a raw typeMyType.Inner inn;// warning: MyType.Inner is a raw typeMyType.Nested nest;// no warning: not parameterized typeMyType<Object> mt1;// no warning: type parameter givenMyType<?> mt2;// no warning: type parameter given (wildcard OK!)}}
Di sini, MyType<E>adalah tipe parameter ( JLS 4.5 ). Adalah umum untuk bahasa sehari-hari menyebut jenis ini hanya sebagai MyTypesingkat, tetapi secara teknis namanya MyType<E>.
mtmemiliki tipe mentah (dan menghasilkan peringatan kompilasi) pada poin pertama dalam definisi di atas; innjuga memiliki tipe mentah pada titik peluru ketiga.
MyType.Nestedbukan tipe parameter, meskipun itu tipe tipe tipe parameter MyType<E>, karena itu static.
mt1, dan mt2keduanya dideklarasikan dengan parameter tipe aktual, jadi mereka bukan tipe mentah.
Apa yang istimewa dari jenis mentah?
Pada dasarnya, tipe mentah berperilaku seperti mereka sebelum obat generik diperkenalkan. Artinya, berikut ini sepenuhnya legal pada waktu kompilasi.
List names =newArrayList();// warning: raw type!
names.add("John");
names.add("Mary");
names.add(Boolean.FALSE);// not a compilation error!
Kode di atas berjalan dengan baik, tetapi anggap Anda juga memiliki yang berikut ini:
for(Object o : names){String name =(String) o;System.out.println(name);}// throws ClassCastException!// java.lang.Boolean cannot be cast to java.lang.String
Sekarang kita mengalami masalah pada saat run-time, karena namesmengandung sesuatu yang bukan instanceof String.
Agaknya, jika Anda hanya ingin namesmengandung String, Anda mungkin masih bisa menggunakan jenis mentah dan memeriksaadd sendiri secara manual , lalu secara manual memasukkan ke Stringsetiap item dari names. Lebih baik lagi , meskipun BUKAN menggunakan tipe mentah dan biarkan kompiler melakukan semua pekerjaan untuk Anda , memanfaatkan kekuatan generik Java.
Tentu saja, jika Anda DO ingin namesuntuk memungkinkan Boolean, maka Anda dapat mendeklarasikan sebagai List<Object> names, dan kode di atas akan mengkompilasi.
Bagaimana jenis mentah berbeda dari menggunakan <Object>sebagai tipe parameter?
Berikut ini adalah kutipan dari Effective Java 2nd Edition, Item 23: Jangan gunakan tipe mentah dalam kode baru :
Apa perbedaan antara tipe mentah Listdan tipe parameter List<Object>? Secara longgar, yang pertama telah memilih pemeriksaan tipe generik, sementara yang kedua secara eksplisit mengatakan kepada kompiler bahwa ia mampu menahan objek dari jenis apa pun. Meskipun Anda bisa meneruskan List<String>ke parameter tipe List, Anda tidak bisa meneruskannya ke parameter tipe List<Object>. Ada aturan subtipe untuk obat generik, dan List<String>merupakan subtipe dari tipe mentah List, tetapi bukan tipe yang diparameterisasi List<Object>. Akibatnya, Anda kehilangan keamanan tipe jika Anda menggunakan tipe mentah seperti List, tetapi tidak jika Anda menggunakan tipe seperti parameterList<Object> .
Untuk mengilustrasikan poin, pertimbangkan metode berikut yang mengambil a List<Object>dan menambahkan a new Object().
Jika Anda telah menyatakan appendNewObjectuntuk mengambil tipe mentah Listsebagai parameter, maka ini akan dikompilasi, dan karena itu Anda akan kehilangan jenis keamanan yang Anda dapatkan dari obat generik.
Bagaimana jenis mentah berbeda dari menggunakan <?>sebagai parameter tipe?
List<Object>,, List<String>dll semuanya List<?>, jadi mungkin tergoda untuk mengatakan bahwa mereka hanya Listsebaliknya. Namun, ada perbedaan besar: karena List<E>hanya mendefinisikan add(E), Anda tidak dapat menambahkan sembarang objek sembarang ke a List<?>. Di sisi lain, karena jenis mentah Listtidak memiliki keamanan jenis, Anda dapat addapa saja untuk a List.
Pertimbangkan variasi cuplikan sebelumnya:
staticvoid appendNewObject(List<?> list){
list.add(newObject());// compilation error!}//...List<String> names =newArrayList<String>();
appendNewObject(names);// this part is fine!
Kompiler melakukan pekerjaan luar biasa untuk melindungi Anda dari kemungkinan pelanggaran jenis invarian List<?>! Jika Anda telah mendeklarasikan parameter sebagai tipe mentah List list, maka kode tersebut akan dikompilasi, dan Anda akan melanggar tipe invarian List<String> names.
Tipe mentah adalah penghapusan tipe itu
Kembali ke JLS 4.8:
Dimungkinkan untuk menggunakan sebagai tipe penghapusan tipe parameter atau penghapusan tipe array yang tipe elemennya adalah tipe parameter. Jenis seperti ini disebut jenis mentah .
[...]
Superclasses (masing-masing, superinterfaces) dari jenis mentah adalah penghapusan dari superclasses (superinterfaces) dari salah satu parameterisasi dari jenis generik.
Jenis konstruktor, metode contoh, atau non- staticbidang dari jenis mentah Cyang tidak diwarisi dari superclasses atau superinterfaces adalah jenis mentah yang sesuai dengan penghapusan jenisnya dalam deklarasi generik yang sesuai dengan C.
Dalam istilah yang lebih sederhana, ketika jenis mentah digunakan, konstruktor, metode instance dan non- staticbidang juga dihapus .
Penghapusan tipe juga memetakan tanda tangan konstruktor atau metode ke tanda tangan yang tidak memiliki tipe atau variabel tipe parameter. Penghapusan tanda tangan konstruktor atau metode sadalah tanda tangan yang terdiri dari nama yang sama dengan sdan penghapusan semua jenis parameter formal yang diberikan dalam s.
Tipe pengembalian metode dan parameter tipe metode generik atau konstruktor juga mengalami penghapusan jika metode atau tanda tangan konstruktor dihapus.
Penghapusan tanda tangan dari metode generik tidak memiliki parameter tipe.
Laporan bug berikut berisi beberapa pemikiran dari Maurizio Cimadamore, dev compiler, dan Alex Buckley, salah satu penulis JLS, tentang mengapa perilaku semacam ini harus terjadi: https://bugs.openjdk.java.net/browse / JDK-6400189 . (Singkatnya, itu membuat spesifikasi lebih sederhana.)
Jika tidak aman, mengapa diizinkan menggunakan jenis mentah?
Berikut kutipan lain dari JLS 4.8:
Penggunaan jenis mentah hanya diperbolehkan sebagai konsesi untuk kompatibilitas kode warisan. Penggunaan tipe mentah dalam kode yang ditulis setelah pengenalan genericity ke dalam bahasa pemrograman Java sangat tidak dianjurkan. Ada kemungkinan bahwa versi bahasa pemrograman Java di masa depan akan melarang penggunaan tipe mentah.
Java 2nd Edition yang Efektif juga menambahkan ini:
Mengingat Anda tidak boleh menggunakan jenis mentah, mengapa perancang bahasa mengizinkannya? Untuk memberikan kompatibilitas.
Platform Java akan memasuki dekade kedua ketika obat generik diperkenalkan, dan ada sejumlah besar kode Java yang tidak menggunakan obat generik. Itu dianggap penting bahwa semua kode ini tetap legal dan dapat dioperasikan dengan kode baru yang menggunakan generik. Itu harus legal untuk melewati contoh tipe parameter ke metode yang dirancang untuk digunakan dengan tipe biasa, dan sebaliknya. Persyaratan ini, yang dikenal sebagai kompatibilitas migrasi , mendorong keputusan untuk mendukung jenis mentah.
Singkatnya, tipe mentah tidak boleh digunakan dalam kode baru. Anda harus selalu menggunakan tipe parameter .
Apakah tidak ada pengecualian?
Sayangnya, karena generik Java tidak diverifikasi, ada dua pengecualian di mana tipe mentah harus digunakan dalam kode baru:
Literal kelas, mis List.class. TidakList<String>.class
instanceofoperan, mis o instanceof Set. tidako instanceof Set<String>
Apa maksud Anda bahwa, "generik Java tidak diverifikasi"?
Carl G
7
Untuk pengecualian kedua, sintaksis o instanceof Set<?>juga diizinkan untuk menghindari jenis mentah (meskipun hanya dangkal dalam kasus ini).
Paul Bellora
1
Jenis mentah sangat berguna dan mengurangi kode boilerplate jika ada pencarian JNDI untuk kacang yang memperluas antarmuka. Ini menyelesaikan kebutuhan untuk menulis nkacang jarak jauh untuk setiap kelas pelaksana dengan kode yang identik.
djmj
8
"Non-reified" adalah cara lain untuk mengatakan bahwa mereka terhapus. Kompiler tahu apa parameter generiknya, tetapi informasi itu tidak diteruskan ke bytecode yang dihasilkan. JLS mensyaratkan bahwa literal kelas tidak memiliki parameter tipe.
Erick G. Hagstrom
2
@OldCurmudgeon Itu menarik. Maksud saya secara resmi itu bukan , karena kelas literal didefinisikan sebagai adil TypeName.class, di mana TypeNameadalah pengidentifikasi polos ( jls ). Berbicara secara hipotetis, saya kira itu bisa benar-benar baik. Mungkin sebagai petunjuk, List<String>.classadalah varian yang JLS secara spesifik menyebut kesalahan kompiler, jadi jika mereka pernah menambahkannya ke bahasa saya akan berharap bahwa itulah yang mereka gunakan.
Radiodef
62
Apa jenis mentah di Jawa, dan mengapa saya sering mendengar bahwa mereka tidak boleh digunakan dalam kode baru?
Jenis mentah adalah sejarah kuno bahasa Jawa. Pada awalnya ada Collectionsdan mereka memegang Objectstidak lebih dan tidak kurang. Setiap operasi pada Collectionsgips diperlukan dari Objectke tipe yang diinginkan.
List aList =newArrayList();String s ="Hello World!";
aList.add(s);String c =(String)aList.get(0);
Meskipun ini bekerja sebagian besar waktu, kesalahan memang terjadi
List aNumberList =newArrayList();String one ="1";//Number one
aNumberList.add(one);Integer iOne =(Integer)aNumberList.get(0);//Insert ClassCastException here
Koleksi tua tanpa jenis tidak dapat menegakkan keamanan tipe sehingga programmer harus mengingat apa yang disimpannya dalam koleksi.
Generik tempat ditemukan untuk mengatasi batasan ini, pengembang akan mendeklarasikan tipe yang disimpan sekali dan kompiler akan melakukannya sebagai gantinya.
List<String> aNumberList =newArrayList<String>();
aNumberList.add("one");Integer iOne = aNumberList.get(0);//Compile time errorString sOne = aNumberList.get(0);//works fine
Untuk perbandingan:
// Old style collections now known as raw typesList aList =newArrayList();//Could contain anything// New style collections with GenericsList<String> aList =newArrayList<String>();//Contains only Strings
Lebih kompleks dari antarmuka yang Dapat Dibandingkan:
//raw, not type save can compare with Other classesclassMyCompareAbleimplementsCompareAble{int id;publicint compareTo(Object other){returnthis.id -((MyCompareAble)other).id;}}//GenericclassMyCompareAbleimplementsCompareAble<MyCompareAble>{int id;publicint compareTo(MyCompareAble other){returnthis.id - other.id;}}
Perhatikan bahwa tidak mungkin untuk mengimplementasikan CompareAbleantarmuka dengan compareTo(MyCompareAble)tipe mentah. Mengapa Anda tidak menggunakannya?
Setiap yang Objectdisimpan dalam Collectionharus dilemparkan sebelum dapat digunakan
Menggunakan obat generik memungkinkan kompilasi pemeriksaan waktu
Menggunakan tipe mentah sama dengan menyimpan setiap nilai Object
Apa yang dilakukan oleh kompiler: Generik kompatibel ke belakang, mereka menggunakan kelas java yang sama dengan tipe mentah. Keajaiban sebagian besar terjadi pada waktu kompilasi.
List<String> someStrings =newArrayList<String>();
someStrings.add("one");String one = someStrings.get(0);
Akan dikompilasi sebagai:
List someStrings =newArrayList();
someStrings.add("one");String one =(String)someStrings.get(0);
Ini adalah kode yang sama yang akan Anda tulis jika Anda menggunakan jenis mentah secara langsung. Pikir saya tidak yakin apa yang terjadi dengan CompareAbleantarmuka, saya kira itu menciptakan dua compareTofungsi, satu mengambil MyCompareAbledan yang lain mengambil Objectdan meneruskannya ke yang pertama setelah casting itu.
Apa alternatif untuk jenis mentah: Gunakan obat generik
Untuk membuat tipe parameter Box<T>, Anda memberikan argumen tipe aktual untuk parameter tipe formal T:
Box<Integer> intBox =newBox<>();
Jika argumen tipe aktual dihilangkan, Anda membuat tipe mentah Box<T>:
Box rawBox =newBox();
Oleh karena itu, Boxadalah jenis mentah dari jenis generik Box<T>. Namun, kelas atau tipe antarmuka non-generik bukan tipe mentah.
Jenis mentah muncul dalam kode lawas karena banyak kelas API (seperti kelas Koleksi) tidak generik sebelum JDK 5.0. Saat menggunakan tipe mentah, Anda pada dasarnya mendapatkan perilaku pra-generik - a Boxmemberi Anda Object. Untuk kompatibilitas mundur, menetapkan tipe parameter ke tipe mentahnya diperbolehkan:
Box<String> stringBox =newBox<>();Box rawBox = stringBox;// OK
Tetapi jika Anda menetapkan tipe mentah ke tipe parameter, Anda mendapatkan peringatan:
Box rawBox =newBox();// rawBox is a raw type of Box<T>Box<Integer> intBox = rawBox;// warning: unchecked conversion
Anda juga mendapatkan peringatan jika Anda menggunakan tipe mentah untuk memanggil metode generik yang didefinisikan dalam tipe generik yang sesuai:
Peringatan menunjukkan bahwa tipe mentah mem-bypass pemeriksaan tipe generik, menunda tangkapan kode tidak aman ke runtime. Karena itu, Anda harus menghindari penggunaan jenis mentah.
Bagian Type Erasure memiliki informasi lebih lanjut tentang bagaimana kompiler Java menggunakan tipe mentah.
Pesan Kesalahan yang Tidak Dicentang
Seperti disebutkan sebelumnya, saat mencampur kode lawas dengan kode generik, Anda mungkin menemukan pesan peringatan yang mirip dengan yang berikut:
Catatan: Example.java menggunakan operasi yang tidak dicentang atau tidak aman.
Catatan: Kompilasi ulang dengan -Xlint: tidak dicentang untuk detailnya.
Ini bisa terjadi ketika menggunakan API lama yang beroperasi pada tipe mentah, seperti yang ditunjukkan pada contoh berikut:
publicclassWarningDemo{publicstaticvoid main(String[] args){Box<Integer> bi;
bi = createBox();}staticBox createBox(){returnnewBox();}}
Istilah "tidak dicentang" berarti bahwa kompiler tidak memiliki cukup informasi jenis untuk melakukan semua pemeriksaan jenis yang diperlukan untuk memastikan keamanan jenis. Peringatan "tidak dicentang" dinonaktifkan, secara default, meskipun kompiler memberikan petunjuk. Untuk melihat semua peringatan "tidak dicentang", kompilasi ulang dengan -Xlint: tidak dicentang.
Mengkompilasi ulang contoh sebelumnya dengan -Xlint: tidak dicentang mengungkapkan informasi tambahan berikut:
WarningDemo.java:4: warning:[unchecked] unchecked conversion
found :Box
required:Box<java.lang.Integer>
bi = createBox();^1 warning
Untuk sepenuhnya menonaktifkan peringatan yang tidak dicentang, gunakan flag -Xlint: -blecked. The @SuppressWarnings("unchecked")penjelasan menekan peringatan dicentang. Jika Anda tidak terbiasa dengan @SuppressWarningssintaks, lihat Anotasi.
Tipe "mentah" di Jawa adalah kelas yang non-generik dan berkaitan dengan Objek "mentah", dan bukan tipe tipe generik yang aman.
Misalnya, sebelum Java generics tersedia, Anda akan menggunakan kelas koleksi seperti ini:
LinkedList list =newLinkedList();
list.add(newMyObject());MyObject myObject =(MyObject)list.get(0);
Ketika Anda menambahkan objek ke daftar, itu tidak peduli apa jenis objek itu, dan ketika Anda mendapatkannya dari daftar, Anda harus secara eksplisit melemparkannya ke jenis yang Anda harapkan.
Menggunakan obat generik, Anda menghapus faktor "tidak dikenal", karena Anda harus secara eksplisit menentukan jenis objek yang bisa masuk dalam daftar:
LinkedList<MyObject> list =newLinkedList<MyObject>();
list.add(newMyObject());MyObject myObject = list.get(0);
Perhatikan bahwa dengan obat generik Anda tidak harus membuang objek yang berasal dari panggilan panggilan, koleksi ditentukan sebelumnya hanya berfungsi dengan MyObject. Fakta ini adalah faktor pendorong utama untuk obat generik. Ini mengubah sumber kesalahan runtime menjadi sesuatu yang dapat diperiksa pada waktu kompilasi.
Lebih khusus, tipe mentah adalah apa yang Anda dapatkan ketika Anda hanya menghilangkan parameter tipe untuk tipe generik. Jenis mentah benar-benar hanya merupakan fitur kompatibilitas ke belakang, dan berpotensi dapat dihapus. Anda bisa mendapatkan perilaku serupa menggunakan? parameter wildcard.
John Flatness
@zerocrates: serupa tetapi berbeda! Menggunakan ?masih menawarkan keamanan tipe. Saya membahasnya dalam jawaban saya.
polygenelubricants
19
privatestaticList<String> list =newArrayList<String>();
Anda harus menentukan tipe-parameter.
Peringatan itu menyarankan bahwa jenis yang didefinisikan untuk mendukung obat generik harus parameter, daripada menggunakan bentuk mentah mereka.
Listadalah didefinisikan generik dukungan: public class List<E>. Ini memungkinkan banyak jenis operasi aman, yang diperiksa waktu kompilasi.
Sekarang digantikan oleh inferensi intan di Jawa 7 -private static List<String> list = new ArrayList<>();
Ian Campbell
14
Apa jenis mentah dan mengapa saya sering mendengar bahwa mereka tidak boleh digunakan dalam kode baru?
"Tipe mentah" adalah penggunaan kelas generik tanpa menentukan argumen tipe untuk tipe parameternya, misalnya menggunakan Listalih-alih List<String>. Ketika obat generik diperkenalkan ke Jawa, beberapa kelas diperbarui untuk menggunakan obat generik. Menggunakan kelas ini sebagai "tipe mentah" (tanpa menentukan argumen tipe) memungkinkan kode lama masih dikompilasi.
"Jenis mentah" digunakan untuk kompatibilitas ke belakang. Penggunaannya dalam kode baru tidak dianjurkan karena menggunakan kelas generik dengan argumen tipe memungkinkan pengetikan yang lebih kuat, yang pada gilirannya dapat meningkatkan pemahaman kode dan mengarah pada menangkap masalah potensial sebelumnya.
Apa alternatifnya jika kita tidak bisa menggunakan jenis mentah, dan bagaimana itu lebih baik?
Alternatif yang lebih disukai adalah dengan menggunakan kelas generik sebagaimana dimaksud - dengan argumen tipe yang sesuai (misalnya List<String>). Hal ini memungkinkan programmer untuk menentukan tipe yang lebih spesifik, menyampaikan lebih banyak makna kepada pengelola masa depan tentang tujuan penggunaan variabel atau struktur data, dan memungkinkan kompiler untuk menegakkan tipe-keamanan yang lebih baik. Keunggulan ini bersama-sama dapat meningkatkan kualitas kode dan membantu mencegah pengenalan beberapa kesalahan pengkodean.
Misalnya, untuk metode di mana pemrogram ingin memastikan variabel Daftar yang disebut 'nama' hanya berisi Strings:
List<String> names =newArrayList<String>();
names.add("John");// OK
names.add(newInteger(1));// compile error
Ah, jadi tergoda untuk menyalin polygenelubricantsreferensi "tipe mentah" dari stackoverflow.com/questions/2770111/… ke dalam jawaban saya sendiri, tapi saya rasa saya akan membiarkannya untuk digunakan dalam jawaban sendiri.
Bert F
1
ya, pada dasarnya saya telah menyalin dan menempel segmen itu di mana-mana orang menggunakan tipe mentah pada stackoverflow, dan akhirnya memutuskan untuk hanya memiliki satu pertanyaan untuk merujuk mulai sekarang. Saya harap ini merupakan kontribusi yang baik bagi masyarakat.
polygenelubricants
1
@polygenelubricants yang saya perhatikan - kami menekan beberapa pertanyaan yang sama :-)
Bert F
1
@ ha9u63ar: Memang. Secara umum jawaban singkat dan sederhana setidaknya sama baiknya dengan yang lama dan diterima.
displayName
Apa itu "syping yang lebih kuat"?
carloswm85
12
Kompiler ingin Anda menulis ini:
privatestaticList<String> list =newArrayList<String>();
karena jika tidak, Anda dapat menambahkan jenis apa pun yang Anda suka list, menjadikan instantiasi sebagai hal yang new ArrayList<String>()tidak berguna. Java generics adalah fitur waktu kompilasi saja, jadi objek yang dibuat dengan new ArrayList<String>()senang hati akan menerima Integeratau JFrameelemen jika ditugaskan untuk referensi "tipe mentah" List- objek itu sendiri tidak tahu apa-apa tentang jenis apa yang seharusnya dikandungnya, hanya kompiler yang melakukannya.
ArrayList<String> arritu adalah ArrayListvariabel referensi dengan tipe Stringyang merujuk ke ArralyListObject of Type String. Ini berarti ia hanya bisa menampung objek tipe String.
Ini adalah Ketat untuk Stringbukan Jenis Mentah jadi, itu tidak akan pernah memunculkan peringatan.
arr.add("hello");// alone statement will compile successfully and no warning.
arr.add(23);//prone to compile time error.//error: no suitable method found for add(int)
Kasus 2
Dalam hal ini ArrayList<String> arradalah tipe yang ketat tetapi Objek Anda new ArrayList();adalah tipe mentah.
arr.add("hello");//alone this compile but raise the warning.
arr.add(23);//again prone to compile time error.//error: no suitable method found for add(int)
di sini arradalah tipe yang ketat. Jadi, itu akan meningkatkan kesalahan waktu kompilasi saat menambahkan a integer.
Peringatan : - RawObjek Tipe direferensikan ke Stricttipe Variabel yang direferensikan ArrayList.
Kasus 3
Dalam hal ini ArrayList arradalah tipe mentah tetapi Objek Anda new ArrayList<String>();adalah tipe Ketat.
arr.add("hello");
arr.add(23);//compiles fine but raise the warning.
Ini akan menambahkan semua jenis Obyek ke dalamnya karena arrmerupakan Jenis Mentah.
Peringatan : - StrictObjek Tipe direferensikan ke rawtipe Variabel yang dirujuk.
Jenis baku adalah kurangnya parameter tipe saat menggunakan tipe generik.
Baku-jenis tidak boleh digunakan karena bisa menyebabkan kesalahan runtime, seperti memasukkan doubleke dalam apa yang seharusnya menjadi Setdari ints.
Set set =newHashSet();
set.add(3.45);//ok
Saat mengambil barang dari Set, Anda tidak tahu apa yang keluar. Mari kita asumsikan bahwa Anda mengharapkannya menjadi milik semua orang int, Anda akan mengubahnya Integer; pengecualian saat runtime ketika double3,45 datang.
Dengan parameter tipe yang ditambahkan ke Anda Set, Anda akan mendapatkan kesalahan kompilasi sekaligus. Kesalahan preemptive ini memungkinkan Anda memperbaiki masalah sebelum sesuatu meledak selama runtime (sehingga menghemat waktu dan usaha).
Set<Integer> set =newHashSet<Integer>();
set.add(3.45);//NOT ok.
Seperti yang disebutkan dalam jawaban yang diterima, Anda kehilangan semua dukungan untuk obat generik dalam kode jenis mentah. Setiap tipe parameter dikonversi ke penghapusannya (yang dalam contoh di atas adalah adil Object).
Apa yang dikatakan adalah bahwa Anda listadalah Listobjek yang tidak ditentukan. Itu adalah bahwa Java tidak tahu objek apa yang ada di dalam daftar. Kemudian ketika Anda ingin mengulang daftar Anda harus membuang setiap elemen, untuk dapat mengakses properti dari elemen itu (dalam hal ini, String).
Secara umum adalah ide yang lebih baik untuk parametrize koleksi, sehingga Anda tidak memiliki masalah konversi, Anda hanya akan dapat menambahkan elemen dari tipe parametrized dan editor Anda akan menawarkan metode yang tepat untuk dipilih.
privatestaticList<String> list =newArrayList<String>();
Tipe mentah merujuk pada penggunaan tipe generik tanpa menentukan parameter tipe.
Misalnya ,
Daftar adalah tipe mentah, sedangkan List<String>tipe tipe parameter.
Ketika obat generik diperkenalkan di JDK 1.5, tipe mentah dipertahankan hanya untuk mempertahankan kompatibilitas dengan versi Java yang lebih lama. Meskipun menggunakan jenis mentah masih memungkinkan,
Mereka harus dihindari :
Mereka biasanya membutuhkan gips
Mereka tidak mengetik aman, dan beberapa jenis kesalahan penting hanya akan muncul saat runtime
Mereka kurang ekspresif, dan tidak mendokumentasikan diri dengan cara yang sama seperti contoh tipe parameter
import java.util.*;publicfinalclassAvoidRawTypes{void withRawType(){//Raw List doesn't self-document, //doesn't state explicitly what it can containList stars =Arrays.asList("Arcturus","Vega","Altair");Iterator iter = stars.iterator();while(iter.hasNext()){String star =(String) iter.next();//cast needed
log(star);}}void withParameterizedType(){List<String> stars =Arrays.asList("Spica","Regulus","Antares");for(String star: stars){
log(star);}}privatevoid log(Object message){System.out.println(Objects.toString(message));}}
Saya menemukan halaman ini setelah melakukan beberapa latihan sampel dan memiliki kebingungan yang sama persis.
============== Saya beralih dari kode ini seperti yang disediakan oleh sampel ===============
publicstaticvoid main(String[] args)throwsIOException{Map wordMap =newHashMap();if(args.length >0){for(int i =0; i < args.length; i++){
countWord(wordMap, args[i]);}}else{
getWordFrequency(System.in, wordMap);}for(Iterator i = wordMap.entrySet().iterator(); i.hasNext();){Map.Entry entry =(Map.Entry) i.next();System.out.println(entry.getKey()+" :\t"+ entry.getValue());}
====================== Ke Kode Ini ========================
publicstaticvoid main(String[] args)throwsIOException{// replace with TreeMap to get them sorted by nameMap<String,Integer> wordMap =newHashMap<String,Integer>();if(args.length >0){for(int i =0; i < args.length; i++){
countWord(wordMap, args[i]);}}else{
getWordFrequency(System.in, wordMap);}for(Iterator<Entry<String,Integer>> i = wordMap.entrySet().iterator(); i.hasNext();){Entry<String,Integer> entry = i.next();System.out.println(entry.getKey()+" :\t"+ entry.getValue());}}
Jenis mentah baik-baik saja ketika mereka mengekspresikan apa yang ingin Anda ungkapkan.
Misalnya, fungsi deserialisasi mungkin mengembalikan a List, tetapi tidak tahu tipe elemen daftar. Jadi Listadalah tipe pengembalian yang sesuai di sini.
Jawaban:
Apa itu jenis mentah?
Spesifikasi Bahasa Jawa mendefinisikan jenis mentah sebagai berikut:
JLS 4.8 Jenis Baku
Berikut ini contoh untuk diilustrasikan:
Di sini,
MyType<E>
adalah tipe parameter ( JLS 4.5 ). Adalah umum untuk bahasa sehari-hari menyebut jenis ini hanya sebagaiMyType
singkat, tetapi secara teknis namanyaMyType<E>
.mt
memiliki tipe mentah (dan menghasilkan peringatan kompilasi) pada poin pertama dalam definisi di atas;inn
juga memiliki tipe mentah pada titik peluru ketiga.MyType.Nested
bukan tipe parameter, meskipun itu tipe tipe tipe parameterMyType<E>
, karena itustatic
.mt1
, danmt2
keduanya dideklarasikan dengan parameter tipe aktual, jadi mereka bukan tipe mentah.Apa yang istimewa dari jenis mentah?
Pada dasarnya, tipe mentah berperilaku seperti mereka sebelum obat generik diperkenalkan. Artinya, berikut ini sepenuhnya legal pada waktu kompilasi.
Kode di atas berjalan dengan baik, tetapi anggap Anda juga memiliki yang berikut ini:
Sekarang kita mengalami masalah pada saat run-time, karena
names
mengandung sesuatu yang bukaninstanceof String
.Agaknya, jika Anda hanya ingin
names
mengandungString
, Anda mungkin masih bisa menggunakan jenis mentah dan memeriksaadd
sendiri secara manual , lalu secara manual memasukkan keString
setiap item darinames
. Lebih baik lagi , meskipun BUKAN menggunakan tipe mentah dan biarkan kompiler melakukan semua pekerjaan untuk Anda , memanfaatkan kekuatan generik Java.Tentu saja, jika Anda DO ingin
names
untuk memungkinkanBoolean
, maka Anda dapat mendeklarasikan sebagaiList<Object> names
, dan kode di atas akan mengkompilasi.Lihat juga
Bagaimana jenis mentah berbeda dari menggunakan
<Object>
sebagai tipe parameter?Berikut ini adalah kutipan dari Effective Java 2nd Edition, Item 23: Jangan gunakan tipe mentah dalam kode baru :
Untuk mengilustrasikan poin, pertimbangkan metode berikut yang mengambil a
List<Object>
dan menambahkan anew Object()
.Generik di Jawa tidak berubah. A
List<String>
bukanList<Object>
, jadi yang berikut ini akan menghasilkan peringatan kompiler:Jika Anda telah menyatakan
appendNewObject
untuk mengambil tipe mentahList
sebagai parameter, maka ini akan dikompilasi, dan karena itu Anda akan kehilangan jenis keamanan yang Anda dapatkan dari obat generik.Lihat juga
<E extends Number>
dan<Number>
?Bagaimana jenis mentah berbeda dari menggunakan
<?>
sebagai parameter tipe?List<Object>
,,List<String>
dll semuanyaList<?>
, jadi mungkin tergoda untuk mengatakan bahwa mereka hanyaList
sebaliknya. Namun, ada perbedaan besar: karenaList<E>
hanya mendefinisikanadd(E)
, Anda tidak dapat menambahkan sembarang objek sembarang ke aList<?>
. Di sisi lain, karena jenis mentahList
tidak memiliki keamanan jenis, Anda dapatadd
apa saja untuk aList
.Pertimbangkan variasi cuplikan sebelumnya:
Kompiler melakukan pekerjaan luar biasa untuk melindungi Anda dari kemungkinan pelanggaran jenis invarian
List<?>
! Jika Anda telah mendeklarasikan parameter sebagai tipe mentahList list
, maka kode tersebut akan dikompilasi, dan Anda akan melanggar tipe invarianList<String> names
.Tipe mentah adalah penghapusan tipe itu
Kembali ke JLS 4.8:
Dalam istilah yang lebih sederhana, ketika jenis mentah digunakan, konstruktor, metode instance dan non-
static
bidang juga dihapus .Ambil contoh berikut:
Ketika kita menggunakan mentah
MyType
,getNames
terhapus juga, sehingga mengembalikan mentahList
!JLS 4.6 terus menjelaskan yang berikut:
Laporan bug berikut berisi beberapa pemikiran dari Maurizio Cimadamore, dev compiler, dan Alex Buckley, salah satu penulis JLS, tentang mengapa perilaku semacam ini harus terjadi: https://bugs.openjdk.java.net/browse / JDK-6400189 . (Singkatnya, itu membuat spesifikasi lebih sederhana.)
Jika tidak aman, mengapa diizinkan menggunakan jenis mentah?
Berikut kutipan lain dari JLS 4.8:
Java 2nd Edition yang Efektif juga menambahkan ini:
Singkatnya, tipe mentah tidak boleh digunakan dalam kode baru. Anda harus selalu menggunakan tipe parameter .
Apakah tidak ada pengecualian?
Sayangnya, karena generik Java tidak diverifikasi, ada dua pengecualian di mana tipe mentah harus digunakan dalam kode baru:
List.class
. TidakList<String>.class
instanceof
operan, miso instanceof Set
. tidako instanceof Set<String>
Lihat juga
Collection<String>.class
ilegal?sumber
o instanceof Set<?>
juga diizinkan untuk menghindari jenis mentah (meskipun hanya dangkal dalam kasus ini).n
kacang jarak jauh untuk setiap kelas pelaksana dengan kode yang identik.TypeName.class
, di manaTypeName
adalah pengidentifikasi polos ( jls ). Berbicara secara hipotetis, saya kira itu bisa benar-benar baik. Mungkin sebagai petunjuk,List<String>.class
adalah varian yang JLS secara spesifik menyebut kesalahan kompiler, jadi jika mereka pernah menambahkannya ke bahasa saya akan berharap bahwa itulah yang mereka gunakan.Jenis mentah adalah sejarah kuno bahasa Jawa. Pada awalnya ada
Collections
dan mereka memegangObjects
tidak lebih dan tidak kurang. Setiap operasi padaCollections
gips diperlukan dariObject
ke tipe yang diinginkan.Meskipun ini bekerja sebagian besar waktu, kesalahan memang terjadi
Koleksi tua tanpa jenis tidak dapat menegakkan keamanan tipe sehingga programmer harus mengingat apa yang disimpannya dalam koleksi.
Generik tempat ditemukan untuk mengatasi batasan ini, pengembang akan mendeklarasikan tipe yang disimpan sekali dan kompiler akan melakukannya sebagai gantinya.
Untuk perbandingan:
Lebih kompleks dari antarmuka yang Dapat Dibandingkan:
Perhatikan bahwa tidak mungkin untuk mengimplementasikan
CompareAble
antarmuka dengancompareTo(MyCompareAble)
tipe mentah. Mengapa Anda tidak menggunakannya?Object
disimpan dalamCollection
harus dilemparkan sebelum dapat digunakanObject
Apa yang dilakukan oleh kompiler: Generik kompatibel ke belakang, mereka menggunakan kelas java yang sama dengan tipe mentah. Keajaiban sebagian besar terjadi pada waktu kompilasi.
Akan dikompilasi sebagai:
Ini adalah kode yang sama yang akan Anda tulis jika Anda menggunakan jenis mentah secara langsung. Pikir saya tidak yakin apa yang terjadi dengan
CompareAble
antarmuka, saya kira itu menciptakan duacompareTo
fungsi, satu mengambilMyCompareAble
dan yang lain mengambilObject
dan meneruskannya ke yang pertama setelah casting itu.Apa alternatif untuk jenis mentah: Gunakan obat generik
sumber
Tipe mentah adalah nama kelas generik atau antarmuka tanpa argumen tipe apa pun. Misalnya, diberikan kelas Kotak generik:
Untuk membuat tipe parameter
Box<T>
, Anda memberikan argumen tipe aktual untuk parameter tipe formalT
:Jika argumen tipe aktual dihilangkan, Anda membuat tipe mentah
Box<T>
:Oleh karena itu,
Box
adalah jenis mentah dari jenis generikBox<T>
. Namun, kelas atau tipe antarmuka non-generik bukan tipe mentah.Jenis mentah muncul dalam kode lawas karena banyak kelas API (seperti kelas Koleksi) tidak generik sebelum JDK 5.0. Saat menggunakan tipe mentah, Anda pada dasarnya mendapatkan perilaku pra-generik - a
Box
memberi AndaObject
. Untuk kompatibilitas mundur, menetapkan tipe parameter ke tipe mentahnya diperbolehkan:Tetapi jika Anda menetapkan tipe mentah ke tipe parameter, Anda mendapatkan peringatan:
Anda juga mendapatkan peringatan jika Anda menggunakan tipe mentah untuk memanggil metode generik yang didefinisikan dalam tipe generik yang sesuai:
Peringatan menunjukkan bahwa tipe mentah mem-bypass pemeriksaan tipe generik, menunda tangkapan kode tidak aman ke runtime. Karena itu, Anda harus menghindari penggunaan jenis mentah.
Bagian Type Erasure memiliki informasi lebih lanjut tentang bagaimana kompiler Java menggunakan tipe mentah.
Pesan Kesalahan yang Tidak Dicentang
Seperti disebutkan sebelumnya, saat mencampur kode lawas dengan kode generik, Anda mungkin menemukan pesan peringatan yang mirip dengan yang berikut:
Ini bisa terjadi ketika menggunakan API lama yang beroperasi pada tipe mentah, seperti yang ditunjukkan pada contoh berikut:
Istilah "tidak dicentang" berarti bahwa kompiler tidak memiliki cukup informasi jenis untuk melakukan semua pemeriksaan jenis yang diperlukan untuk memastikan keamanan jenis. Peringatan "tidak dicentang" dinonaktifkan, secara default, meskipun kompiler memberikan petunjuk. Untuk melihat semua peringatan "tidak dicentang", kompilasi ulang dengan -Xlint: tidak dicentang.
Mengkompilasi ulang contoh sebelumnya dengan -Xlint: tidak dicentang mengungkapkan informasi tambahan berikut:
Untuk sepenuhnya menonaktifkan peringatan yang tidak dicentang, gunakan flag -Xlint: -blecked. The
@SuppressWarnings("unchecked")
penjelasan menekan peringatan dicentang. Jika Anda tidak terbiasa dengan@SuppressWarnings
sintaks, lihat Anotasi.Sumber asli: Tutorial Java
sumber
Tipe "mentah" di Jawa adalah kelas yang non-generik dan berkaitan dengan Objek "mentah", dan bukan tipe tipe generik yang aman.
Misalnya, sebelum Java generics tersedia, Anda akan menggunakan kelas koleksi seperti ini:
Ketika Anda menambahkan objek ke daftar, itu tidak peduli apa jenis objek itu, dan ketika Anda mendapatkannya dari daftar, Anda harus secara eksplisit melemparkannya ke jenis yang Anda harapkan.
Menggunakan obat generik, Anda menghapus faktor "tidak dikenal", karena Anda harus secara eksplisit menentukan jenis objek yang bisa masuk dalam daftar:
Perhatikan bahwa dengan obat generik Anda tidak harus membuang objek yang berasal dari panggilan panggilan, koleksi ditentukan sebelumnya hanya berfungsi dengan MyObject. Fakta ini adalah faktor pendorong utama untuk obat generik. Ini mengubah sumber kesalahan runtime menjadi sesuatu yang dapat diperiksa pada waktu kompilasi.
sumber
?
masih menawarkan keamanan tipe. Saya membahasnya dalam jawaban saya.Anda harus menentukan tipe-parameter.
Peringatan itu menyarankan bahwa jenis yang didefinisikan untuk mendukung obat generik harus parameter, daripada menggunakan bentuk mentah mereka.
List
adalah didefinisikan generik dukungan:public class List<E>
. Ini memungkinkan banyak jenis operasi aman, yang diperiksa waktu kompilasi.sumber
private static List<String> list = new ArrayList<>();
Apa jenis mentah dan mengapa saya sering mendengar bahwa mereka tidak boleh digunakan dalam kode baru?
"Tipe mentah" adalah penggunaan kelas generik tanpa menentukan argumen tipe untuk tipe parameternya, misalnya menggunakan
List
alih-alihList<String>
. Ketika obat generik diperkenalkan ke Jawa, beberapa kelas diperbarui untuk menggunakan obat generik. Menggunakan kelas ini sebagai "tipe mentah" (tanpa menentukan argumen tipe) memungkinkan kode lama masih dikompilasi."Jenis mentah" digunakan untuk kompatibilitas ke belakang. Penggunaannya dalam kode baru tidak dianjurkan karena menggunakan kelas generik dengan argumen tipe memungkinkan pengetikan yang lebih kuat, yang pada gilirannya dapat meningkatkan pemahaman kode dan mengarah pada menangkap masalah potensial sebelumnya.
Apa alternatifnya jika kita tidak bisa menggunakan jenis mentah, dan bagaimana itu lebih baik?
Alternatif yang lebih disukai adalah dengan menggunakan kelas generik sebagaimana dimaksud - dengan argumen tipe yang sesuai (misalnya
List<String>
). Hal ini memungkinkan programmer untuk menentukan tipe yang lebih spesifik, menyampaikan lebih banyak makna kepada pengelola masa depan tentang tujuan penggunaan variabel atau struktur data, dan memungkinkan kompiler untuk menegakkan tipe-keamanan yang lebih baik. Keunggulan ini bersama-sama dapat meningkatkan kualitas kode dan membantu mencegah pengenalan beberapa kesalahan pengkodean.Misalnya, untuk metode di mana pemrogram ingin memastikan variabel Daftar yang disebut 'nama' hanya berisi Strings:
sumber
polygenelubricants
referensi "tipe mentah" dari stackoverflow.com/questions/2770111/… ke dalam jawaban saya sendiri, tapi saya rasa saya akan membiarkannya untuk digunakan dalam jawaban sendiri.Kompiler ingin Anda menulis ini:
karena jika tidak, Anda dapat menambahkan jenis apa pun yang Anda suka
list
, menjadikan instantiasi sebagai hal yangnew ArrayList<String>()
tidak berguna. Java generics adalah fitur waktu kompilasi saja, jadi objek yang dibuat dengannew ArrayList<String>()
senang hati akan menerimaInteger
atauJFrame
elemen jika ditugaskan untuk referensi "tipe mentah"List
- objek itu sendiri tidak tahu apa-apa tentang jenis apa yang seharusnya dikandungnya, hanya kompiler yang melakukannya.sumber
Inilah saya Mempertimbangkan beberapa kasus di mana Anda dapat mengklarifikasi konsep
Kasus 1
ArrayList<String> arr
itu adalahArrayList
variabel referensi dengan tipeString
yang merujuk keArralyList
Object of TypeString
. Ini berarti ia hanya bisa menampung objek tipe String.Ini adalah Ketat untuk
String
bukan Jenis Mentah jadi, itu tidak akan pernah memunculkan peringatan.Kasus 2
Dalam hal ini
ArrayList<String> arr
adalah tipe yang ketat tetapi Objek Andanew ArrayList();
adalah tipe mentah.di sini
arr
adalah tipe yang ketat. Jadi, itu akan meningkatkan kesalahan waktu kompilasi saat menambahkan ainteger
.Kasus 3
Dalam hal ini
ArrayList arr
adalah tipe mentah tetapi Objek Andanew ArrayList<String>();
adalah tipe Ketat.Ini akan menambahkan semua jenis Obyek ke dalamnya karena
arr
merupakan Jenis Mentah.sumber
Jenis baku adalah kurangnya parameter tipe saat menggunakan tipe generik.
Baku-jenis tidak boleh digunakan karena bisa menyebabkan kesalahan runtime, seperti memasukkan
double
ke dalam apa yang seharusnya menjadiSet
dariint
s.Saat mengambil barang dari
Set
, Anda tidak tahu apa yang keluar. Mari kita asumsikan bahwa Anda mengharapkannya menjadi milik semua orangint
, Anda akan mengubahnyaInteger
; pengecualian saat runtime ketikadouble
3,45 datang.Dengan parameter tipe yang ditambahkan ke Anda
Set
, Anda akan mendapatkan kesalahan kompilasi sekaligus. Kesalahan preemptive ini memungkinkan Anda memperbaiki masalah sebelum sesuatu meledak selama runtime (sehingga menghemat waktu dan usaha).sumber
Inilah kasus lain di mana jenis mentah akan menggigit Anda:
Seperti yang disebutkan dalam jawaban yang diterima, Anda kehilangan semua dukungan untuk obat generik dalam kode jenis mentah. Setiap tipe parameter dikonversi ke penghapusannya (yang dalam contoh di atas adalah adil
Object
).sumber
Apa yang dikatakan adalah bahwa Anda
list
adalahList
objek yang tidak ditentukan. Itu adalah bahwa Java tidak tahu objek apa yang ada di dalam daftar. Kemudian ketika Anda ingin mengulang daftar Anda harus membuang setiap elemen, untuk dapat mengakses properti dari elemen itu (dalam hal ini, String).Secara umum adalah ide yang lebih baik untuk parametrize koleksi, sehingga Anda tidak memiliki masalah konversi, Anda hanya akan dapat menambahkan elemen dari tipe parametrized dan editor Anda akan menawarkan metode yang tepat untuk dipilih.
sumber
halaman tutorial .
Tipe mentah adalah nama kelas generik atau antarmuka tanpa argumen tipe apa pun. Misalnya, diberikan kelas Kotak generik:
Untuk membuat tipe Kotak parameter, Anda memberikan argumen tipe aktual untuk tipe formal parameter T:
Jika argumen tipe aktual dihilangkan, Anda membuat tipe Kotak mentah:
sumber
Hindari jenis mentah
Misalnya ,
Daftar adalah tipe mentah, sedangkan
List<String>
tipe tipe parameter.Ketika obat generik diperkenalkan di JDK 1.5, tipe mentah dipertahankan hanya untuk mempertahankan kompatibilitas dengan versi Java yang lebih lama. Meskipun menggunakan jenis mentah masih memungkinkan,
Mereka harus dihindari :
Mereka kurang ekspresif, dan tidak mendokumentasikan diri dengan cara yang sama seperti contoh tipe parameter
Untuk referensi : https://docs.oracle.com/javase/tutorial/java/generics/rawTypes.html
sumber
Saya menemukan halaman ini setelah melakukan beberapa latihan sampel dan memiliki kebingungan yang sama persis.
============== Saya beralih dari kode ini seperti yang disediakan oleh sampel ===============
====================== Ke Kode Ini ========================
================================================== =============================
Mungkin lebih aman tetapi butuh 4 jam untuk meruntuhkan filosofi ...
sumber
Jenis mentah baik-baik saja ketika mereka mengekspresikan apa yang ingin Anda ungkapkan.
Misalnya, fungsi deserialisasi mungkin mengembalikan a
List
, tetapi tidak tahu tipe elemen daftar. JadiList
adalah tipe pengembalian yang sesuai di sini.sumber