Generics

Variables for the Compiler

Container Concepts and Types

The basic problem explained.

  • Generic container objects, such as lists and maps don't know what will be stored later
  • If you want to restrict that, you would need individual implementations per type
  • Type-safety is key for Java, but this breaks the concept
  • Programming mistakes are likely and happen during runtime, not compile time
public void show()
{
    List l = new ArrayList();
    l.add("String");
    l.add(new Integer(1));
    l.add(new Random());

    Object a = l.get(0);
    Object b = l.get(1);
    Object c = l.get(2);

    // force type by casting
    String s1 = (String) l.get(0);
    Integer i1 = (Integer) l.get(1);
    Random r1 = (Random) l.get(2);

    // big mistake!!!
    String s2 = (String) l.get(2);
    Integer i2 = (Integer) l.get(0);
    Random r2 = (Random) l.get(1);
}

Generics to the Rescue

public void show()
{
    List l = new ArrayList();
    l.add("String");
    l.add(new Integer(1));
    l.add(new Random());

    Object a = l.get(0);
    Object b = l.get(1);
    Object c = l.get(2);

    // force type by casting
    String s1 = (String) l.get(0);
    Integer i1 = (Integer) l.get(1);
    Random r1 = (Random) l.get(2);

    // big mistake!!!
    String s2 = (String) l.get(2);
    Integer i2 = (Integer) l.get(0);
    Random r2 = (Random) l.get(1);
}
public void show()
{
    List<String> l = new ArrayList<String>();
    l.add("String");
    l.add(new Integer(1)); // compiler complains
    l.add(new Random()); // compiler complains

    Object a = l.get(0);
    Object b = l.get(1);
    Object c = l.get(2);

    // force type by casting
    String s1 = (String) l.get(0);
    Integer i1 = (Integer) l.get(1); // compiler complains
    Random r1 = (Random) l.get(2); // compiler complains

    // only one way of usage possible now
    String string1 = l.get(0);
    String string2 = l.get(1);
}

What the Runtime Sees

public void show()
{
    List<String> l = new ArrayList<String>();
    l.add("String");

    Object a = l.get(0);
    Object b = l.get(1);
    Object c = l.get(2);

    // force type by casting
    String s1 = (String) l.get(0);

    // only one way of usage possible now
    String string1 = l.get(0);
    String string2 = l.get(1);
}
public void show()
{
    List l = new ArrayList();
    l.add("String");

    Object a = l.get(0);
    Object b = l.get(1);
    Object c = l.get(2);

    // force type by casting
    String s1 = (String) l.get(0);

    // only one way of usage possible now
    String string1 = l.get(0);
    String string2 = l.get(1);
}

What are Generics

Generics explained

  • Generics are "variables" for the compiler
  • Fill in the type during design time
  • Typically used when types are designed, declared, not when used
  • Enable static type checking during compile-time
  • Terms explained - List<T>
    • List<T> - generic type
    • T - generic type
    • List<String> - parameterized type
    • String - actual type
    • List - raw type

Small Example

Really just a simple example

public class Box<T>
{
    private T content;

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

    public T getContent()
    {
        return content;
    }
}
public void show()
{
    Box<String> box = new Box<String>("Foo");
    String s = box.getContent();
}

Diamond Operator

Who wants to type?

  • Compiler derives information from type
  • Hence called type inference
  • <> let's the compiler do the work
public void show()
{
    Box<String> box = new Box<String>("Foo");
    String s = box.getContent();
}
public void show()
{
    Box<String> box = new Box<>("Foo"); // compile knows the rest
    String s = box.getContent();
}

Nested Generics

When generics hold other generics

  • You can put generic into generics
  • Type declaration has to permit that
public void show()
{
    List<Map<Date, String>> listOfMaps = new ArrayList<Map<Date, String>>();
}
public void show()
{
    List<Map<Date, String>> listOfMaps = new ArrayList<>();
}

Generics for Statics

When you don't have objects

  • Generics in static methods work
  • Cannot take the type from the class around it
  • Need another way to place the variable
  • Declares the generic type before return type
public static <T> T eitherOr( T m, T n )
{
    return Math.random() > 0.5 ? m : n;
}

Wildcards

When you don't know or care

  • Type is always required otherwise the compiler warns about raw-type usage
public void show()
{
    // raw-type usage warnings
    List l = new ArrayList();

    // you don't care, everything goes
    List<?> m = new ArrayList<>();
}

Limit what is permitted

Bound generics permit restrictions

  • Able to specify max type - super
    • Called lower-bound
    • T super Integer
    • T can be that type or lower
    • Classes and Interfaces are covered
/**
 * Permits List<Integer>, List<Number>, List<Object>
 */
public void addNumbers(List<? super Integer> list)

  • Able to specify min type - extends
    • Called upper-bound
    • T extends Number
    • T must extends this type
    • Classes and Interfaces are covered
/**
 * Permits List<Number>, List<Integer>, List<Float>
 * List<Long>, List<Short> ...
 */
public void addNumbers(List<? extends Number> list)