JavaRush /Java Blog /Random-ID /Menggunakan varargs saat bekerja dengan obat generik

Menggunakan varargs saat bekerja dengan obat generik

Dipublikasikan di grup Random-ID
Halo! Dalam pelajaran hari ini kita akan terus mempelajari obat generik. Kebetulan ini adalah topik besar, tetapi tidak ada tempat untuk dituju - ini adalah bagian yang sangat penting dari bahasa ini :) Saat Anda mempelajari dokumentasi Oracle tentang obat generik atau membaca panduan di Internet, Anda akan menemukan istilah-istilah tersebut Tipe yang Tidak Dapat Direifikasi dan Tipe yang Dapat Direifikasi . Kata macam apa yang dimaksud dengan “Dapat Dikembalikan”? Meskipun semuanya baik-baik saja dengan bahasa Inggris, kecil kemungkinan Anda akan menemukannya. Ayo coba terjemahkan! Menggunakan varargs saat bekerja dengan obat generik - 2
*terima kasih Google, Anda banyak membantu -_-*
Tipe yang dapat direifikasi adalah tipe yang informasinya tersedia sepenuhnya saat runtime. Dalam bahasa Java, ini termasuk tipe primitif, tipe mentah, dan tipe non-generik. Sebaliknya, Tipe yang Tidak Dapat Direifikasi adalah tipe yang informasinya dihapus dan tidak tersedia saat runtime. Ini hanya obat generik - List<String> , List<Integer> , dll.

Ngomong-ngomong, apakah kamu ingat apa itu vararg ?

Jika Anda lupa, ini adalah argumen dengan panjang variabel. Mereka berguna dalam situasi di mana kita tidak tahu persis berapa banyak argumen yang dapat diteruskan ke metode kita. Misalnya, jika kita mempunyai kelas kalkulator dan mempunyai metode sum. sum()Anda dapat memasukkan 2 angka, 3, 5, atau sebanyak yang Anda suka ke metode ini . Akan sangat aneh jika membebani metode ini setiap saat sum()untuk mempertimbangkan semua opsi yang memungkinkan. Sebagai gantinya kita bisa melakukan ini:
public class SimpleCalculator {

   public static int sum(int...numbers) {

       int result = 0;

       for(int i : numbers) {

           result += i;
       }

       return result;
   }

   public static void main(String[] args) {

       System.out.println(sum(1,2,3,4,5));
       System.out.println(sum(2,9));
   }
}
Keluaran konsol:

15
11
Jadi, bila digunakan varargsbersamaan dengan obat generik, ada beberapa fitur penting. Mari kita lihat kode ini:
import javafx.util.Pair;
import java.util.ArrayList;
import java.util.List;

public class Main {

   public static <E> void addAll(List<E> list, E... array) {

       for (E element : array) {
           list.add(element);
       }
   }

   public static void main(String[] args) {
       addAll(new ArrayList<String>(),  //  здесь все нормально
               "Leonardo da Vinci",
               "Vasco de Gama"
       );

       // а здесь мы получаем предупреждение
       addAll(new ArrayList<Pair<String, String>>(),
               new Pair<String, String>("Leonardo", "da Vinci"),
               new Pair<String, String>("Vasco", "de Gama")
       );
   }
}
Metode ini mengambil daftar dan sejumlah objek addAll()sebagai input , lalu menambahkan semua objek tersebut ke dalam daftar. Dalam metode ini kami memanggil metode kami dua kali . Pertama kali kita menambahkan dua baris reguler. Semua baik-baik saja disini. Kedua kalinya kita menambahkan dua objek . Dan di sini kita tiba-tiba mendapat peringatan: List<E>Emain()addAll()ListListPair<String, String>

Unchecked generics array creation for varargs parameter
Apa artinya? Mengapa kita menerima peringatan dan apa hubungannya dengan itu array? Array- ini adalah array, dan tidak ada array dalam kode kita! Mari kita mulai dengan yang kedua. Peringatan tersebut menyebutkan sebuah array karena kompiler mengubah argumen panjang variabel (varargs) menjadi sebuah array. Dengan kata lain, tanda tangan dari metode kami adalah addAll():
public static <E> void addAll(List<E> list, E... array)
Ini sebenarnya terlihat seperti ini:
public static <E> void addAll(List<E> list, E[] array)
Artinya, dalam metode ini main(), kompiler akan mengubah kode kita menjadi ini:
public static void main(String[] args) {
   addAll(new ArrayList<String>(),
      new String[] {
        "Leonardo da Vinci",
        "Vasco de Gama"
      }
   );
   addAll(new ArrayList<Pair<String,String>>(),
        new Pair<String,String>[] {
            new Pair<String,String>("Leonardo","da Vinci"),
            new Pair<String,String>("Vasco","de Gama")
        }
   );
}
Semuanya baik-baik saja dengan array String. Tapi dengan array Pair<String, String>- tidak. Faktanya adalah bahwa Pair<String, String>ini adalah Tipe yang Tidak Dapat Direifikasi. Selama kompilasi, semua informasi tentang tipe parameter (<String, String>) akan dihapus. Membuat array dari Tipe Non-Refiable tidak diperbolehkan di Java . Anda dapat memverifikasi ini jika Anda mencoba membuat array Pair<String, String> secara manual
public static void main(String[] args) {

   //  ошибка компиляции! Generic array creation
  Pair<String, String>[] array = new Pair<String, String>[10];
}
Alasannya jelas - seperti keamanan. Seperti yang Anda ingat, saat membuat array, Anda harus menunjukkan objek (atau primitif) mana yang akan disimpan oleh array ini.
int array[] = new int[10];
Dalam salah satu pelajaran sebelumnya, kita memeriksa secara rinci mekanisme penghapusan tipe. Jadi, dalam kasus ini, sebagai akibat dari penghapusan tipe, kita kehilangan informasi Pairpasangan yang disimpan di objek kita <String, String>. Membuat array tidak aman. Saat menggunakan metode dengan varargsdan generik, pastikan untuk mengingat tentang penghapusan tipe dan cara kerjanya. Jika Anda benar-benar yakin dengan kode yang Anda tulis, dan Anda tahu bahwa itu tidak akan menimbulkan masalah, Anda dapat menonaktifkan varargsperingatan yang terkait dengannya menggunakan anotasi@SafeVarargs
@SafeVarargs
public static <E> void addAll(List<E> list, E... array) {

   for (E element : array) {
       list.add(element);
   }
}
Jika Anda menambahkan anotasi ini ke metode Anda, peringatan yang kami temui sebelumnya tidak akan muncul. Masalah lain yang mungkin terjadi ketika menggunakan varargsobat generik secara bersamaan adalah polusi tumpukan. Menggunakan varargs saat bekerja dengan obat generik - 4Kontaminasi dapat terjadi dalam situasi berikut:
import java.util.ArrayList;
import java.util.List;

public class Main {

   static List<String> makeHeapPollution() {
       List numbers = new ArrayList<Number>();
       numbers.add(1);
       List<String> strings = numbers;
       strings.add("");
       return strings;
   }

   public static void main(String[] args) {

       List<String> stringsWithHeapPollution = makeHeapPollution();

       System.out.println(stringsWithHeapPollution.get(0));
   }
}
Keluaran konsol:

Exception in thread "main" java.lang.ClassCastException: java.lang.Integer cannot be cast to java.lang.String
Secara sederhana, polusi heap adalah situasi di mana objek bertipe 1 seharusnya berada di heap А, namun objek bertipe berakhir di sana B, karena kesalahan keamanan tipe. Dalam contoh kita, inilah yang terjadi. Pertama kita membuat variabel Raw numbersdan menetapkannya sebagai koleksi generik ArrayList<Number>. Setelah itu kami menambahkan nomor di sana 1.
List<String> strings = numbers;
Pada baris ini, kompiler mencoba memperingatkan kami tentang kemungkinan kesalahan dengan mengeluarkan peringatan “ Penugasan tidak dicentang... ”, tapi kami mengabaikannya. Hasilnya, kita memiliki variabel generik bertipe List<String>, yang menunjuk ke kumpulan generik bertipe ArrayList<Number>. Situasi ini jelas dapat menimbulkan masalah! Inilah yang terjadi. Dengan menggunakan variabel baru, kami menambahkan string ke koleksi. Tumpukan itu tercemar - pertama-tama kami menambahkan angka dan kemudian string ke koleksi yang diketik. Kompiler memperingatkan kami, tapi kami mengabaikan peringatannya, ClassCastExceptionhanya menerima hasil saat program sedang berjalan. Apa hubungannya dengan itu varargs? Penggunaan varargsdengan obat generik dapat dengan mudah menyebabkan polusi tumpukan. Berikut ini contoh sederhananya:
import java.util.Arrays;
import java.util.List;

public class Main {

   static void makeHeapPollution(List<String>... stringsLists) {
       Object[] array = stringsLists;
       List<Integer> numbersList = Arrays.asList(66,22,44,12);

       array[0] = numbersList;
       String str = stringsLists[0].get(0);
   }

   public static void main(String[] args) {

       List<String> cars1 = Arrays.asList("Ford", "Fiat", "Kia");
       List<String> cars2 = Arrays.asList("Ferrari", "Bugatti", "Zaporozhets");

       makeHeapPollution(cars1, cars2);
   }
}
Apa yang terjadi di sini? Karena penghapusan jenis, lembar parameter kami (kami akan menyebutnya “sheet” dan bukan “daftar” untuk kenyamanan) adalah -
List<String>...stringsLists
- akan berubah menjadi array lembar - List[]dengan tipe yang tidak diketahui (jangan lupa bahwa varargs berubah menjadi array biasa sebagai hasil kompilasi). Oleh karena itu, kita dapat dengan mudah membuat penugasan ke suatu variabel Object[] arraydi baris pertama metode ini - tipenya telah dihapus dari sheet kita! Dan sekarang kita memiliki variabel bertipe Object[], dimana kita dapat menambahkan apa saja - semua objek di Java diwarisi dari Object! Saat ini kami hanya memiliki serangkaian lembar string. Namun berkat penggunaan varargsdan penghapusan tipe, kita dapat dengan mudah menambahkan lembaran angka ke dalamnya, itulah yang kita lakukan. Akibatnya, kami mencemari tumpukan dengan mencampurkan berbagai jenis objek. Hasilnya akan menjadi pengecualian yang sama ClassCastExceptionketika mencoba membaca string dari array. Keluaran konsol:

Exception in thread "main" java.lang.ClassCastException: java.lang.Integer cannot be cast to java.lang.String
Inilah konsekuensi tak terduga yang mungkin timbul dari penggunaan mekanisme yang tampaknya sederhana varargs:) Dan di sinilah kuliah kita hari ini berakhir. Jangan lupa untuk menyelesaikan beberapa masalah, dan jika Anda memiliki waktu dan tenaga tersisa, pelajari literatur tambahan. “ Java yang Efektif ” tidak akan terbaca sendiri! :) Sampai jumpa!
Komentar
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION