Halo! Hari ini kita akan berbicara tentang obat generik. Saya harus mengatakan bahwa Anda akan belajar banyak hal baru! Tidak hanya itu, beberapa kuliah berikutnya akan dikhususkan untuk obat generik.
Oleh karena itu, jika topik ini menarik bagi Anda, Anda beruntung: hari ini Anda akan belajar banyak tentang fitur obat generik. Nah, jika tidak, tenang dan rileks! :) Ini adalah topik yang sangat penting dan Anda perlu mengetahuinya. Mari kita mulai dengan pertanyaan sederhana: “apa” dan “mengapa”. Apa itu obat generik? Generik adalah tipe dengan parameter. Saat membuat generik, Anda tidak hanya menentukan tipenya, tetapi juga tipe data yang harus digunakan. Saya rasa contoh paling jelas sudah terlintas di benak Anda - ini adalah ArrayList! Begini cara kami biasanya membuatnya di program:
Bab 23 buku ini memiliki judul yang sangat mengesankan: “Jangan gunakan tipe mentah dalam kode baru.” Ini adalah sesuatu yang perlu Anda ingat. Saat menggunakan kelas generik, jangan pernah mengubahnya
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 duga, kekhasan dari daftar ini adalah bahwa tidak mungkin untuk "memasukkan" semuanya ke dalamnya: ia bekerja secara eksklusif dengan objek String
. Sekarang mari kita bertamasya singkat ke dalam sejarah Jawa dan mencoba menjawab pertanyaan: “mengapa?” Untuk melakukan ini, kami sendiri akan menulis versi sederhana dari kelas ArrayList. Daftar kami hanya dapat menambahkan data ke array internal 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;
}
}
Katakanlah kita ingin daftar kita hanya menyimpan angka Integer
. Kami tidak memiliki obat generik. Kami tidak dapat secara eksplisit menentukan o contoh check Integer
di add()
. Maka seluruh kelas kita hanya akan cocok untuk Integer
, dan kita harus menulis kelas yang sama untuk semua tipe data yang ada di dunia! Kami memutuskan untuk mengandalkan pemrogram kami dan hanya meninggalkan komentar di kode agar mereka tidak menambahkan sesuatu yang tidak perlu di sana:
//use it ONLY with Integer data type
public void add(Object o) {
this.data[count] = o;
count++;
}
Salah satu pemrogram melewatkan komentar ini dan secara tidak sengaja mencoba memasukkan angka-angka yang dicampur dengan string ke dalam daftar, dan kemudian menghitung 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 di thread "utama" java.lang.ClassCastException: java.lang.String tidak dapat dilemparkan ke java.lang.Integer di Main.main(Main.java:14) Apa hal terburuk dalam situasi ini? Jauh dari kecerobohan seorang programmer. Hal terburuknya adalah kode yang salah ditempatkan di tempat penting dalam program kita dan berhasil dikompilasi . Sekarang kita akan melihat kesalahan bukan pada tahap pengkodean, tetapi hanya pada tahap pengujian (dan ini yang terbaik!). Memperbaiki bug di kemudian hari dalam pengembangan membutuhkan biaya lebih banyak - baik uang maupun waktu. Inilah keuntungan dari obat generik: kelas generik akan memungkinkan pemrogram yang tidak beruntung untuk segera mendeteksi kesalahan. Kode tidak dapat dikompilasi!
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!
}
}
Pemrogram akan segera “sadar” dan langsung mengoreksi dirinya sendiri. Omong-omong, kami tidak perlu membuat kelas sendiri List
untuk melihat kesalahan seperti ini. Cukup hapus tanda kurung tipe ( <Integer>
) dari 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 di thread "main" java.lang.ClassCastException: java.lang.String tidak dapat dilemparkan ke java.lang.Integer di Main.main(Main.java:16) Artinya, bahkan menggunakan alat "asli" Java, Anda bisa membuat kesalahan ini dan membuat koleksi yang tidak aman. Namun, jika kita menempelkan kode ini ke IDEa, kita melihat peringatan: “ Panggilan tidak dicentang untuk menambahkan(E) sebagai anggota tipe mentah java.util.List ” Ini memberi tahu kita bahwa ada sesuatu yang salah saat menambahkan elemen ke a koleksi tanpa obat generik tidak seperti ini. Tapi apa arti ungkapan “tipe mentah”? Terjemahan literalnya akan cukup akurat - “ tipe mentah ” atau “ tipe kotor ”. Raw type
adalah kelas generik yang tipenya telah dihapus. Dengan kata lain, List myList1
ini adalah Raw type
. Kebalikannya raw type
adalah generic type
kelas generik (juga dikenal sebagai class parameterized type
), dibuat dengan benar, dengan spesifikasi tipe. Misalnya, List<String> myList1
. Anda mungkin memiliki pertanyaan: mengapa diperbolehkan menggunakan raw types
? Alasannya sederhana. Pembuat Java meninggalkan dukungan dalam bahasa tersebut raw types
agar tidak menimbulkan masalah kompatibilitas. Pada saat Java 5.0 dirilis (generik pertama kali muncul di versi ini), banyak kode telah ditulis menggunakan raw types
. Oleh karena itu, kemungkinan tersebut masih ada hingga saat ini. Kami telah menyebutkan buku klasik Joshua Bloch “Effective Java” lebih dari satu kali dalam kuliah. Sebagai salah satu pencipta bahasa tersebut, ia tidak mengabaikan topik penggunaan raw types
dan yang ada di dalam bukunya generic types
. ![Apa itu obat generik di Java - 2](https://cdn.javarush.com/images/article/a80f8588-2815-48ba-82f0-b0d770ad7f28/1024.jpeg)
generic type
menjadi raw type
.
Metode yang Diketik
Java memungkinkan Anda mengetik metode individual, menciptakan apa yang disebut metode generik. Mengapa metode seperti itu nyaman? Pertama-tama, karena mereka memungkinkan Anda bekerja dengan berbagai jenis parameter. Jika logika yang sama dapat diterapkan dengan aman ke tipe yang berbeda, metode generik adalah solusi yang bagus. Mari kita lihat sebuah contoh. Katakanlah kita mempunyai semacam daftarmyList1
. Kami ingin menghapus semua nilai darinya, dan mengisi semua ruang kosong dengan nilai baru. Inilah tampilan kelas kita dengan metode 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 sintaksnya, sepertinya agak tidak biasa:
public static <T> void fill(List<T> list, T val)
Tipe pengembalian diawali dengan <T>, yang menunjukkan metode generik. Dalam hal ini, metode ini mengambil 2 parameter sebagai masukan: daftar objek T dan objek terpisah lainnya T. Dengan menggunakan <T>, pengetikan metode tercapai: kita tidak dapat meneruskan daftar string dan angka ke sana. Daftar string dan string, daftar angka dan angka, daftar objek kita Cat
dan objek lainnya Cat
- itulah satu-satunya cara. Metode ini main()
dengan jelas menunjukkan bahwa metode ini fill()
dapat dengan mudah bekerja dengan berbagai jenis data. Pertama, dibutuhkan input daftar string dan string, lalu daftar angka dan angka. Output konsol: [Newline, Newline, Newline] [888, 888, 888] Bayangkan jika fill()
kita memerlukan logika metode untuk 30 kelas berbeda, dan kita tidak memiliki metode umum. Kami akan dipaksa untuk menulis metode yang sama sebanyak 30 kali, hanya untuk tipe data yang berbeda! Namun berkat metode umum, kita dapat menggunakan kembali kode kita! :)
Kelas yang diketik
Anda tidak hanya dapat menggunakan kelas generik yang disediakan di Java, tetapi juga membuatnya sendiri! Berikut ini contoh sederhananya: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 kita Box<T>
(“kotak”) diketik. Setelah menetapkan tipe data ( ) padanya selama pembuatan <T>
, kita tidak lagi dapat menempatkan objek tipe lain ke dalamnya. Hal ini dapat dilihat pada contoh. Saat membuat, kami menentukan bahwa objek kami akan bekerja dengan string:
Box<String> stringBox = new Box<>();
Dan ketika pada baris kode terakhir kita mencoba memasukkan angka 12345 ke dalam kotak, kita mendapatkan kesalahan kompilasi! Sama seperti itu, kami membuat kelas generik kami sendiri! :) Ini mengakhiri kuliah kita hari ini. Tapi kami tidak mengucapkan selamat tinggal pada obat generik! Pada kuliah berikutnya kita akan membahas tentang fitur-fitur yang lebih canggih, jadi jangan ucapkan selamat tinggal! ) Semoga sukses dalam studimu! :)
GO TO FULL VERSION