2 Ekim 2017 Pazartesi

Generics ve Bounded Wildcard

1. Bounded Wildcard
Kalıtım hiyerarşisinde üstten veya alttan sınırlandırılmış wildcard anlamına gelir. Java'daki kurallar PECS (Producer extends Consumer super) kuralı ile hatırlanabilir. Kural kısaca şu anlama geliyor.

1. Eğer liste dolaşılacaksa - yani producer ise - extends ile kullan.
2. Eğer listeye yeni bir şey eklenecekse - yani consumer ise - super ile kullan.

Eğer liste hem dolaşılacak hem de eklenecekse wildcard kullanılmaması gerekir. Örnekte count metodu listeye yeni eleman ekler, sum ise listeyi dolaşır.
public static double sumCount(Collection<Number> nums, int n) {
  count(nums, n);
  return sum(nums);
}

2. Upper Bounded Wildcard
Extends Olmazsa
Elimizde Meeting sınıflarını süzen bir kod olsun.
private List<Meeting> myMeetings = new ArrayList<>();

public PastMeeting getPastMeeting (int id){
  Meeting meetingWithId;
    = myMeetings.parallelStream()
        .filter(meeting -> meeting.getId() == id )
        .findFirst()
        .get();
  return meetingWithId;
}
Eğer bu metodu generic yapmak istersek ve extends kullanmadan şöyle kodlamayı deneyebiliriz ancak bu kod derlenmez çünkü T Object ile yer değiştirir ve Object nesnesinin getId() diye bir metodu yoktur.
public T returnMeeting(T meeting, List<T> meetingList, int id) {        
  T meeting = meetingList.parallelStream()
                .filter(m -> m.getId() == id )
                .findFirst()
                .get();
  return meeting;
}
Derlemek için T sınıfının ne ile yer değiştirebileceğini belirtmek gerekir. Şöyle yaparız.
private <T extends Meeting> T returnMeeting(List<T> meetingList, int id) {
  return meetingList.parallelStream()
           .filter(m -> m.getId() == id )
           .findFirst()
           .get();
}
Tek sınıflı extends
Örnek
Yukarıdaki PECS açıklamasından yola çıkarak şöyle bir örnek verelim. sum isimli bir metodumuz olsun.
public static double sum(Collection<? extends Number> nums) {
   double s = 0.0;
   for (Number num : nums) 
      s += num.doubleValue();
   return s;
}
Bu metodu Number'dan türeyen Integer, Double vs. gibi listelerle çağırabilirim ve sorun çıkmaz çünkü metod imzasında extends kullandım.
List<Integer>ints = Arrays.asList(1,2,3);
assert sum(ints) == 6.0;
List<Double>doubles = Arrays.asList(2.78,3.14);
assert sum(doubles) == 5.92;
List<Number>nums = Arrays.<Number>asList(1,2,2.78,3.14);
assert sum(nums) == 8.92;
Örnek
Şu kod derlenmez.
private void addString(List<? extends String> list, String s) {
  list.add(s); // does not compile
  list.add(list.get(0)); // doesn't compile either
}
Örnek
Java 8 ile "enhanced type inference" yapılabiliyor. Elimizde şöyle bir kod olsun.
package org.my.java;

public class TestTypeVariable {

  static <T,A extends T> void typeVarType(T t, A a){
    System.out.println(a.getClass());
    System.out.println(t.getClass());
  }

  public static void main(String[] s){
    int i= 1;
    typeVarType("string", i);
  }
}
Çıktı olarak şunu alırız. Burada T String yerine Object olarak düşünüldüğü için kod derlenir ve çalışır.
class java.lang.Integer
class java.lang.String

Çok Sınıflı Extends
Extends ilişkisi "and" kullanılarak ta yapılabilir. Örnekte V'nin hem Number hem de Comparable arayüzlerini gerçekleştirmesini beklenir.
class  MyTest<K,V extends Number & Comparable> implements Comparable<MyTest>{

    private K key;
    private V value;

}
V için Integer, Float gibi sınıflar kullanılabilir.
MyTest<String, Integer> myTest; // K = String, V = Integer
Ancak String Number arayüzünü gerçekleştirmediği için kullanılamaz
MyTest<Integer, String> myTest; //V = String, does not implement Number
Daha karmaşık extends tanımlamaları şöyle. Bu sefer T ve E Integer'dan türeyen nesneleri içeren List yapısını temsil ediyor.
<T extends List<? extends Integer>, E extends List<? extends Integer>> 
void iterate(MyClass<T, E> myClass) {
...    
}

İç İçe Extends
 
Daha karmaşık extends tanımlamaları şöyle. Bu sefer T ve E Integer'dan türeyen nesneleri içeren List yapısını temsil ediyor.
<T extends List<? extends Integer>, E extends List<? extends Integer>> 
void iterate(MyClass<T, E> myClass) {
...    
}

3. Lower Bounded Wildcard
Yukarıdaki PECS açıklamasından yola çıkarak listeye bir şey ekleneceğini anlayabiliriz. Liste şöyle tanımlanır.
List<? super Number> list = null;
Eklemek için Number'dan kalıttığı bir sınıf kullanılabilir.

Ekleme için belirtilen sınıf kullanılır. 
Örnekte Map ArrayList alacak alacak diye tanımlanıyor. Object veya List eklemeye çalışmak hatadır
List<Object> list = new ArrayList();
Map<Object, ? super ArrayList> m = new HashMap<Object,  ArrayList>();
m.put(1, new Object()); //Error
m.put(2, list); //Error
Kullanmak için şöyle yapmak gerekir.
ArrayList list = new ArrayList();
Map<Object, ? super List> m = new HashMap<Object, List>(); 
m.put(2, list);

Eklenen nesne belirtilen sınıftan kalıtan bir sınıf olabilir.
Basit bir nesne hiyerarşimiz olsun. Shape sınıfının kendisi veya Shape sınıfından kalıtan Circle, Square, Rectangle gibi sınıflar listeye eklenebilir. Listeyi okumaya çalışmak hatalıdır.
public void testContraVariance(List<? super Shape> list) {
  list.add(new Shape());//compiles
  list.add(new Circle());//compiles
  list.add(new Square());//compiles
  list.add(new Rectangle());//compiles
  Shape shape= list.get(0); // does not compile
}






Hiç yorum yok:

Yorum Gönder