====== Generics((From [[http://www.angelikalanger.com/GenericsFAQ/FAQSections/ProgrammingIdioms.html|Java Generics FAQs]] by Angelika Langer, also read [[http://java.sun.com/j2se/1.5/pdf/generics-tutorial.pdf|Generics in the Java Programming Language]] by Gilad Bracha)) ====== * A view to a regular collection that performs a runtime type check each time an element is inserted is called //a checked collection//: List stringList = Collections.checkedList(new ArrayList(), String.class); * Is a list of ''String'' also a list of ''Object''? The answer is **no**, consider the following code: List ls = new ArrayList(); List lo = ls; // not really allowed lo.add(new Object()); String s = ls.get(0); // run-time error: ClassCastException Type-safety is a primary design goal of generics. In particular, the language is designed to guarantee that if your entire application has been compiled without unchecked warnings, it is type safe (does not cause exceptions like above). * The component type of an array may not be a type variable or a parametrized type, unless it is an (unbounded) wildcard type. List[] lArr = new List[10]; // not really allowed, the only possibility is: List[] lArr = new List[10]; Object oA = lArr; Object[] oArr = (Object[]) o; List l = new ArrayList(); l.add(new Integer(3)); oAr[1] = l; String s = lArr[1].get(0); // run-time error: ClassCastException * ''Collection'' is a heterogeneous collection, while ''Collection'' is a homogeneous collection of elements of the same unknown type. You cannot add anything into the last collection, except ''null'' (which can be cast to //anything//) and you can get only ''Object'' values from this collection. If you know in runtime the type of collection elements you can cast and add objects to collection using "getTypeArgument" trick described below. * When collection is declared with wildcard parametrized collection type, cannot be added elements to: Collection> c = new ArrayList>(); c.add(new Pair("a", "b")); // compiler error The collection with wildcard parametrization of the type itself (''%%Collection>%%'') works fine. * void someMethod(Pair pair) { ... } is almost the same as void someMethod(Pair pair) { ... } except that you can use ''X'' and ''Y'' types in 2nd method implementation, while in 1st the only possible type is ''Object''. void someMethod(Pair pair) { ... } makes sure that only pairs of elements of the same type are passed to the method. * Because there is only one byte code representation of each generic type or method, Java compiler chooses the most general method signature (that matches any parameter ''T'') to delegate the call (''Object'' or ''Object[]'' or ''Collection''): static void overloadedMethod(Collection l) { System.out.println("overloadedMethod(Collection)"); } static void overloadedMethod(List l) { System.out.println("overloadedMethod(List)"); } static void genericMethod(List l) { overloadedMethod(l) ; // which method is called? } ... genericMethod(new ArrayList()); // expected overloadedMethod(List l) to be called * You cannot override the generics methods, if they do not have a 100% signature compatibility on compile time. You can always check this by putting ''@Override'' before a method call: class Box { public void reset(T t) { ... } } class NumBox extends Box { @Override public void reset(S t) { // compiler error here: the method must override a superclass method super.reset(t); } } (new NumBox()).reset(Long.valueOf(1)); // seems fine? // NumBox.reset(S) might not be compatible with Box.reset(Long). // Compiler cannot guarantee this when generating a binary class for NumBox. (new NumBox()).reset(Long.valueOf(1)); // Oops, is it Box.reset(Long) or NumBox.reset(Integer)? The method is not overriding a super class method anymore Correct examples follow: class NumBox extends Box { @Override public void reset(S t) { // fine here super.reset(t); } } class NumBox extends Box { @Override public void reset(Long t) { // fine here super.reset(t); } } * A wildcard parametrized type with a lower bound usage example: class Person implements Comparable { ... } class Student extends Person { ... } class Utilities { public static > void sort(List list) { ... } } List list = new ArrayList(); Utilities.sort(list); // error The reason for the error message is that the compiler infers the type parameter of the sort method as ''T'' = ''Student'' and that class ''Student'' is not ''Comparable''. It is ''Comparable'', but that does not meet the requirements imposed by the bound of the type parameter of method ''sort''. The solution is to use the following signature: class Utilities { public static > void sort(List list) { ... } } * "getThis" trick is a way to recover the type of ''this'' object in a class hierarchy. Example: public abstract class Node> { private final List children = new ArrayList(); private final N parent; protected Node(N parent) { this.parent = parent; parent.children.add(this); // error: incompatible types } public N getParent() { return parent; } public List getChildren() { return children; } } public class SpecialNode extends Node { public SpecialNode(SpecialNode parent) { super(parent); } } The list has been declared as ''List'', where ''N'' is a subtype of ''Node'', which cannot be casted to ''List''. And ''this'' is ''Node'', which causes the error. In order to overcome the problem, we need to recover the this object's actual type: public abstract class Node> { private final List children = new ArrayList(); private final N parent; protected abstract N getThis(); protected Node(N parent) { this.parent = parent; parent.children.add(getThis()); // fine } public N getParent() { return parent; } public List getChildren() { return children; } } public class SpecialNode extends Node { public SpecialNode(SpecialNode parent) { super(parent); } @Override protected SpecialNode getThis() { return this; } } * "getTypeArgument" trick is a technique for recovering the type argument from a wildcard parametrized type at run-time. interface GenericType { void method(T arg); } class ConcreteType implements GenericType { public void method(TypeArgument arg) {...} } /* Some interface */ class GenericUsage { // Using a wildcard parametrization of the generic interface: private GenericType reference; public void method(Object arg) { reference.method(arg); // error } } In order to solve the problem, you add a method to the implementation of the generic interface that returns the type argument of the parametrisation of the generic interface that the class implements. This way you can later retrieve the type argument dynamically at run-time: interface GenericType { void method(T arg); Class getTypeArgument(); } class ConcreteType implements GenericType { public void method(TypeArgument arg) {...} public Class getTypeArgument() { return TypeArgument.class; } } class GenericUsage { private GenericType reference; public void method(Object arg) { _helper(reference, arg); } private static void _helper(GenericType reference, Object arg) { reference.method(reference.getTypeArgument().cast(arg)); } } The work-around relies on the fact that the compiler performs type argument inference when a generic method is invoked. It means that the type of the ''reference'' argument in the helper method is not a wildcard parametrization, but a concrete parametrization for an unknown type that the compiler infers when the method is invoked. * There are cases when the generic version and the wildcard version of a method mean different things: public static class Box { public Box(S s) { } } public static void print1(List> list) { for (Box box : list) { System.out.println(box); } } public static void print2(List> list) { for (Box box : list) { System.out.println(box); } } public static void print3(List> list) { for (Box box : list) { System.out.println(box); } } List> list1 = new ArrayList>(); list1.add(new Box("abc")); list1.add(new Box(Integer.valueOf(100))); print1(list1); // error: this generic method expects a list of boxes, each having the same type print2(list1); // fine print3(list1); // fine List> list2 = new ArrayList>(); list2.add(new Box("abc")); list2.add(new Box(Integer.valueOf(100))); print1(list2); // fine print2(list2); // error: this wildcard method expects a list of boxes, where there is no restriction regarding the type of the boxes print3(list2); // fine * Using a generic helper method and wildcard capture. public static void reverse(List list) { reverseCapture(list); } private static void reverseCapture(List list) { List tmp = new ArrayList(list); for (int i = 0; i < list.size(); i++) { tmp.set(i, list.get(list.size() - i - 1)); } list = tmp; } More complicated case: class Pair { private E fst, snd; public E getFirst() { return fst; } public void setFirst(E s) { fst = s; } public E getSecond() { return snd; } public void setSecond(E s) { snd = s; } } public static List> swapAndReverse(List> l) { return captureType(l); } public static > List captureType(List l) { final List list = new ArrayList(l); for (int i = 0; i < l.size(); i++) { list.set(i, l.get(l.size() - i - 1)); } for (T pair : list) { E e = pair.getFirst(); pair.setFirst(pair.getSecond()); pair.setSecond(e); } return list; } The generic method has the advantage that it returns a concrete instantiation of ''List'', that does not suffer from the limitations that come with the wildcard instantiation that is returned from the wildcard version of the method. * If the two method have the same erasure then the class is illegal and rejected with a compile-time error message. interface Equivalent { boolean equalTo(T other); } interface EqualityComparable { boolean equalTo(T other); } class SomeClass implements Equivalent, EqualityComparable { // error, as interface methods have the same erasures, see below public boolean equalTo(Double other) { ... } public boolean equalTo(SomeClass other) { ... } } During type erasure the compiler does not only create the type erased versions of the two colliding interfaces, it would also try to create the necessary bridge methods. Bridge methods are synthetic methods generated by the compiler when a class has a parametrized supertype: interface Equivalent { boolean equalTo(Object other); } interface EqualityComparable { boolean equalTo(Object other); } class SomeClass implements Equivalent, EqualityComparable { public boolean equalTo(Double other) { ... } public boolean equalTo(Object other) { return equalTo((Double) other); } public boolean equalTo(SomeClass other) { ... } public boolean equalTo(Object other) { return equalTo((SomeClass) other); } } The bridge methods would have the same signature. Instead of resolving the conflict the compiler reports an error. There is no problem if the two interfaces are generic and the conflicting methods have different type erasures: interface Equivalent { boolean equalTo(T other); } interface EqualityComparable { boolean equalTo(T other); } class SomeClass implements Equivalent, EqualityComparable { public boolean equalTo(Double other) { ... } public boolean equalTo(SomeClass other) { ... } } Example (after a conceivable translation by type erasure): interface Equivalent { boolean equalTo(Number other); } interface EqualityComparable { boolean equalTo(Object other); } class SomeClass implements Equivalent, EqualityComparable { public boolean equalTo(Double other) { ... } public boolean equalTo(Number other) { return equalTo((Double) other); } public boolean equalTo(SomeClass other) { ... } public boolean equalTo(Object other) { return equalTo((SomeClass) other); } } As long as the colliding methods are instantiated for the same type argument there is no problem at all: class SomeClass implements Equivalent, EqualityComparable { public boolean equalTo(SomeClass other) { ... } } The class provide exactly one method, namely the matching one from both interfaces and the compiler generates one synthetic bridge method: class SomeClass implements Equivalent, EqualityComparable { public boolean equalTo(SomeClass other) { ... } public boolean equalTo(Object other) { return equalTo((SomeClass) other); } } {{tag>Java generics}}