CodeGym/Java Blog/Acak/Wildcard dalam obat generik
John Squirrels
Level 41
San Francisco

Wildcard dalam obat generik

Dipublikasikan di grup Acak
anggota
Hai! Mari kita lanjutkan studi kita tentang obat generik. Anda telah memperoleh banyak pengetahuan tentang mereka dari pelajaran sebelumnya (tentang penggunaan varargs saat bekerja dengan obat generik dan tentang type erasure ), tetapi ada topik penting yang belum kami pertimbangkan — wildcard . Ini adalah fitur yang sangat penting dari obat generik. Sedemikian rupa sehingga kami telah mendedikasikan pelajaran terpisah untuk itu! Yang mengatakan, tidak ada yang rumit tentang wildcard. Anda akan segera melihatnya :) Wildcard dalam obat generik - 1Mari kita lihat sebuah contoh:
public class Main {

   public static void main(String[] args) {

       String str = new String("Test!");
       // No problem
       Object obj = str;

       List<String> strings = new ArrayList<String>();
       // Compilation error!
       List<Object> objects = strings;
   }
}
Apa yang terjadi di sini? Kami melihat dua situasi yang sangat mirip. Dalam kasus ini, kami melemparkan Stringobjek ke Objectobjek. Tidak ada masalah di sini — semuanya berjalan seperti yang diharapkan. Namun dalam situasi kedua, kompiler menghasilkan kesalahan. Tapi kita melakukan hal yang sama, bukan? Kali ini kita hanya menggunakan kumpulan dari beberapa objek. Tetapi mengapa kesalahan itu terjadi? Apa bedanya? Apakah kita mentransmisikan satu Stringobjek ke satu Objectatau 20 objek? Ada perbedaan penting antara objek dan kumpulan objek . Jika Bkelas adalah anak dari Akelas, maka Collection<B>bukan anak dari Collection<A>. Inilah mengapa kami tidak dapat mentransmisikan List<String>ke aList<Object>. Stringadalah anak dari Object, tetapi List<String>bukan anak dari List<Object>. Ini mungkin tidak tampak super intuitif. Mengapa pencipta bahasa membuatnya seperti ini? Bayangkan kompiler tidak memberi kita kesalahan:
List<String> strings = new ArrayList<String>();
List<Object> objects = strings;
Dalam hal ini, kita dapat, misalnya, melakukan hal berikut:
objects.add(new Object());
String s = strings.get(0);
Karena kompiler tidak memberi kami kesalahan apa pun dan mengizinkan kami membuat List<Object>referensi yang menunjuk ke strings, kami dapat menambahkan Objectobjek lama apa pun ke stringskoleksi! Dengan demikian, kami kehilangan jaminan bahwa koleksi kami hanya berisi Stringobjek yang ditentukan oleh argumen type dalam pemanggilan tipe umum . Dengan kata lain, kami telah kehilangan keunggulan utama dari obat generik — keamanan jenis. Dan karena kompiler tidak menghentikan kita melakukan ini, kita akan mendapatkan kesalahan hanya pada saat dijalankan, yang selalu jauh lebih buruk daripada kesalahan kompilasi. Untuk mencegah situasi seperti ini, kompiler memberi kami kesalahan:
// Compilation error
List<Object> objects = strings;
...dan mengingatkan kita bahwa List<String>bukan keturunan dari List<Object>. Ini adalah aturan ketat untuk obat generik, dan harus diingat saat bekerja dengannya. Ayo lanjutkan. Misalkan kita memiliki hierarki kelas kecil:
public class Animal {

   public void feed() {

       System.out.println("Animal.feed()");
   }
}

public class Pet extends Animal {

   public void call() {

       System.out.println("Pet.call()");
   }
}

public class Cat extends Pet {

   public void meow() {

       System.out.println("Cat.meow()");
   }
}
Hirarki diatapi oleh kelas Animal sederhana, yang diwarisi oleh Pet. Hewan peliharaan memiliki 2 subkelas: Anjing dan Kucing. Sekarang misalkan kita perlu membuat iterateAnimals()metode sederhana. Metode harus mengambil kumpulan hewan apa pun ( Animal, Pet, Cat, Dog), mengulangi semua elemen, dan menampilkan pesan di konsol selama setiap iterasi. Mari kita coba menulis metode seperti itu:
public static void iterateAnimals(Collection<Animal> animals) {

   for(Animal animal: animals) {

       System.out.println("Another iteration in the loop!");
   }
}
Sepertinya masalahnya sudah selesai! Namun, seperti yang baru-baru ini kita pelajari, List<Cat>, List<Dog>dan List<Pet>bukan keturunan dari List<Animal>! Ini berarti bahwa ketika kami mencoba memanggil iterateAnimals()metode dengan daftar kucing, kami mendapatkan kesalahan kompilasi:
import java.util.*;

public class Main3 {


   public static void iterateAnimals(Collection<Animal> animals) {

       for(Animal animal: animals) {

           System.out.println("Another iteration in the loop!");
       }
   }

   public static void main(String[] args) {


       List<Cat> cats = new ArrayList<>();
       cats.add(new Cat());
       cats.add(new Cat());
       cats.add(new Cat());
       cats.add(new Cat());

       // Compilation error!
       iterateAnimals(cats);
   }
}
Situasinya tidak terlihat bagus untuk kita! Apakah kita harus menulis metode terpisah untuk menghitung setiap jenis hewan? Sebenarnya, tidak, kami tidak :) Dan ternyata, wildcard membantu kami dalam hal ini! Kita dapat menyelesaikan masalah dengan satu metode sederhana menggunakan konstruk berikut:
public static void iterateAnimals(Collection<? extends Animal> animals) {

   for(Animal animal: animals) {

       System.out.println("Another iteration in the loop!");
   }
}
Ini adalah kartu pengganti. Lebih tepatnya, ini adalah yang pertama dari beberapa jenis wildcard. Ini dikenal sebagai wildcard batas atas dan diekspresikan oleh ? memanjang . Apa yang dikatakan konstruksi ini kepada kita? Ini berarti bahwa metode tersebut menerima kumpulan Animalobjek atau kumpulan objek dari kelas apa pun yang diturunkan dari Animal(? extends Animal). Dengan kata lain, metode ini dapat menerima koleksi Animal, Pet, Dog, atau Catobjek — tidak ada bedanya. Mari yakinkan diri kita sendiri bahwa ini berhasil:
public static void main(String[] args) {

   List<Animal> animals = new ArrayList<>();
   animals.add(new Animal());
   animals.add(new Animal());

   List<Pet> pets = new ArrayList<>();
   pets.add(new Pet());
   pets.add(new Pet());

   List<Cat> cats = new ArrayList<>();
   cats.add(new Cat());
   cats.add(new Cat());

   List<Dog> dogs = new ArrayList<>();
   dogs.add(new Dog());
   dogs.add(new Dog());

   iterateAnimals(animals);
   iterateAnimals(pets);
   iterateAnimals(cats);
   iterateAnimals(dogs);
}
Keluaran konsol:
Another iteration in the loop!
Another iteration in the loop!
Another iteration in the loop!
Another iteration in the loop!
Another iteration in the loop!
Another iteration in the loop!
Another iteration in the loop!
Another iteration in the loop!
Kami membuat total 4 koleksi dan 8 objek, dan tepat ada 8 entri di konsol. Semuanya bekerja dengan baik! :) Wildcard memungkinkan kami untuk dengan mudah menyesuaikan logika yang diperlukan yang terkait dengan tipe tertentu ke dalam satu metode. Kami menghilangkan kebutuhan untuk menulis metode terpisah untuk setiap jenis hewan. Bayangkan berapa banyak metode yang kita butuhkan jika aplikasi kita digunakan oleh kebun binatang atau kantor dokter hewan :) Tapi sekarang mari kita lihat situasi yang berbeda. Hierarki pewarisan kami tetap tidak berubah: kelas tingkat atas adalah Animal, dengan Petkelas tepat di bawah, dan kelas Catdan Dogdi tingkat berikutnya. Sekarang Anda perlu menulis ulang iterateAnimals()metode tersebut agar bekerja dengan semua jenis hewan, kecuali anjing . Artinya, harus Collection<Animal>menerimaCollection<Pet>atau Collection<Car>, tetapi seharusnya tidak bekerja dengan Collection<Dog>. Bagaimana kita bisa mencapai hal ini? Tampaknya kita kembali menghadapi kemungkinan menulis metode terpisah untuk setiap jenis :/ Bagaimana lagi kita menjelaskan kepada kompiler apa yang kita inginkan terjadi? Ini sebenarnya cukup sederhana! Sekali lagi, wildcard membantu kami di sini. Tapi kali ini kita akan menggunakan jenis wildcard lain — wildcard batas bawah , yang diekspresikan menggunakan super .
public static void iterateAnimals(Collection<? super Cat> animals) {

   for(int i = 0; i < animals.size(); i++) {

       System.out.println("Another iteration in the loop!");
   }
}
Di sini prinsipnya serupa. Konstruk <? super Cat>memberi tahu kompiler bahwa iterateAnimals()metode dapat menerima sebagai input kumpulan Catobjek atau leluhur kelas apa pun Catsebagai input. Dalam hal ini, Catclass, parent Pet, dan parent dari parent , Animal, semuanya cocok dengan deskripsi ini. Kelas Dogtidak cocok dengan batasan kami, jadi upaya untuk menggunakan metode dengan List<Dog>argumen akan menghasilkan kesalahan kompilasi:
public static void main(String[] args) {

   List<Animal> animals = new ArrayList<>();
   animals.add(new Animal());
   animals.add(new Animal());

   List<Pet> pets = new ArrayList<>();
   pets.add(new Pet());
   pets.add(new Pet());

   List<Cat> cats = new ArrayList<>();
   cats.add(new Cat());
   cats.add(new Cat());

   List<Dog> dogs = new ArrayList<>();
   dogs.add(new Dog());
   dogs.add(new Dog());

   iterateAnimals(animals);
   iterateAnimals(pets);
   iterateAnimals(cats);

   // Compilation error!
   iterateAnimals(dogs);
}
Kami telah memecahkan masalah kami, dan sekali lagi wildcard ternyata sangat berguna :) Dengan ini, pelajaran telah berakhir. Sekarang Anda melihat betapa pentingnya obat generik dalam studi Anda tentang Java — kami telah memiliki 4 pelajaran lengkap tentang obat generik! Tetapi sekarang Anda sangat ahli dalam topik tersebut dan Anda dapat membuktikan keahlian Anda dalam wawancara kerja :) Dan sekarang, saatnya untuk kembali ke tugas! Semoga sukses dalam studi Anda! :)
Komentar
  • Populer
  • Baru
  • Lama
Anda harus login untuk memberikan komentar
Halaman ini belum memiliki komentar