CodeGym /Java Blog /Acak /Menggunakan varargs saat bekerja dengan obat generik
John Squirrels
Level 41
San Francisco

Menggunakan varargs saat bekerja dengan obat generik

Dipublikasikan di grup Acak
Hai! Dalam pelajaran hari ini, kita akan melanjutkan mempelajari obat generik. Kebetulan, ini adalah topik besar, tetapi tidak ada yang menghindarinya — ini adalah bagian yang sangat penting dari bahasa :) Ketika Anda mempelajari dokumentasi Oracle tentang obat generik atau membaca tutorial online, Anda akan menemukan istilah non-reifiable type dan jenis yang dapat diperbaiki . Jenis yang dapat diperbaiki adalah jenis yang informasinya tersedia sepenuhnya saat runtime. Di Jawa, tipe seperti itu termasuk primitif, tipe mentah, dan tipe non-generik. Sebaliknya, tipe non-reifiable adalah tipe yang informasinya dihapus dan menjadi tidak dapat diakses saat runtime. Seperti yang terjadi, ini adalah obat generik — List<String>, List<Integer>, dll.

Omong-omong, apakah Anda ingat apa itu varargs?

Jika Anda lupa, ini adalah argumen dengan panjang variabel. Mereka berguna dalam situasi di mana kita tidak tahu berapa banyak argumen yang mungkin diteruskan ke metode kita. Misalnya, jika kita memiliki kelas kalkulator yang memiliki summetode. Metodenya sum()bisa menerima 2 angka, atau 3, atau 5, atau sebanyak yang Anda suka. Akan sangat aneh jika membebani sum()metode untuk setiap kemungkinan jumlah argumen. Sebagai gantinya, kita dapat 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
Ini menunjukkan kepada kita bahwa ada beberapa fitur penting saat menggunakan varargs yang dikombinasikan dengan obat generik. Mari kita lihat kode berikut:

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>(), // This is okay
               "Leonardo da Vinci",
               "Vasco de Gama"
       );

       // but here we get a warning
       addAll(new ArrayList<Pair<String, String>>(),
               new Pair<String, String>("Leonardo", "da Vinci"),
               new Pair<String, String>("Vasco", "de Gama")
       );
   }
}
Metode addAll()mengambil a List<E>dan sejumlah Eobjek sebagai input, lalu menambahkan semua objek ini ke daftar. Dalam main()metode ini, kami memanggil addAll()metode kami dua kali. Dalam kasus pertama, kami menambahkan dua string biasa ke file List. Semuanya teratur di sini. Dalam kasus kedua, kami menambahkan dua Pair<String, String>objek ke file List. Tapi di sini kami tiba-tiba menerima peringatan:

Unchecked generics array creation for varargs parameter
Maksudnya itu apa? Mengapa kita mendapat peringatan dan mengapa ada penyebutan array? Lagi pula, kode kita tidak memiliki array! Mari kita mulai dengan kasus kedua. Peringatan tersebut menyebutkan sebuah array karena kompiler mengubah argumen panjang-variabel (varargs) menjadi sebuah array. Dengan kata lain, tanda tangan dari addAll()metode kami adalah:

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)
Yaitu, dalam main()metodenya, kompiler 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") 
        } 
   ); 
}
Array Stringbaik-baik saja. Tapi Pair<String, String>array tidak. Masalahnya adalah itu Pair<String, String>adalah tipe yang tidak dapat diperbaiki. Selama kompilasi, semua informasi tentang argumen tipe (<String, String>) dihapus. Membuat array dengan tipe non-reifiable tidak diperbolehkan di Java . Anda dapat melihat ini jika Anda mencoba membuat larik Pair<String, String> secara manual

public static void main(String[] args) {

   // Compilation error Generic array creation
  Pair<String, String>[] array = new Pair<String, String>[10];
}
Alasannya jelas: keamanan jenis. Seperti yang akan Anda ingat, saat membuat array, Anda pasti perlu menentukan objek (atau primitif) mana yang akan disimpan oleh array.

int array[] = new int[10];
Dalam salah satu pelajaran kami sebelumnya, kami memeriksa penghapusan jenis secara mendetail. Dalam hal ini, penghapusan tipe menyebabkan kita kehilangan informasi yang Pairdipasangkan oleh objek <String, String>. Membuat array akan menjadi tidak aman. Saat menggunakan metode yang melibatkan vararg dan generik, pastikan untuk mengingat tentang penghapusan tipe dan cara kerjanya. Jika Anda benar-benar yakin dengan kode yang Anda tulis dan tahu bahwa itu tidak akan menimbulkan masalah, Anda dapat menonaktifkan peringatan terkait vararg 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 dapat terjadi saat menggunakan vararg dengan obat generik adalah polusi tumpukan. Menggunakan varargs saat bekerja dengan obat generik - 3Polusi tumpukan dapat terjadi dalam situasi berikut:

import java.util.ArrayList;
import java.util.List;

public class Main {

   static List<String> polluteHeap() {
       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 = polluteHeap();

       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
Sederhananya, polusi heap adalah saat objek bertipe Aharus berada di heap, tetapi objek bertipe Bberakhir di sana karena kesalahan terkait keamanan tipe. Dalam contoh kita, inilah yang terjadi. Pertama, kami membuat variabel mentah numbersdan menugaskan koleksi generik ( ArrayList<Number>) padanya. Kemudian kami menambahkan nomor 1ke koleksi.

List<String> strings = numbers;
Pada baris ini, kompiler mencoba memperingatkan kita tentang kemungkinan kesalahan dengan mengeluarkan peringatan " Penugasan yang tidak dicentang... ", tetapi kita mengabaikannya. Kami berakhir dengan variabel generik tipe List<String>yang menunjuk ke kumpulan tipe generik ArrayList<Number>. Jelas, situasi ini dapat menimbulkan masalah! Dan begitulah. Menggunakan variabel baru kami, kami menambahkan string ke koleksi. Kami sekarang memiliki tumpukan polusi — kami menambahkan nomor dan kemudian string ke koleksi parametrized. Kompiler memperingatkan kami, tetapi kami mengabaikan peringatannya. Hasilnya, kami ClassCastExceptionhanya mendapatkan saat program sedang berjalan. Jadi apa hubungannya ini dengan varargs? Menggunakan varargs dengan obat generik dapat dengan mudah menyebabkan polusi tumpukan. Berikut ini contoh sederhana:

import java.util.Arrays;
import java.util.List;

public class Main {

   static void polluteHeap(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");

       polluteHeap(cars1, cars2);
   }
}
Apa yang terjadi di sini? Karena penghapusan tipe, argumen panjang variabel kami

List<String>...stringsLists
menjadi larik daftar, yaitu List[]objek dengan tipe yang tidak diketahui (jangan lupa bahwa varargs berubah menjadi larik biasa selama kompilasi). Karena itu, kita dapat dengan mudah menugaskannya ke variabel Object[] arraydi baris pertama metode — tipe objek di daftar kita telah dihapus! Dan sekarang kami memiliki Object[]variabel, yang dapat kami tambahkan apa saja, karena semua objek di Java mewarisi Object! Pada awalnya, kami hanya memiliki sebuah array dari daftar string. Namun berkat penghapusan tipe dan penggunaan varargs kami, kami dapat dengan mudah menambahkan daftar angka, yang kami lakukan. Akibatnya, kami mencemari tumpukan dengan mencampur objek dari berbagai jenis. Hasilnya akan berbeda lagi ClassCastExceptionketika kita mencoba membaca sebuah string dari array. Keluaran konsol:

Exception in thread "main" java.lang.ClassCastException: java.lang.Integer cannot be cast to java.lang.String
Konsekuensi tak terduga seperti itu dapat disebabkan oleh penggunaan varargs, mekanisme yang tampaknya sederhana :) Dan dengan itu, pelajaran hari ini akan berakhir. Jangan lupa untuk menyelesaikan beberapa tugas, dan jika Anda punya waktu dan tenaga, pelajari beberapa bacaan tambahan. " Java Efektif " tidak akan terbaca sendiri! :) Sampai Lain waktu!
Komentar
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION