generics

Generics


IntBox

public class IntBox {
    private int content;

    public void set(int content) { 
        this.content = content; 
    }

    public int get() { 
        return content; 
    }
}
IntBox intBox = new IntBox();
int i = intBox.get(); 

DoubleBox

public class DoubleBox {
    private double content;

    public void set(double content) { 
        this.content = content; 
    }

    public double get() { 
        return content; 
    }
}
DoubleBox doubleBox = new DoubleBox();
double d = doubleBox.get(); 

Box

public class Box {
    private Object content;

    public void set(Object content) { 
        this.content = content; 
    }

    public Object get() { 
        return content; 
    }
}
Box box = new Box();
Object d = box.get(); 

Box box = new Box();
box.set(42);
int i = box.get();  🚫
int i = (int) box.get();
  • Keine Typchecks zur Compilezeit

Box<T>

public class Box<T> {
   private T content;
   
   public void set(T content) { 
       this.content = content; 
   }
   
   public T get() { 
       return content; 
   }
}

Box<Integer> intBox = new Box<>();
intBox.set(42);
int i = intBox.get();
intBox.set(3.14);   🚫
Box<Double> doubleBox = new Box<>();
doubleBox.set(3.14);

Generell

class Name<T, E, K, V, S, U>      
        extends X<T> implements Y<T, E>, Z { }
  • Parameter: Übergabe Wert foo(value)
  • Typparameter: Übergabe Typ Foo<Type>
  • Typparameter wird beim Erzeugen gesetzt
new Box<Integer>(3);
new Box<Double>();

Falls der Typ "klar" ist, kann auch <> stehen

Box<String> box = new Box<>();
Box< Box<String> > boxBox = new Box<>();

Implementierung

public class Box<T> {
   private T content;

   public void set(T content) { 
       this.content = content; 
   }
}

wird beim Kompilieren zu

public class Box {
   private Object content;

   public void set(Object content) { 
       this.content = content; 
   }
}

Generische Methoden

modifier... <T, U, ...> returntyp name(params...)
class Utils {

    public static <T> void foo(T t) { }
}

Meist werden Typargumente automatisch ergänzt (Type inference)

Utils.foo(42);

Konsequenzen

Note wird schlechter bei

  • catch Exception
  • return null
  • foo(null)
  • Raw Types in Abgabe
    • IntelliJ unterlegt Box färbig
    • IntelliJ Problems
    • IntelliJ Analyze -> Inspect Code

Bounded Types

public class NumberBox<T extends Number> {

    private T content;

    public NumberBox(T content) {
        this.content = content;
    }

    public int intValue() {
        return content.intValue();
    }
}

new NumberBox<>(3);
new NumberBox<>(3.1415);
new NumberBox<>("legit");   🚫

extends steht hier auch für Interfaces


Vererbung


Integer i = 42;
Number number = i;  ✔️
Object object = i;  ✔️

Box<Number> box = new Box<>();
box.set(10);
box.set(3.1415);

Box<Number> box = new Box<Number>();
box = new Box<Integer>();     🚫

generics-inheritance


generics-inheritance

Box<Integer> integerBox = new Box<>();
Box<Number> numberBox = new Box<>();
numberBox = integerBox; 🚫
ansonsten

numberBox.set(3.1415);
int i = integerBox.get();

? extends - Wildcards

Box<Number> box = new Box<>();
box = new Box<Integer>();   🚫
Box<? extends Number> box;
box = new Box<Number>();
box = new Box<Integer>();
box = new Box<Double>();

box.set(3.1415);     🚫 box könnte Box<Integer> sein

double d = box.get();     🚫 box könnte Box<LongLong> sein
Number number = box.get();  ✔️
Box<?> box = new Box<AnyClass>();️

? super

Box<? super Integer> box;
box = new Box<Integer>();
box = new Box<Number>();
box = new Box<Object>();

box.set(number);     🚫 box könnte Box<Integer> sein
box.set(42);    ✔️

int i = box.get();     🚫 box könnte Box<Number> sein

Verwendung

extends für Parameter


static int intValue(Box<Number> box) {
    return box.get().intValue();
}

intValue(new Box<Number>());      ✔️
intValue(new Box<Integer>());     🚫

könnte man verbessern zu


static int intValue(Box<? extends Number> box) {
    return box.get().intValue();
}

intValue(new Box<Number>());      ✔️
intValue(new Box<Integer>());     ✔️

Einschränkungen

  • Keine primitiven Typen als Typargument
    
    Box<int> box;   🚫
  • Keine Konstruktoraufrufe für Typparameter
    
    new T();   🚫
  • Keine statischen Typparameter - Referenzen
    
    class Foo<T> {
        static T t; 🚫
    }

  • Keine generischen Arrays erzeugen
    
    static <T> void foo() {
        T[] array;        ✔️
        array = new T[1]; 🚫
    }
  • Keine Arrays parametrisierter Typen
    
    static void foo() {
        Box<Integer>[] array;        ✔️
        array = new Box<Integer>[1]; 🚫
    }
  • Typparameter - varargs sind ok, solang die Methode nicht für ein Object[] aufgerufen wird
    
    @SafeVarargs
    static <T> void foo(T... ts) {
    }