Generics

in

JAVA

Thomas Feiner

Why Generics?



    LinkedList list = new LinkedList();
    list.add(new Integer(0));
    Integer i = (Integer)list.get(0);
    String s = (String)list.get(0); //not typesafe, fails at runtime
            

Generic Collections


  • Homogenous collections
  • Compile time checks (no casts necessary)



    LinkedList<Integer> list = new LinkedList<>(); //diamond operator
    list.add(new Integer(0));
    Integer i = list.get(0);
    String s = list.get(0); //compile time error
           

Definition of Generic Type


    public class Pocket<T> {
        private T value;

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

        public T get() {
            return value;
        }

        public boolean isEmpty() {
            return value != null;
        }
    }
    

Formal type parameter represented by type variable T

Usage of Generic Type


    public class MyClass {
        public static void main (String[] args) {
            Pocket<String> myPocket = new Pocket<>(); //diamond operator
            myPocket.set("item1");
            System.out.println(myPocket.get());
            System.out.println(myPocket.isEmpty());
        }
    }
    

Concrete type argument "String".
MyPocket<String> is a parameterized type.

Limitations


  • Similar to type, but not really a type
    (therefore define them in capital letters)
  • Not allowed in "new"
  • Cannot be derived from
  • No class literal
  • No primitives
  • No Arrays of parameterized types
    (use @SafeVarargs for varargs)

Concepts

Term Example
generic type Pocket<T>
formal type parameter T
parameterized type Pocket<String>
actual type parameter String
raw type Pocket

Nested generics



    Pocket<Pocket<String>> pocketOfPockets = new Pocket<>();
    pocketOfPockets.set( new Pocket<String>() );
    pocketOfPockets.get().set( "Inner String Pocket" );
    System.out.println( pocketOfPockets.get().get() ); // Inner String Pocket
            

Diamond operator



     List<List<String>> listOfLists = new ArrayList<>();
            
  • for declaration and initialisation of attributes and local variables

  • for initialisation of attributes, local variables and parameter variables

  • as argument for method and constructor calls

  • for method return values

  • type inference is complex, use explicit type if diamond operator not possible

Generic interfaces



     public interface Comparable<T> {
        public int compareTo(T o);
     }
            

     public interface Set<E> extends Collection<E> {
        Iterator<E> iterator();

        <T> T[] toArray(T[] a);

        boolean add(E e);

        ...
     }
            

Implementing Generic interfaces


non-generic class resolves Generics during implementation


    public MyStringComparable implements Comparable<String>
            

generic class implementing generic interface


    public MyComparable<E> implements Comparable<String>
            

hint: you can also use Void as type parameter


    public MyComparable<E> implements Comparable<Void>
            

Generic methods


Static methods cannot access the actual type parameter

Different methods cannot use different type variables

Solution: Generic Methods


Modifier <type variable(s)> "return type" "method name" "parameters" "throws-clause"
            

public static <T> void copy(List<T> dest, List<T> src)
            

Type inference



public static <T> T random(T first, T second);
            
  • Return type is computed from context: type inference

  • first and second do not need to be of same type

  • In case of different types, compiler walks up the type hierarchy, until same type is found

Type inference


Call Identified types Common types
random("test", 1) String, Integer Object, Serializable, Comparable
random(1L, 1D) Long, Double Object, Number, Comparable
random(new Point(), new StringBuilder() Point, StringBuilder Object, Serialiable, Cloneable

Type inference



    Object       s1 = random( "Essen", 1 );

    Serializable s2 = random( "Essen", 1 );

    Comparable   s3 = random( "Essen", 1 );
            

Type inference



List<String[]> myList = Arrays.<String[]>asList(new String[] { "A", "B" } );
            

Improved type inference in Java 8

Type erasure



    public class Pocket<T> {
        private T value;
        public void set( T value ) {
            this.value = value;
        }
    }
           

    public class Pocket {
        private Object value;
        public void set(Object value ) {
            this.value = value;
        }
    }
           

Type erasure



    Pocket<Integer> p  = new Pocket<Integer>(1);
    p.set(1);
    Integer i = p.get();
           

    Pocket p  = new Pocket( 1 );
    p.set(1);
    Integer i = (Integer) p.get();
           

Type erasure - problems


  • Type information not available during runtime

  • no "new T"

  • no "instanceof"

  • no casts
  • 
    List<Number> numbers = new ArrayList<Integer>())
                     

Type erasure - problems


  • no .class for generic types
  • 
         Class<Pocket<String>> pocketClass = Pocket<String>.class;
                     
  • no generic exceptions

  • static methods cannot access the actual type parameter(s)

  • no overloading

  • no Arrays of generic classes

Raw types


  • Due to compatibility reasons you can use the raw type
  • 
         List myList = new ArrayList();
                    

  • Do NOT use raw types in new code. If this is needed, something is pretty wrong!

Restrict types - extends



    public static <T extends Comparable<T>> T max( T first, T second )
            

The common type of first and second has to be "Comparable"



    max( 12L, 100F ) );  // Bound mismatch compiler error
            

Recursive type bound

Generics and inheritance


  • Arrays are covariant
  • Generics are invariant                                 
  • 
    HashSet<Object> mySet = new HashSet<String>();
                    

Wildcards with ?


If type does not matter (read-only access), use ? wildcard


    public static boolean isOnePocketEmpty(Pocket<?>... pockets)
            

read only, you cannot write!


    List<?> myIntegerList = new ArrayList<Integer>();
    Object firstNumber = myIntegerList.get(0);
    myIntegerList.add(23); // compiler error
            

Wildcards


Example Meaning
List<?> myList List with arbitrary elements
List<? extends Number> myList List with numbers or descendants
Comparator<? super String> String-, Object- or CharSequence Comparator

PECS principle


Producer extends, Consumer Super

LESS: lesen extends, schreiben super


public class PecsExample {
    public static void main(String[] args) {
        List<Integer> src = Arrays.asList(1, 2, 3, 4);
        List<Number> dest = new ArrayList<>();

        copyList2(src, dest);
        dest.stream().forEach(element -> System.out.println(element.intValue()));
    }

    private static void copyList1(List<? extends Number> src, List<? extends Number> dest) {
        for (Number element : src) {
            dest.add(element); // compile error
        }
    }

    private static void copyList2(List<? extends Number> src, List<? super Number> dest) {
        // src.add(3); // compile error, extends can only be read
        for (Number element : src) {
            dest.add(element);
        }
        // Object firstElement = dest.get(0); // super can only be written (returns just Object)
    }
}
        

Thank you for your attention!