Apa itu jenis mentah dan mengapa kita tidak menggunakannya?

662

Pertanyaan:

  • Apa jenis mentah di Jawa, dan mengapa saya sering mendengar bahwa mereka tidak boleh digunakan dalam kode baru?
  • Apa alternatifnya jika kita tidak bisa menggunakan jenis mentah, dan bagaimana itu lebih baik?
polygenelubricants
sumber
tutorial java masih menggunakan JComboBox yang menyebabkan peringatan ini. Versi kotak kombo yang mana yang tidak akan menyebabkan peringatan ini? docs.oracle.com/javase/tutorial/uiswing/components/…
SuperStar
1
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:

JLS 4.8 Jenis Baku

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:

public class MyType<E> {
    class Inner { }
    static class Nested { }

    public static void main(String[] args) {
        MyType mt;          // warning: MyType is a raw type
        MyType.Inner inn;   // warning: MyType.Inner is a raw type

        MyType.Nested nest; // no warning: not parameterized type
        MyType<Object> mt1; // no warning: type parameter given
        MyType<?> 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 = new ArrayList(); // 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 memeriksa add 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.

List<String> names = new ArrayList<String>();
names.add("John");
names.add("Mary");
names.add(Boolean.FALSE); // compilation error!

Tentu saja, jika Anda DO ingin namesuntuk memungkinkan Boolean, maka Anda dapat mendeklarasikan sebagai List<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 :

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().

void appendNewObject(List<Object> list) {
   list.add(new Object());
}

Generik di Jawa tidak berubah. A List<String>bukan List<Object>, jadi yang berikut ini akan menghasilkan peringatan kompiler:

List<String> names = new ArrayList<String>();
appendNewObject(names); // compilation error!

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.

Lihat juga


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:

static void appendNewObject(List<?> list) {
    list.add(new Object()); // compilation error!
}
//...

List<String> names = new ArrayList<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 .

Ambil contoh berikut:

class MyType<E> {
    List<String> getNames() {
        return Arrays.asList("John", "Mary");
    }

    public static void main(String[] args) {
        MyType rawType = new MyType();
        // unchecked warning!
        // required: List<String> found: List
        List<String> names = rawType.getNames();
        // compilation error!
        // incompatible types: Object cannot be converted to String
        for (String str : rawType.getNames())
            System.out.print(str);
    }
}

Ketika kita menggunakan mentah MyType, getNamesterhapus juga, sehingga mengembalikan mentah List!

JLS 4.6 terus menjelaskan yang berikut:

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>

Lihat juga

polygenelubricants
sumber
16
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 = new ArrayList();
String s = "Hello World!";
aList.add(s);
String c = (String)aList.get(0);

Meskipun ini bekerja sebagian besar waktu, kesalahan memang terjadi

List aNumberList = new ArrayList();
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 = new ArrayList<String>();
aNumberList.add("one");
Integer iOne = aNumberList.get(0);//Compile time error
String sOne = aNumberList.get(0);//works fine

Untuk perbandingan:

// Old style collections now known as raw types
List aList = new ArrayList(); //Could contain anything
// New style collections with Generics
List<String> aList = new ArrayList<String>(); //Contains only Strings

Lebih kompleks dari antarmuka yang Dapat Dibandingkan:

//raw, not type save can compare with Other classes
class MyCompareAble implements CompareAble
{
   int id;
   public int compareTo(Object other)
   {return this.id - ((MyCompareAble)other).id;}
}
//Generic
class MyCompareAble implements CompareAble<MyCompareAble>
{
   int id;
   public int compareTo(MyCompareAble other)
   {return this.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 = new ArrayList<String>();
someStrings.add("one");
String one = someStrings.get(0);

Akan dikompilasi sebagai:

List someStrings = new ArrayList();
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

Josefx
sumber
30

Tipe mentah adalah nama kelas generik atau antarmuka tanpa argumen tipe apa pun. Misalnya, diberikan kelas Kotak generik:

public class Box<T> {
    public void set(T t) { /* ... */ }
    // ...
}

Untuk membuat tipe parameter Box<T>, Anda memberikan argumen tipe aktual untuk parameter tipe formal T:

Box<Integer> intBox = new Box<>();

Jika argumen tipe aktual dihilangkan, Anda membuat tipe mentah Box<T>:

Box rawBox = new Box();

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 = new Box<>();
Box rawBox = stringBox;               // OK

Tetapi jika Anda menetapkan tipe mentah ke tipe parameter, Anda mendapatkan peringatan:

Box rawBox = new Box();           // 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:

Box<String> stringBox = new Box<>();
Box rawBox = stringBox;
rawBox.set(8);  // warning: unchecked invocation to set(T)

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:

public class WarningDemo {
    public static void main(String[] args){
        Box<Integer> bi;
        bi = createBox();
    }

    static Box createBox(){
        return new Box();
    }
}

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.

Sumber asli: Tutorial Java

Adelin
sumber
21

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 = new LinkedList();
list.add(new MyObject());
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 = new LinkedList<MyObject>();
list.add(new MyObject());
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.

Andy White
sumber
3
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
 private static List<String> list = new ArrayList<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.

Bozho
sumber
3
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 = new ArrayList<String>();
names.add("John");          // OK
names.add(new Integer(1));  // compile error
Bert F
sumber
1
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:

private static List<String> list = new ArrayList<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.

Michael Borgwardt
sumber
12

Inilah saya Mempertimbangkan beberapa kasus di mana Anda dapat mengklarifikasi konsep

1. ArrayList<String> arr = new ArrayList<String>();
2. ArrayList<String> arr = new ArrayList();
3. ArrayList arr = new ArrayList<String>();

Kasus 1

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.

Vikrant Kashyap
sumber
8

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 = new HashSet();
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 = new HashSet<Integer>();
set.add(3.45); //NOT ok.
Lars Andren
sumber
7

Inilah kasus lain di mana jenis mentah akan menggigit Anda:

public class StrangeClass<T> {
  @SuppressWarnings("unchecked")
  public <X> X getSomethingElse() {
    return (X)"Testing something else!";
  }

  public static void main(String[] args) {
    final StrangeClass<String> withGeneric    = new StrangeClass<>();
    final StrangeClass         withoutGeneric = new StrangeClass();
    final String               value1,
                               value2;

    // Compiles
    value1 = withGeneric.getSomethingElse();

    // Produces compile error:
    // incompatible types: java.lang.Object cannot be converted to java.lang.String
    value2 = withoutGeneric.getSomethingElse();
  }
}

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).

GuyPaddock
sumber
5

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.

private static List<String> list = new ArrayList<String>();
pakore
sumber
4

halaman tutorial .

Tipe mentah adalah nama kelas generik atau antarmuka tanpa argumen tipe apa pun. Misalnya, diberikan kelas Kotak generik:

public class Box<T> {
    public void set(T t) { /* ... */ }
    // ...
}

Untuk membuat tipe Kotak parameter, Anda memberikan argumen tipe aktual untuk tipe formal parameter T:

Box<Integer> intBox = new Box<>();

Jika argumen tipe aktual dihilangkan, Anda membuat tipe Kotak mentah:

Box rawBox = new Box();
Mykhaylo Adamovych
sumber
2

Hindari jenis mentah

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.*;
    
    public final class AvoidRawTypes {
    
    void withRawType() {
    
        //Raw List doesn't self-document, 
        //doesn't state explicitly what it can contain
    
        List 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);
        }
    
    }
    
    private void log(Object message) {
    
        System.out.println(Objects.toString(message));
    
    }
    
    }

Untuk referensi : https://docs.oracle.com/javase/tutorial/java/generics/rawTypes.html

Ashish
sumber
1

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 ===============

public static void main(String[] args) throws IOException {

    Map wordMap = new HashMap();
    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 ========================

public static void main(String[] args) throws IOException {
    // replace with TreeMap to get them sorted by name
    Map<String, Integer> wordMap = new HashMap<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());
    }

}

================================================== =============================

Mungkin lebih aman tetapi butuh 4 jam untuk meruntuhkan filosofi ...

pengguna2442615
sumber
0

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.

Stefan Reich
sumber
Kamu bisa menggunakan ? sebagai tipe parameter
Dániel Kis
Ya, tapi itu lebih banyak untuk mengetik dan saya tidak suka mengetik lagi. :)
Stefan Reich