MERHABA! Jenerik çalışmamıza devam edelim. Onlar hakkında önceki derslerde zaten önemli miktarda bilgi edindiniz ( jeneriklerle çalışırken vararg'ları kullanma ve tip silme hakkında ), ancak henüz ele almadığımız önemli bir konu var: joker karakterler . Bu jeneriklerin çok önemli bir özelliğidir. Öyle ki ona ayrı bir ders ayırdık! Bununla birlikte, joker karakterler konusunda özellikle karmaşık bir şey yoktur. Hemen göreceksiniz :)
Bir örneğe bakalım:

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;
}
}
Burada neler oluyor? Çok benzer iki durum görüyoruz. String
Bu durumda, bir nesneyi bir nesneye atarız Object
. Burada sorun yok — her şey beklendiği gibi çalışıyor. Ancak ikinci durumda derleyici bir hata üretir. Ama aynı şeyi yapıyoruz, değil mi? Bu sefer sadece birkaç nesneden oluşan bir koleksiyon kullanıyoruz. Ancak hata neden oluşuyor? Fark ne? String
Bir nesneyi bir Object
veya 20 nesneye mi atıyoruz ? Bir nesne ile bir nesneler koleksiyonu arasında önemli bir ayrım vardır . Sınıf, B
sınıfın bir alt öğesiyse A
, o zaman Collection<B>
öğesinin bir alt öğesi değildir Collection<A>
. List<String>
Bu yüzden bir oyuncumuzu alamadık.List<Object>
. String
'nin çocuğudur Object
, ama ' List<String>
nin çocuğu değildir List<Object>
. Bu süper sezgisel görünmeyebilir. Dilin yaratıcıları neden bu şekilde yaptı? Derleyicinin bize bir hata vermediğini düşünelim:
List<String> strings = new ArrayList<String>();
List<Object> objects = strings;
Bu durumda, örneğin aşağıdakileri yapabiliriz:
objects.add(new Object());
String s = strings.get(0);
Derleyici bize herhangi bir hata vermediğinden ve List<Object>
işaret eden bir referans oluşturmamıza izin verdiğinden, koleksiyona strings
herhangi bir eski nesneyi ekleyebiliriz ! Bu nedenle, koleksiyonumuzun yalnızca invocation genel türünde type bağımsız değişkeni tarafından belirtilen nesneleri içerdiği garantisini kaybettik . Başka bir deyişle, jenerik ilaçların ana avantajı olan tip güvenliğini kaybettik. Ve derleyici bunu yapmamızı engellemediği için, yalnızca çalışma zamanında bir hata alırız ki bu her zaman bir derleme hatasından çok daha kötüdür. Bu gibi durumları önlemek için derleyici bize bir hata veriyor: Object
strings
String
// Compilation error
List<Object> objects = strings;
... ve List<String>
soyundan olmadığını bize hatırlatıyor List<Object>
. Bu, jenerik ilaçlar için katı bir kuraldır ve onlarla çalışırken hatırlanması gerekir. Hadi devam edelim. Küçük bir sınıf hiyerarşimiz olduğunu varsayalım:
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()");
}
}
Hiyerarşinin tepesinde, Pet tarafından miras alınan basit bir Animal sınıfı bulunur. Pet'in 2 alt sınıfı vardır: Köpek ve Kedi. Şimdi basit bir metod oluşturmamız gerektiğini varsayalım iterateAnimals()
. Animal
Yöntem, herhangi bir hayvanın ( , Pet
, Cat
, ) bir koleksiyonunu almalı Dog
, tüm öğeler üzerinde yineleme yapmalı ve her yineleme sırasında konsolda bir mesaj göstermelidir. Böyle bir yöntem yazmaya çalışalım:
public static void iterateAnimals(Collection<Animal> animals) {
for(Animal animal: animals) {
System.out.println("Another iteration in the loop!");
}
}
Görünüşe göre sorun çözüldü! Ancak, son zamanlarda öğrendiğimiz gibi, List<Cat>
ve List<Dog>
torunları List<Pet>
değiliz List<Animal>
! iterateAnimals()
Bu , yöntemi bir kedi listesiyle çağırmaya çalıştığımızda bir derleme hatası aldığımız anlamına gelir :
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);
}
}
Durum bizim için pek iyi görünmüyor! Her tür hayvanı numaralandırmak için ayrı yöntemler yazmak zorunda mıyız? Aslında hayır, yapmıyoruz :) Ve tesadüfen, joker karakterler bu konuda bize yardımcı oluyor! Aşağıdaki yapıyı kullanarak sorunu basit bir yöntemle çözebiliriz:
public static void iterateAnimals(Collection<? extends Animal> animals) {
for(Animal animal: animals) {
System.out.println("Another iteration in the loop!");
}
}
Bu bir joker karakterdir. Daha doğrusu, bu, çeşitli joker karakter türlerinin ilkidir. Üst sınır joker karakterleri olarak bilinir ve ? uzanır . Bu yapı bize ne anlatıyor? Bu, yöntemin bir nesne koleksiyonunu veya (? Extends Animal) Animal
sınıfından türeyen herhangi bir sınıftaki nesne koleksiyonunu kabul ettiği anlamına gelir. Animal
Başka bir deyişle, yöntem Animal
, Pet
, Dog
veya Cat
nesnelerin bir koleksiyonunu kabul edebilir - fark etmez. İşe yaradığına kendimizi inandıralım:
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);
}
Konsol çıktısı:
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!
Toplam 4 koleksiyon ve 8 nesne oluşturduk ve konsolda tam olarak 8 giriş var. Her şey harika çalışıyor! :) Joker karakter, belirli türlere bağlı gerekli mantığı tek bir yönteme kolayca sığdırmamızı sağladı. Her hayvan türü için ayrı bir yöntem yazma ihtiyacını ortadan kaldırdık. Uygulamamız bir hayvanat bahçesi veya bir veteriner tarafından kullanılsaydı ne kadar çok yönteme ihtiyaç duyacağımızı bir düşünün :) Ama şimdi farklı bir duruma bakalım. Kalıtım hiyerarşimiz değişmeden kalır: en üst düzey sınıf Animal
, Pet
hemen altındaki sınıf ve bir sonraki düzeyde Cat
ve sınıflardır. Şimdi yöntemi köpekler hariç her tür hayvanla çalışacak şekilde Dog
yeniden yazmanız gerekiyor . Yani kabul etmeli ,iterateAnimals()
Collection<Animal>
Collection<Pet>
veya Collection<Car>
ile çalışmamalıdır Collection<Dog>
. Bunu nasıl başarabiliriz? Görünüşe göre yine her tür için ayrı bir yöntem yazma ihtimaliyle karşı karşıyayız :/ Derleyiciye ne olmasını istediğimizi başka nasıl açıklayabiliriz? Aslında oldukça basit! Bir kez daha joker karakterler burada yardımımıza koşuyor. Ancak bu kez başka bir tür joker karakter kullanacağız — alt sınırlı bir joker karakter , super kullanılarak ifade edilir .
public static void iterateAnimals(Collection<? super Cat> animals) {
for(int i = 0; i < animals.size(); i++) {
System.out.println("Another iteration in the loop!");
}
}
Burada prensip benzerdir. Yapı, <? super Cat>
derleyiciye, yöntemin iterateAnimals()
girdi olarak bir nesneler koleksiyonunu Cat
veya sınıfın herhangi bir atasını Cat
girdi olarak kabul edebileceğini söyler. Bu durumda, Cat
sınıfın ebeveyni Pet
ve ebeveyninin ebeveyni olan sınıfın Animal
tümü bu açıklamayla eşleşir. Sınıf Dog
, kısıtlamamızla eşleşmiyor, bu nedenle yöntemi bir List<Dog>
bağımsız değişkenle kullanma girişimi bir derleme hatasıyla sonuçlanacak:
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);
}
Sorunumuzu çözdük ve joker karakterler bir kez daha son derece faydalı oldu :) Bununla birlikte ders sona erdi. Artık jeneriklerin Java çalışmanızda ne kadar önemli olduğunu görüyorsunuz — onlar hakkında tam 4 dersimiz oldu! Ama artık konuya iyice hakim oldunuz ve iş görüşmelerinde yeteneklerinizi kanıtlayabilirsiniz :) Ve şimdi görevlere geri dönme zamanı! Çalışmalarınızda başarılar! :)
GO TO FULL VERSION