JavaRush /Java Blog /Random-ID /Obat generik untuk kucing
Viacheslav
Level 3

Obat generik untuk kucing

Dipublikasikan di grup Random-ID
Obat generik untuk kucing - 1

Perkenalan

Hari ini adalah hari yang menyenangkan untuk mengingat apa yang kita ketahui tentang Java. Menurut dokumen yang paling penting, yaitu. Spesifikasi Bahasa Java (JLS – Java Language Specifiaction), Java adalah bahasa yang diketik dengan kuat, seperti yang dijelaskan pada bab “ Bab 4. Jenis, Nilai, dan Variabel ”. Apa artinya ini? Katakanlah kita memiliki metode utama:
public static void main(String[] args) {
String text = "Hello world!";
System.out.println(text);
}
Pengetikan yang kuat memastikan bahwa ketika kode ini dikompilasi, kompiler akan memeriksa bahwa jika kita menentukan tipe variabel teks sebagai String, maka kita tidak mencoba menggunakannya di mana pun sebagai variabel tipe lain (misalnya, sebagai Integer) . Misalnya, jika kita mencoba menyimpan nilai alih-alih teks 2L(yaitu panjang, bukan String), kita akan mendapatkan kesalahan pada waktu kompilasi:

Main.java:3: error: incompatible types: long cannot be converted to String
String text = 2L;
Itu. Pengetikan yang kuat memungkinkan Anda memastikan bahwa operasi pada objek dilakukan hanya jika operasi tersebut sah untuk objek tersebut. Ini juga disebut keamanan tipe. Seperti yang dinyatakan dalam JLS, ada dua kategori tipe di Java: tipe primitif dan tipe referensi. Anda dapat mengingat tentang tipe primitif dari artikel ulasan: “ Tipe primitif di Java: Mereka tidak terlalu primitif .” Tipe referensi dapat diwakili oleh kelas, antarmuka, atau array. Dan hari ini kita akan tertarik pada tipe referensi. Dan mari kita mulai dengan array:
class Main {
  public static void main(String[] args) {
    String[] text = new String[5];
    text[0] = "Hello";
  }
}
Kode ini berjalan tanpa kesalahan. Seperti yang kita ketahui (misalnya dari " Oracle Java Tutorial: Arrays "), array adalah wadah yang menyimpan satu jenis data saja. Dalam hal ini - hanya garis. Mari kita coba menambahkan long ke array, bukan String:
text[1] = 4L;
Mari kita jalankan kode ini (misalnya, di Repl.it Online Java Compiler ) dan dapatkan kesalahan:
error: incompatible types: long cannot be converted to String
Array dan keamanan tipe bahasa tidak memungkinkan kami menyimpan ke dalam array apa yang tidak sesuai dengan tipenya. Ini adalah manifestasi dari keamanan tipe. Kami diberitahu: “Perbaiki kesalahannya, tetapi sampai saat itu saya tidak akan mengkompilasi kodenya.” Dan hal terpenting tentang hal ini adalah hal ini terjadi pada saat kompilasi, dan bukan pada saat program diluncurkan. Artinya, kita melihat kesalahan dengan segera, dan bukan “suatu hari nanti”. Dan karena kita sudah ingat tentang array, mari kita ingat juga tentang Java Collections Framework . Kami memiliki struktur berbeda di sana. Misalnya, daftar. Mari kita tulis ulang contohnya:
import java.util.*;
class Main {
  public static void main(String[] args) {
    List text = new ArrayList(5);
    text.add("Hello");
    text.add(4L);
    String test = text.get(0);
  }
}
Saat mengkompilasinya, kita akan menerima testkesalahan pada baris inisialisasi variabel:
incompatible types: Object cannot be converted to String
Dalam kasus kami, Daftar dapat menyimpan objek apa pun (yaitu objek bertipe Object). Oleh karena itu, penyusun mengatakan bahwa ia tidak dapat memikul beban tanggung jawab seperti itu. Oleh karena itu, kita perlu secara eksplisit menentukan tipe yang akan kita dapatkan dari daftar:
String test = (String) text.get(0);
Indikasi ini disebut konversi tipe atau pengecoran tipe. Dan semuanya akan berfungsi dengan baik sekarang sampai kita mencoba mendapatkan elemen di indeks 1, karena itu bertipe Panjang. Dan kita akan mendapatkan kesalahan yang wajar, tetapi saat program sedang berjalan (di Runtime):

type conversion, typecasting
Exception in thread "main" java.lang.ClassCastException: java.lang.Long cannot be cast to java.lang.String
Seperti yang bisa kita lihat, ada beberapa kelemahan penting di sini. Pertama, kita dipaksa untuk “mentransmisikan” nilai yang diperoleh dari daftar ke kelas String. Setuju, ini jelek. Kedua, jika terjadi kesalahan, kita hanya akan melihatnya saat program dijalankan. Jika kode kami lebih kompleks, kami mungkin tidak langsung mendeteksi kesalahan seperti itu. Dan pengembang mulai memikirkan cara membuat pekerjaan dalam situasi seperti itu lebih mudah dan kodenya lebih jelas. Dan mereka lahir - Generik.
Obat generik untuk kucing - 2

Generik

Jadi, obat generik. Apa itu? Generik adalah cara khusus untuk mendeskripsikan tipe yang digunakan, yang dapat digunakan oleh kompiler kode dalam pekerjaannya untuk memastikan keamanan tipe. Ini terlihat seperti ini:
Obat generik untuk kucing - 3
Berikut contoh singkat dan penjelasannya:
import java.util.*;
class Main {
  public static void main(String[] args) {
    List<String> text = new ArrayList<String>(5);
    text.add("Hello");
    text.add(4L);
    String test = text.get(1);
  }
}
Dalam contoh ini, kita mengatakan bahwa kita tidak hanya memiliki List, tetapi List, yang HANYA berfungsi dengan objek bertipe String. Dan tidak ada yang lain. Apa yang baru saja ditunjukkan dalam tanda kurung, kita dapat menyimpannya. "Kurung" seperti itu disebut "kurung sudut", yaitu. kurung sudut. Kompiler akan dengan senang hati memeriksa kami apakah kami membuat kesalahan saat bekerja dengan daftar string (daftar tersebut diberi nama teks). Kompiler akan melihat bahwa kita dengan berani mencoba memasukkan Long ke dalam daftar String. Dan pada waktu kompilasi akan memberikan error:
error: no suitable method found for add(long)
Anda mungkin ingat bahwa String adalah turunan dari CharSequence. Dan putuskan untuk melakukan sesuatu seperti:
public static void main(String[] args) {
	ArrayList<CharSequence> text = new ArrayList<String>(5);
	text.add("Hello");
	String test = text.get(0);
}
Tapi ini tidak mungkin dan kita akan mendapatkan error: error: incompatible types: ArrayList<String> cannot be converted to ArrayList<CharSequence> Tampaknya aneh, karena. baris CharSequence sec = "test";tidak mengandung kesalahan. Mari kita cari tahu. Mereka mengatakan tentang perilaku ini: “Generik tidak berubah.” Apa itu "invarian"? Saya suka bagaimana hal ini dikatakan di Wikipedia dalam artikel “ Kovariansi dan kontravarian ”:
Obat generik untuk kucing - 4
Jadi, Invarian adalah tidak adanya pewarisan antar tipe turunan. Jika Cat merupakan subtipe dari Hewan, maka Set<Cats> bukan merupakan subtipe dari Set<Animals> dan Set<Animals> bukan merupakan subtipe dari Set<Cats>. Omong-omong, perlu dikatakan bahwa sejak Java SE 7, apa yang disebut " Operator Berlian " muncul. Karena dua tanda kurung siku <> itu seperti berlian. Ini memungkinkan kami menggunakan obat generik seperti ini:
public static void main(String[] args) {
  List<String> lines = new ArrayList<>();
  lines.add("Hello world!");
  System.out.println(lines);
}
Berdasarkan kode ini, kompiler memahami bahwa jika kita menunjukkan di sisi kiri bahwa itu Listakan berisi objek bertipe String, maka di sisi kanan yang kita maksud adalah kita ingin menyimpan linesArrayList baru ke dalam variabel, yang juga akan menyimpan objek. dari jenis yang ditentukan di sisi kiri. Jadi kompiler dari sisi kiri memahami atau menyimpulkan tipe untuk sisi kanan. Inilah sebabnya mengapa perilaku ini disebut type inference atau "Type Inference" dalam bahasa Inggris. Hal menarik lainnya yang perlu diperhatikan adalah Tipe RAW atau “tipe mentah”. Karena Obat generik tidak selalu ada, dan Java mencoba mempertahankan kompatibilitas ke belakang bila memungkinkan, kemudian obat generik dipaksa untuk bekerja dengan kode di mana tidak ada obat generik yang ditentukan. Mari kita lihat contohnya:
List<CharSequence> lines = new ArrayList<String>();
Seperti yang kita ingat, baris seperti itu tidak dapat dikompilasi karena invarian obat generik.
List<Object> lines = new ArrayList<String>();
Dan yang ini juga tidak dapat dikompilasi, karena alasan yang sama.
List lines = new ArrayList<String>();
List<String> lines2 = new ArrayList();
Baris seperti itu akan dikompilasi dan akan berfungsi. Di sanalah Tipe Mentah digunakan, mis. tipe yang tidak ditentukan. Sekali lagi, perlu diperhatikan bahwa Tipe Mentah TIDAK BOLEH digunakan dalam kode modern.
Obat generik untuk kucing - 5

Kelas yang diketik

Jadi, kelas yang diketik. Mari kita lihat bagaimana kita bisa menulis kelas kita yang diketik. Misalnya, kami memiliki hierarki kelas:
public static abstract class Animal {
  public abstract void voice();
}

public static class Cat extends Animal {
  public void voice(){
    System.out.println("Meow meow");
  }
}

public static class Dog extends Animal {
  public void voice(){
    System.out.println("Woof woof");
  }
}
Kami ingin membuat kelas yang mengimplementasikan wadah hewan. Dimungkinkan untuk menulis kelas yang berisi file Animal. Ini sederhana, bisa dimengerti, TAPI... mencampurkan anjing dan kucing itu buruk, mereka tidak berteman satu sama lain. Selain itu, jika seseorang menerima wadah seperti itu, dia mungkin secara keliru melemparkan kucing dari wadah tersebut ke dalam sekawanan anjing... dan ini tidak akan membawa kebaikan. Dan di sini obat generik akan membantu kita. Misalnya, mari kita tulis implementasinya seperti ini:
public static class Box<T> {
  List<T> slots = new ArrayList<>();
  public List<T> getSlots() {
    return slots;
  }
}
Kelas kita akan bekerja dengan objek bertipe yang ditentukan oleh generik bernama T. Ini adalah sejenis alias. Karena Generik ditentukan dalam nama kelas, kemudian kita akan menerimanya ketika mendeklarasikan kelas:
public static void main(String[] args) {
  Box<Cat> catBox = new Box<>();
  Cat murzik = new Cat();
  catBox.getSlots().add(murzik);
}
Seperti yang bisa kita lihat, kami menunjukkan bahwa kami memiliki Box, yang hanya berfungsi dengan Cat. Kompiler menyadari bahwa catBoxalih-alih menggunakan generik, TAnda perlu mengganti tipe Catdi mana pun nama generik ditentukan T:
Obat generik untuk kucing - 6
Itu. berkat Box<Cat>kompiler ia memahami apa slotsyang seharusnya terjadi List<Cat>. Sebab Box<Dog>didalamnya akan ada slots, yang berisi List<Dog>. Ada beberapa generik dalam deklarasi tipe, misalnya:
public static class Box<T, V> {
Nama generiknya bisa apa saja, meskipun disarankan untuk mematuhi beberapa aturan tak tertulis - "Konvensi Penamaan Parameter Tipe": Tipe elemen - E, tipe kunci - K, tipe angka - N, T - untuk tipe, V - untuk tipe nilai . Ngomong-ngomong, ingat kami mengatakan bahwa obat generik itu invarian, yaitu. jangan pertahankan hierarki warisan. Faktanya, kita bisa mempengaruhi hal ini. Artinya, kita mempunyai kesempatan untuk membuat COvarian generik, yaitu. menjaga warisan dalam urutan yang sama. Perilaku ini disebut "Tipe Terikat", yaitu. tipe terbatas. Misalnya, kelas kita Boxbisa berisi semua hewan, maka kita akan mendeklarasikan generik seperti ini:
public static class Box<T extends Animal> {
Artinya, kita menetapkan batas atas pada class Animal. Kita juga dapat menentukan beberapa jenis setelah kata kunci extends. Ini berarti bahwa tipe yang akan kita gunakan harus merupakan turunan dari suatu kelas dan pada saat yang sama mengimplementasikan beberapa antarmuka. Misalnya:
public static class Box<T extends Animal & Comparable> {
Dalam hal ini, jika kita mencoba memasukkan Boxsesuatu yang bukan merupakan pewaris Animaldan tidak diimplementasikan Comparable, maka selama kompilasi kita akan menerima kesalahan:
error: type argument Cat is not within bounds of type-variable T
Obat generik untuk kucing - 7

Metode Pengetikan

Generik digunakan tidak hanya dalam tipe, tetapi juga dalam metode individual. Penerapan metodenya dapat dilihat pada tutorial resmi: " Metode Generik ".

Latar belakang:

Obat generik untuk kucing - 8
Mari kita lihat gambar ini. Seperti yang Anda lihat, kompiler melihat tanda tangan metode dan melihat bahwa kita mengambil beberapa kelas yang tidak terdefinisi sebagai masukan. Itu tidak menentukan dengan tanda tangan bahwa kita mengembalikan beberapa jenis objek, mis. Obyek. Oleh karena itu, jika kita ingin membuat, katakanlah, ArrayList, kita perlu melakukan ini:
ArrayList<String> object = (ArrayList<String>) createObject(ArrayList.class);
Anda harus secara eksplisit menulis bahwa outputnya adalah ArrayList, yang jelek dan menambah kemungkinan membuat kesalahan. Misalnya, kita dapat menulis omong kosong seperti itu dan itu akan dikompilasi:
ArrayList object = (ArrayList) createObject(LinkedList.class);
Bisakah kami membantu kompiler? Ya, obat generik memungkinkan kita melakukan ini. Mari kita lihat contoh yang sama:
Obat generik untuk kucing - 9
Kemudian, kita dapat membuat objek seperti ini:
ArrayList<String> object = createObject(ArrayList.class);
Obat generik untuk kucing - 10

Kartu Liar

Menurut Tutorial Oracle tentang Generik, khususnya bagian " Wildcard ", kita dapat mendeskripsikan "tipe tidak diketahui" dengan tanda tanya, yang disebut tanda tanya. Wildcard adalah alat yang berguna untuk mengurangi beberapa keterbatasan obat generik. Misalnya, seperti yang telah kita bahas sebelumnya, obat generik bersifat invarian. Artinya meskipun semua kelas merupakan turunan (subtipe) dari tipe Object, namun ia List<любой тип>bukan subtipe List<Object>. TAPI, List<любой тип>itu adalah subtipe List<?>. Jadi kita bisa menulis kode berikut:
public static void printList(List<?> list) {
  for (Object elem: list) {
    System.out.print(elem + " ");
  }
  System.out.println();
}
Seperti obat generik biasa (yaitu tanpa menggunakan wildcard), obat generik dengan wildcard dapat dibatasi. Wildcard batas atas tampak familier:
public static void printCatList(List<? extends Cat> list) {
  for (Cat cat: list) {
    System.out.print(cat + " ");
  }
  System.out.println();
}
Namun Anda juga dapat membatasinya dengan wildcard batas bawah:
public static void printCatList(List<? super Cat> list) {
Dengan demikian, metode ini akan mulai menerima semua kucing, serta hierarki yang lebih tinggi (hingga Object).
Obat generik untuk kucing - 11

Ketik Penghapusan

Berbicara tentang obat generik, ada baiknya mengetahui tentang “Type Erasing”. Faktanya, penghapusan tipe adalah tentang fakta bahwa obat generik adalah informasi untuk kompiler. Selama eksekusi program, tidak ada lagi informasi tentang generik, ini disebut “menghapus”. Penghapusan ini mengakibatkan tipe generik digantikan oleh tipe spesifik. Jika generik tidak memiliki batasan, maka tipe Object akan diganti. Jika batasnya ditentukan (misalnya <T extends Comparable>), maka akan diganti. Berikut ini contoh dari Tutorial Oracle: " Penghapusan Tipe Generik ":
Obat generik untuk kucing - 12
Seperti disebutkan di atas, dalam contoh ini generik Tdihapus hingga batasnya, mis. sebelum Comparable.
Obat generik untuk kucing - 13

Kesimpulan

Generik adalah topik yang sangat menarik. Saya harap topik ini menarik bagi Anda. Ringkasnya, kita dapat mengatakan bahwa generik adalah alat luar biasa yang diterima pengembang untuk memberi kompiler informasi tambahan guna memastikan keamanan tipe di satu sisi dan fleksibilitas di sisi lain. Dan jika Anda tertarik, saya sarankan Anda memeriksa sumber daya yang saya suka: #Viacheslav
Komentar
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION