CodeGym /Java Blogu /Rastgele /Jeneriklerle çalışırken varargs kullanmak
John Squirrels
Seviye
San Francisco

Jeneriklerle çalışırken varargs kullanmak

grupta yayınlandı
MERHABA! Bugünün dersinde jenerikleri incelemeye devam edeceğiz. Olduğu gibi, bu büyük bir konu ama bundan kaçış yok — dilin son derece önemli bir parçası :) Jeneriklerle ilgili Oracle belgelerini incelediğinizde veya çevrimiçi eğitimleri okuduğunuzda, somutlaştırılamayan türler ve terimleriyle karşılaşacaksınız. şeyleştirilebilir tipler Gerçekleştirilebilir bir tür, çalışma zamanında bilgilerinin tamamen mevcut olduğu bir türdür. Java'da bu tür türler, ilkelleri, ham türleri ve genel olmayan türleri içerir. Buna karşılık, somutlaştırılamayan türler, bilgileri çalışma zamanında silinen ve erişilemez hale gelen türlerdir. Olduğu gibi, bunlar jeneriktir - List<String>, List<Integer>, vb.

Bu arada, varargs'ın ne olduğunu hatırlıyor musun?

Unuttuysanız, bu değişken uzunluklu bir argümandır. Metodumuza kaç argüman aktarılabileceğini bilmediğimiz durumlarda faydalıdırlar. Örneğin, bir yöntemi olan bir hesap makinesi sınıfımız varsa sum. Yöntem sum(), 2 veya 3 veya 5 veya istediğiniz kadar sayı alabilir. sum()Her olası bağımsız değişken sayısı için yöntemi aşırı yüklemek çok garip olurdu . Bunun yerine şunu yapabiliriz:

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));
   }
}
Konsol çıktısı:

15
11
Bu bize vararg'ları jeneriklerle birlikte kullanırken bazı önemli özelliklerin olduğunu gösterir. Aşağıdaki koda bakalım:

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")
       );
   }
}
Yöntem , a ve herhangi bir sayıda nesneyi addAll()girdi olarak alır ve ardından tüm bu nesneleri listeye ekler. Yöntemde , yöntemimizi iki kez çağırıyoruz . İlk durumda, . Burada her şey yolunda. İkinci durumda , . Ancak burada beklenmedik bir şekilde bir uyarı alıyoruz: List<E>Emain()addAll()ListPair<String, String>List

Unchecked generics array creation for varargs parameter
Bu ne anlama gelir? Neden bir uyarı alıyoruz ve neden bir uyarı var array? Sonuçta, kodumuzda array! İkinci durumla başlayalım. Uyarı, bir diziden bahsediyor çünkü derleyici değişken uzunluklu bağımsız değişkeni (varargs) bir diziye dönüştürüyor. Başka bir deyişle, yöntemimizin imzası addAll():

public static <E> void addAll(List<E> list, E... array)
Aslında şöyle görünüyor:

public static <E> void addAll(List<E> list, E[] array)
Yani, yöntemde main()derleyici kodumuzu şuna dönüştürür:

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") 
        } 
   ); 
}
Bir Stringdizi gayet iyi. Ancak bir Pair<String, String>dizi değildir. Sorun şu ki, Pair<String, String>şeyleştirilebilir olmayan bir tür. Derleme sırasında, tür bağımsız değişkenleriyle (<String, String>) ilgili tüm bilgiler silinir. Tanımlanamaz türde diziler oluşturmaya Java'da izin verilmez . El ile bir Pair<String, String> dizisi oluşturmaya çalışırsanız bunu görebilirsiniz.

public static void main(String[] args) {

   // Compilation error Generic array creation
  Pair<String, String>[] array = new Pair<String, String>[10];
}
Nedeni açık: güvenlik yazın. Hatırlayacağınız gibi dizi oluştururken mutlaka dizinin hangi nesneleri (veya primitifleri) saklayacağını belirtmeniz gerekir.

int array[] = new int[10];
Önceki derslerimizden birinde yazı silme işlemini detaylı bir şekilde incelemiştik. PairBu durumda, tip silme, nesnelerin çiftleri sakladığı bilgileri kaybetmemize neden olur <String, String>. Diziyi oluşturmak güvensiz olur. Değişkenler ve jenerikler içeren yöntemleri kullanırken , tip silmeyi ve bunun nasıl çalıştığını hatırladığınızdan emin olun. Yazdığınız koddan kesinlikle eminseniz ve herhangi bir sorun çıkarmayacağını biliyorsanız, açıklamaları kullanarak varargs ile ilgili uyarıları kapatabilirsiniz . @SafeVarargs

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

   for (E element : array) {
       list.add(element);
   }
}
Bu ek açıklamayı yönteminize eklerseniz, daha önce karşılaştığımız uyarı görünmez. Vararg'ları jenerik ilaçlarla kullanırken ortaya çıkabilecek başka bir sorun da yığın kirliliğidir. Jeneriklerle çalışırken vararg'ları kullanma - 3Yığın kirliliği aşağıdaki durumda olabilir:

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));
   }
}
Konsol çıktısı:

Exception in thread "main" java.lang.ClassCastException: java.lang.Integer cannot be cast to java.lang.String
Basit bir ifadeyle, yığın kirliliği, türdeki nesnelerin Ayığında olması gerektiği, ancak türdeki nesnelerin, Btür güvenliği ile ilgili hatalar nedeniyle orada sona erdiği zamandır. Örneğimizde, olan tam olarak budur. İlk olarak, ham değişkeni yarattık numbersve ArrayList<Number>ona genel bir koleksiyon ( ) atadık. 1Ardından sayıyı koleksiyona ekledik .

List<String> strings = numbers;
Bu satırda derleyici " Unchecked atama... " uyarısı vererek olası hatalar konusunda bizi uyarmaya çalıştı ama biz dikkate almadık. List<String>Sonunda , type türünde bir genel koleksiyona işaret eden türde bir genel değişken elde ederiz ArrayList<Number>. Açıkçası, bu durum sorun yaratabilir! Ve öyle de oluyor. Yeni değişkenimizi kullanarak koleksiyona bir dize ekliyoruz. Artık yığın kirliliğimiz var - parametreleştirilmiş koleksiyona bir sayı ve ardından bir dize ekledik. Derleyici bizi uyardı, ancak uyarısını dikkate almadık. ClassCastExceptionSonuç olarak, sadece program çalışırken bir tane alırız . Peki bunun varargs ile ne ilgisi var? Vararg'ları jenerik ilaçlarla kullanmak kolayca yığın kirliliğine yol açabilir. İşte basit bir örnek:

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);
   }
}
Burada neler oluyor? Tip silme nedeniyle, değişken uzunluklu argümanımız

List<String>...stringsLists
bir listeler dizisi haline gelir, yani List[]bilinmeyen türdeki nesnelerin (varargs'ın derleme sırasında düzenli bir diziye dönüştüğünü unutmayın). Bu nedenle, onu yöntemin ilk satırındaki değişkene kolayca atayabiliriz Object[] array— listemizdeki nesnelerin türü silindi! Object[]Ve şimdi , Java'daki tüm nesneler miras aldığı için, herhangi bir şey ekleyebileceğimiz bir değişkenimiz var Object! İlk başta, yalnızca bir dizi dizi listemiz var. Ancak tip silme ve vararg kullanımımız sayesinde, yaptığımız bir sayı listesini kolayca ekleyebiliriz. Sonuç olarak, farklı türdeki nesneleri karıştırarak yığını kirletiriz. ClassCastExceptionDiziden bir dize okumaya çalıştığımızda sonuç yine farklı olacaktır . Konsol çıktısı:

Exception in thread "main" java.lang.ClassCastException: java.lang.Integer cannot be cast to java.lang.String
Görünüşte basit bir mekanizma olan vararglar bu tür beklenmedik sonuçlara yol açabilir :) Ve böylece, bugünkü dersimiz burada sona eriyor. Birkaç görevi çözmeyi unutmayın ve zamanınız ve enerjiniz varsa, biraz daha çalışın. " Etkili Java " kendini okumaz! :) Bir sonrakine kadar!
Yorumlar
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION