CodeGym /Blog Java /rawak /Generik di Jawa
John Squirrels
Tahap
San Francisco

Generik di Jawa

Diterbitkan dalam kumpulan
Hai! Kami akan bercakap tentang Java Generics. Saya mesti mengatakan bahawa anda akan belajar banyak! Bukan sahaja pelajaran ini, tetapi juga beberapa pelajaran seterusnya, akan ditumpukan kepada generik. Jadi, jika anda berminat dengan generik, hari ini anda bertuah: anda akan belajar banyak tentang ciri generik. Dan jika tidak, letak jawatan dan berehat! :) Ini adalah topik yang sangat penting, dan anda perlu mengetahuinya. Mari kita mulakan dengan yang mudah: "apa" dan "mengapa".

Apakah Java Generics?

Generik ialah jenis yang mempunyai parameter. Apabila membuat jenis generik, anda bukan sahaja menentukan jenis, tetapi juga jenis data yang akan berfungsi dengannya. Saya rasa contoh yang paling jelas telah terlintas di fikiran anda: ArrayList! Beginilah cara kami biasanya mencipta satu 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, ciri senarai ini ialah kami tidak boleh memasukkan segala-galanya ke dalamnya: ia berfungsi secara eksklusif dengan objek String . Sekarang mari kita ambil sedikit penyimpangan ke dalam sejarah Java dan cuba jawab soalan "mengapa?" Untuk melakukan ini, kami akan menulis versi ringkas kelas ArrayList kami sendiri. Senarai kami hanya tahu cara menambah data dan mendapatkan semula data daripada tatasusunan dalaman:

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 hanya menyimpan Integer s. Kami tidak menggunakan jenis generik. Kami tidak mahu memasukkan semakan "instanceof Integer " yang jelas dalam kaedah add() . Jika kami melakukannya, seluruh kelas kami hanya sesuai untuk Integer , dan kami perlu menulis kelas yang serupa untuk setiap jenis data lain di dunia! Kami akan bergantung pada pengaturcara kami, dan hanya tinggalkan komen dalam kod untuk memastikan mereka tidak menambah apa-apa yang kami tidak mahu:

// Use this class ONLY with the Integer data type
public void add(Object o) {
   this.data[count] = o;
   count++;
}
Salah seorang pengaturcara terlepas komen ini dan secara tidak sengaja meletakkan beberapa String dalam senarai nombor 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 
Exception in thread "main" java.lang.ClassCastException: java.lang.String cannot be cast to java.lang.Integer 
      at Main.main (Main.java:14)
Apakah bahagian yang paling teruk dalam situasi ini? Sudah tentu bukan kecuaian pengaturcara. Bahagian yang paling teruk ialah kod yang salah berakhir di tempat penting dalam program kami dan berjaya disusun. Sekarang kita akan menghadapi pepijat bukan semasa menulis kod, tetapi hanya semasa ujian (dan ini adalah senario kes terbaik!). Membaiki pepijat pada peringkat pembangunan kemudiannya memerlukan kos yang lebih tinggi — baik dari segi wang dan masa. Di sinilah generik memberi manfaat kepada kita: kelas generik membolehkan pengaturcara yang tidak bernasib baik mengesan ralat dengan segera. Program ini 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 segera menyedari kesilapannya dan serta-merta menjadi lebih baik. Dengan cara ini, kami tidak perlu mencipta kelas Senarai kami sendiri untuk melihat ralat seperti ini. Hanya keluarkan kurungan sudut dan taip ( <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 
Exception in thread "main" java.lang.ClassCastException: java.lang.String cannot be cast to java.lang.Integer 
     at Main.main(Main.java:16)
Dalam erti kata lain, walaupun menggunakan mekanisme "asli" Java, kita boleh membuat kesilapan seperti ini dan mencipta koleksi yang tidak selamat. Walau bagaimanapun, jika kami menampal kod ini ke dalam IDE, kami mendapat amaran: "Panggilan tidak ditandai untuk menambah(E) sebagai ahli jenis mentah java.util.List" Kami diberitahu bahawa sesuatu mungkin berlaku apabila menambah item kepada koleksi yang tidak mempunyai jenis generik. Tetapi apakah maksud frasa "jenis mentah"? Jenis mentah ialah kelas generik yang jenisnya telah dialih keluar. Dengan kata lain, List myList1 ialah jenis mentah . Lawan daripada jenis mentah ialah jenis generik — kelas generik dengan petunjuk jenis berparameter . Contohnya, List<String> myList1. Anda mungkin bertanya mengapa bahasa itu membenarkan penggunaan jenis mentah ? Sebabnya mudah sahaja. Pencipta Java meninggalkan sokongan untuk jenis mentah dalam bahasa untuk mengelakkan masalah keserasian. Pada masa Java 5.0 dikeluarkan (generik pertama kali muncul dalam versi ini), banyak kod telah ditulis menggunakan jenis mentah . Akibatnya, mekanisme ini masih disokong hari ini. Kami telah berulang kali menyebut buku klasik Joshua Bloch "Effective Java" dalam pelajaran. Sebagai salah seorang pencipta bahasa, dia tidak melangkau jenis mentah dan jenis generik dalam bukunya. Apakah generik di Jawa?  - 2Bab 23 buku itu mempunyai tajuk yang sangat fasih: "Jangan gunakan jenis mentah dalam kod baharu" Inilah yang anda perlu ingat. Apabila menggunakan kelas generik, jangan sekali-kali menukar jenis generik kepada jenis mentah .

Kaedah generik

Java membolehkan anda membuat parameter kaedah individu dengan mencipta kaedah generik yang dipanggil. Bagaimanakah kaedah sedemikian membantu? Di atas semua itu, mereka membantu kerana membenarkan anda bekerja dengan pelbagai jenis parameter kaedah. Jika logik yang sama boleh digunakan dengan selamat untuk jenis yang berbeza, kaedah generik boleh menjadi penyelesaian yang hebat. Pertimbangkan ini sebagai contoh yang sangat mudah: Katakan kita mempunyai beberapa senarai yang dipanggil myList1 . Kami mahu mengalih keluar semua nilai daripada senarai 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("Old String 1");
       strings.add("Old String 2");
       strings.add("Old String 3");

       fill(strings, "New String");

       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)
Kami menulis <T> sebelum jenis pulangan. Ini menunjukkan bahawa kita sedang berurusan dengan kaedah generik. Dalam kes ini, kaedah menerima 2 parameter sebagai input: senarai objek T dan objek T berasingan yang lain. Dengan menggunakan <T>, kami parameterkan jenis parameter kaedah: kami tidak boleh lulus dalam senarai Rentetan dan Integer. Senarai Rentetan dan Rentetan, senarai Integer dan Integer, senarai objek Cat kita sendiri dan objek Cat yang lain — itulah yang perlu kita lakukan. Kaedah main() menggambarkan bagaimana kaedah fill() boleh digunakan dengan mudah untuk berfungsi dengan pelbagai jenis data. Pertama, kami menggunakan kaedah dengan senarai Rentetan dan Rentetan sebagai input, dan kemudian dengan senarai Integer dan Integer. Output konsol:

[New String, New String, New String] [888, 888, 888]
Bayangkan jika kita tidak mempunyai kaedah generik dan memerlukan logik kaedah fill() untuk 30 kelas yang berbeza. Kami perlu menulis kaedah yang sama 30 kali untuk jenis data yang berbeza! Tetapi terima kasih kepada kaedah generik, kami boleh menggunakan semula kod kami! :)

Kelas generik

Anda tidak terhad kepada kelas generik yang disediakan dalam perpustakaan Java standard — anda boleh 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("Old String");
       System.out.println(stringBox.get());
       stringBox.set("New String");

       System.out.println(stringBox.get());
      
       stringBox.set(12345); // Compilation error!
   }
}
Kelas Box<T> kami ialah kelas generik. Sebaik sahaja kami menetapkan jenis data ( <T> ) semasa penciptaan, kami tidak lagi dapat meletakkan objek jenis lain di dalamnya. Ini boleh dilihat dalam contoh. Apabila mencipta objek kami, kami menunjukkan bahawa ia akan berfungsi dengan Strings:

Box<String> stringBox = new Box<>();
Dan dalam baris terakhir kod, apabila kami cuba meletakkan nombor 12345 di dalam kotak, kami mendapat ralat kompilasi! Semudah itu! Kami telah mencipta kelas generik kami sendiri! :) Dengan itu, pelajaran hari ini berakhir. Tetapi kami tidak mengucapkan selamat tinggal kepada generik! Dalam pelajaran seterusnya, kita akan bercakap tentang ciri yang lebih maju, jadi jangan pergi! ) Untuk mengukuhkan apa yang anda pelajari, kami cadangkan anda menonton pelajaran video daripada Kursus Java kami
Semoga berjaya dalam pelajaran anda! :)
Komen
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION