JavaRush /Blog Java /Random-MS /Apakah generik dalam Java

Apakah generik dalam Java

Diterbitkan dalam kumpulan
hello! Hari ini kita akan bercakap tentang generik. Saya mesti mengatakan bahawa anda akan belajar banyak perkara baru! Bukan itu sahaja, malah beberapa kuliah seterusnya akan ditumpukan kepada generik. Apakah generik dalam Java - 1 Oleh itu, jika topik ini menarik kepada anda, anda bertuah: hari ini anda akan belajar banyak tentang ciri-ciri generik. Nah, jika tidak, bertenang dan berehat! :) Ini adalah topik yang sangat penting dan anda perlu mengetahuinya. Mari kita mulakan dengan yang mudah: “apa” dan “mengapa”. Apakah generik? Generik ialah jenis dengan parameter. Apabila mencipta generik, anda bukan sahaja menentukan jenisnya, tetapi juga jenis data yang harus berfungsi dengannya. Saya rasa contoh yang paling jelas telah terlintas di fikiran anda - ini ialah ArrayList! Begini cara kami biasanya menciptanya dalam program:
import java.util.ArrayList;
import java.util.List;

public class Main {

   public static void main(String[] args) {

       List<String> myList1 = new ArrayList<>();
       myList1.add("Test String 1");
       myList1.add("Test String 2");
   }
}
Seperti yang anda mungkin rasa, keistimewaan senarai itu ialah tidak mungkin untuk "memasukkan" segala-galanya ke dalamnya: ia berfungsi secara eksklusif dengan objek String. Sekarang mari kita bersiar-siar singkat ke dalam sejarah Jawa dan cuba menjawab soalan: “mengapa?” Untuk melakukan ini, kami sendiri akan menulis versi ringkas kelas ArrayList. Senarai kami hanya boleh menambah data pada tatasusunan dalaman dan menerima data ini:
public class MyListClass {

   private Object[] data;
   private int count;

   public MyListClass() {
       this.data = new Object[10];
       this.count = 0;
   }

   public void add(Object o) {
       this.data[count] = o;
       count++;
   }

   public Object[] getData() {
       return data;
   }
}
Katakan kami mahu senarai kami menyimpan nombor sahaja Integer. Kami tidak mempunyai generik. Kami tidak boleh menyatakan secara eksplisit contoh semakan Integerdalam add(). Kemudian keseluruhan kelas kami hanya sesuai untuk Integer, dan kami perlu menulis kelas yang sama untuk semua jenis data yang wujud di dunia! Kami memutuskan untuk bergantung pada pengaturcara kami dan hanya meninggalkan komen dalam kod supaya mereka tidak menambah apa-apa yang tidak perlu di sana:
//use it ONLY with Integer data type
public void add(Object o) {
   this.data[count] = o;
   count++;
}
Salah seorang pengaturcara terlepas komen ini dan secara tidak sengaja cuba meletakkan nombor bercampur dengan rentetan dalam senarai, dan kemudian mengira jumlahnya:
public class Main {

   public static void main(String[] args) {

       MyListClass list = new MyListClass();
       list.add(100);
       list.add(200);
       list.add("Lolkek");
       list.add("Shalala");

       Integer sum1 = (Integer) list.getData()[0] + (Integer) list.getData()[1];
       System.out.println(sum1);

       Integer sum2 = (Integer) list.getData()[2] + (Integer) list.getData()[3];
       System.out.println(sum2);
   }
}
Output konsol: 300 Pengecualian dalam utas "utama" java.lang.ClassCastException: java.lang.String tidak boleh dihantar ke java.lang.Integer di Main.main(Main.java:14) Apakah yang paling teruk dalam situasi ini? Jauh sekali daripada menjadi pengaturcara yang lalai. Perkara yang paling teruk ialah kod yang salah berakhir di tempat penting dalam program kami dan berjaya disusun . Sekarang kita akan melihat ralat bukan pada peringkat pengekodan, tetapi hanya pada peringkat ujian (dan ini dalam kes terbaik!). Membaiki pepijat kemudian dalam pembangunan kos lebih tinggi - kedua-dua wang dan masa. Inilah kelebihan generik: kelas generik akan membenarkan pengaturcara yang tidak bernasib baik untuk mengesan ralat dengan segera. Kod itu tidak akan disusun!
import java.util.ArrayList;
import java.util.List;

public class Main {

   public static void main(String[] args) {

       List<Integer> myList1 = new ArrayList<>();

       myList1.add(100);
       myList1.add(100);
       myList1.add("Lolkek");//error!
       myList1.add("Shalala");//error!
   }
}
Pengaturcara akan segera "sedar" dan serta-merta membetulkan dirinya. Dengan cara ini, kami tidak perlu membuat kelas kami sendiri Listuntuk melihat ralat seperti ini. Hanya keluarkan kurungan jenis ( <Integer>) daripada ArrayList biasa!
import java.util.ArrayList;
import java.util.List;

public class Main {

   public static void main(String[] args) {

      List list = new ArrayList();

      list.add(100);
      list.add(200);
      list.add("Lolkek");
      list.add("Shalala");

       System.out.println((Integer) list.get(0) + (Integer) list.get(1));
       System.out.println((Integer) list.get(2) + (Integer) list.get(3));
   }
}
Output konsol: 300 Pengecualian dalam utas "utama" java.lang.ClassCastException: java.lang.String tidak boleh dihantar ke java.lang.Integer di Main.main(Main.java:16) Iaitu, walaupun menggunakan alatan “asli” Java, anda boleh membuat kesilapan ini dan mencipta koleksi yang tidak selamat. Walau bagaimanapun, jika kita menampal kod ini ke dalam IDEa, kita akan melihat amaran: " Panggilan tidak ditandai untuk menambah(E) sebagai ahli jenis mentah java.util.List " Ini memberitahu kita bahawa sesuatu mungkin tidak kena semasa menambahkan elemen pada koleksi tanpa generik bukan dengan cara ini. Tetapi apakah maksud frasa "jenis mentah"? Terjemahan literal akan agak tepat - " jenis mentah " atau " jenis kotor ". Raw typeialah kelas generik yang jenisnya telah dialih keluar. Dalam erti kata lain, List myList1ini adalah Raw type. Sebaliknya raw typeialah generic typekelas generik (juga dikenali sebagai kelas parameterized type), dicipta dengan betul, dengan spesifikasi jenis. Sebagai contoh, List<String> myList1. Anda mungkin mempunyai soalan: mengapa ia dibenarkan untuk digunakan raw types? Sebabnya mudah sahaja. Pencipta Java meninggalkan sokongan dalam bahasa raw typesagar tidak menimbulkan masalah keserasian. Pada masa Java 5.0 dikeluarkan (generik muncul buat kali pertama dalam versi ini), banyak kod telah ditulis menggunakan raw types. Oleh itu, kemungkinan ini masih wujud sehingga kini. Kami telah menyebut buku klasik Joshua Bloch "Effective Java" lebih daripada sekali dalam kuliah. Sebagai salah seorang pencipta bahasa, dia tidak mengabaikan topik penggunaan raw typesdan dalam buku itu generic types. Apakah generik dalam Java - 2Bab 23 buku ini mempunyai tajuk yang sangat fasih: "Jangan gunakan jenis mentah dalam kod baharu." Ini adalah sesuatu yang anda perlu ingat. Apabila menggunakan kelas generik, jangan sekali-kali mengubahnya generic typemenjadi raw type.

Kaedah Ditaip

Java membolehkan anda menaip kaedah individu, mencipta kaedah generik yang dipanggil. Mengapa kaedah sedemikian mudah? Pertama sekali, kerana ia membolehkan anda bekerja dengan pelbagai jenis parameter. Jika logik yang sama boleh digunakan dengan selamat untuk jenis yang berbeza, kaedah generik adalah penyelesaian yang hebat. Mari kita lihat contoh. Katakan kita mempunyai beberapa jenis senarai myList1. Kami mahu mengalih keluar semua nilai daripadanya, dan mengisi semua ruang kosong dengan nilai baharu. Inilah rupa kelas kami dengan kaedah generik:
public class TestClass {

   public static <T> void fill(List<T> list, T val) {
       for (int i = 0; i < list.size(); i++)
           list.set(i, val);
   }

   public static void main(String[] args) {

       List<String> strings = new ArrayList<>();
       strings.add("Старая строка 1");
       strings.add("Старая строка 2");
       strings.add("Старая строка 3");

       fill(strings, "Новая строка");

       System.out.println(strings);

       List<Integer> numbers = new ArrayList<>();
       numbers.add(1);
       numbers.add(2);
       numbers.add(3);

       fill(numbers, 888);
       System.out.println(numbers);
   }
}
Perhatikan sintaks, ia kelihatan agak luar biasa:
public static <T> void fill(List<T> list, T val)
Jenis pulangan didahului oleh <T>, yang menunjukkan kaedah generik. Dalam kes ini, kaedah mengambil 2 parameter sebagai input: senarai objek T dan satu lagi objek berasingan T. Dengan menggunakan <T>, penaipan kaedah dicapai: kami tidak boleh lulus senarai rentetan dan nombor di sana. Senarai rentetan dan rentetan, senarai nombor dan nombor, senarai objek kami Catdan objek lain Cat- itulah satu-satunya cara. Kaedah ini main()jelas menunjukkan bahawa kaedah ini fill()mudah berfungsi dengan pelbagai jenis data. Pertama, ia mengambil sebagai input senarai rentetan dan rentetan, dan kemudian senarai nombor dan nombor. Output konsol: [Newline, Newline, Newline] [888, 888, 888] Bayangkan jika fill()kami memerlukan logik kaedah untuk 30 kelas berbeza dan kami tidak mempunyai kaedah generik. Kami akan dipaksa untuk menulis kaedah yang sama 30 kali, hanya untuk jenis data yang berbeza! Tetapi terima kasih kepada kaedah generik, kami boleh menggunakan semula kod kami! :)

Kelas ditaip

Anda bukan sahaja boleh menggunakan kelas generik yang disediakan di Java, tetapi juga mencipta kelas anda sendiri! Berikut ialah contoh mudah:
public class Box<T> {

   private T t;

   public void set(T t) {
       this.t = t;
   }

   public T get() {
       return t;
   }

   public static void main(String[] args) {

       Box<String> stringBox = new Box<>();

       stringBox.set("Старая строка");
       System.out.println(stringBox.get());
       stringBox.set("Новая строка");

       System.out.println(stringBox.get());

       stringBox.set(12345);//ошибка компиляции!
   }
}
Kelas kami Box<T>(“kotak”) ditaip. Setelah menetapkan jenis data ( ) kepadanya semasa penciptaan <T>, kami tidak lagi dapat meletakkan objek jenis lain ke dalamnya. Ini boleh dilihat dalam contoh. Apabila mencipta, kami menyatakan bahawa objek kami akan berfungsi dengan rentetan:
Box<String> stringBox = new Box<>();
Dan apabila dalam baris terakhir kod kami cuba meletakkan nombor 12345 di dalam kotak, kami mendapat ralat kompilasi! Sama seperti itu, kami mencipta kelas generik kami sendiri! :) Ini mengakhiri kuliah kita pada hari ini. Tetapi kami tidak mengucapkan selamat tinggal kepada generik! Dalam kuliah seterusnya kita akan bercakap tentang ciri yang lebih maju, jadi jangan ucapkan selamat tinggal! ) Semoga berjaya dalam pelajaran! :)
Komen
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION