11 Mayıs 2018 Cuma

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.
Örnek
Ö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
Örnek
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) {
...    
}
Örnek
Variadic function şöyle yaparız.
public class Test {
   private static class A implements AutoCloseable, Runnable {
      @Override public void close () throws Exception {}
      @Override public void run () {} }
   private static class B implements AutoCloseable, Runnable {
      @Override public void close () throws Exception {}
      @Override public void run () {} }
   private static class C extends B {}

   private static <T extends AutoCloseable & Runnable> void printType( T... args ) {
      System.out.println( args.getClass().getComponentType().getSimpleName() );
   }

   public static void main( String[] args ) {
      printType( new A() );          // A[] created here
      printType( new B(), new B() ); // B[] created here
      printType( new B(), new C() ); // B[] which is the common base class
      printType( new A(), new B() ); // AutoCloseable[] - well...
      printType();                   // AutoCloseable[] - same as above
   }
}
İç İç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
Generics ve Lower Bounded Wildcard - <?super T> yazısına taşıdım.

Hiç yorum yok:

Yorum Gönder