28 Eylül 2020 Pazartesi

Generics ve Upper Bounded Wildcard - Listeyi Dolaşır

Giriş 
Not : Bu yazıyla ilgili olarak Generics ve Upper Bounded Wildcard Return Type yazısına da bakılabilir.
Açıklaması şöyle
Useful when defined as method argument where method implementation only reads data from collection. But you can not add objects except null to upper bounded collection.
Java ve C++ Farkı
Java ve C++ arasındaki en bariz ayrılık noktalarından birisi de Bounded Wildcard kavramı. C++ bu kavramı desteklemez

Neden Extends Lazım
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();
}
Wildcard ve T Farkı
Bazı kodlarda ? yani wildcard bazı kodlarda ise T kullanıldığını görüyoruz. Her iki kullanım da çoğunlukla aynı şey. Ancak arada bazı farklar olabiliyor

1. Return Type Gerekiyorsa
Eğer generic metod kendisine geçilen parametre tipini return ediyorsa wildcard kullanılamaz. T kullanmak gerekir.
Örnek
Elimizde şöyle bir kod olsun. Burada wildcard kullanamayız yoksa T return tipini belirtmek mümkün değil.
public <T> T giveMeMaximum(Collection<T> items);
public <T> Collection<T> applyFilter(Collection<T> items);
Eğer T tipini dönmek gerekmeseydi şöyle olabilirdi
public int count(Collection<?> items);
public boolean containsDuplicate(Collection<?> items);
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. Çünkü extends varsa listeden sadece okuma yapabilirim.
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ştirmesi 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) {
...    
}

Hiç yorum yok:

Yorum Gönder