1 / 18

Java Generics

Java Generics. The Dark Ages: Before Java 5. Java relied only on inclusion polymorphism A polymorphism code = Using a common superclass Every class is a subclass of java.lang.Object So there's always at least one common superclass Collections were actually collections of objects

hamlet
Download Presentation

Java Generics

An Image/Link below is provided (as is) to download presentation Download Policy: Content on the Website is provided to you AS IS for your information and personal use and may not be sold / licensed / shared on other websites without getting consent from its author. Content is provided to you AS IS for your information and personal use only. Download presentation by click this link. While downloading, if for some reason you are not able to download a presentation, the publisher may have deleted the file from their server. During download, if you can't get a presentation, the file might be deleted by the publisher.

E N D

Presentation Transcript


  1. Java Generics

  2. The Dark Ages: Before Java 5 • Java relied only on inclusion polymorphism • A polymorphism code = Using a common superclass • Every class is a subclass of java.lang.Object • So there's always at least one common superclass • Collections were actually collections of objects • You can put anything into a collection • When you extract something, its static type is Object

  3. Java 5 and Beyond • Parametric polymorphism introduced in Java 5 • September 2004 • Most significant enhancement since Java's birth • May resemble C++ templates, but: • Implemented differently • “Compile once and for all” • Better error messages • Type constraints are explicit • Less power (e.g., cannot inherit from a type parameter)‏ • Parameters can only be types • No executable blowup

  4. Generics – Before and After // Without generics List list = new ArrayList(); list.add(new Integer(0)); Integer x = (Integer) list.get(0); // Programmer must downcast list.add("abc"); Integer y = (Integer) list.get(1); // Run -time exception // With Generics – The compiler “knows” the type of the list elements List<Integer> list = new ArrayList<Integer>(); list.add(new Integer(0)); Integer x = list.get(0); list.add("abc");// Compiler Error – Expected ineteger

  5. A Generic Method public static<T> List<T> dup(T t, int n) { List<T> result = new ArrayList<T>(); for(int i = 0; i < n; ++i)‏ result.add(t); return result; } ... List<String> list = dup("abc", 2); // Implicit instantiation // Explicit not supported ...

  6. Recap: java.lang.Integer, Number package java.lang; public abstract class Number { public abstract int intValue(); public abstract long longValue(); public abstract float floatValue(); public abstract double doubleValue(); public byte byteValue() { return (byte) intValue(); } public short shortValue() { return (short) intValue(); } } package java.lang; public final class Integer extends Number { private final int n; public Integer(int v) { n = v; } public int intValue() { return n; } public long longValue() { return (long) n; } public float floatValue() { return (float) n; } public double doubleValue() { return (double) n; } }

  7. The Cell<T> class public class Cell<T> { private T value; public T get() { return value; } public void set(T t) { value = t; } // T is at least an Object, so it supports toString()‏ public String toString() { return value.toString(); } }

  8. Using Cell<T> static void main(String[] args) { Cell<Integer> ci = new Cell<Integer>(); ci.set(new Integer(5)); System.out.println(ci.get()); int n = ci.get(); // auto-unboxing n = n*n; ci.set(n); // auto-boxing System.out.println(ci.get()); Cell<Number> cn = new Cell<Number>(); cn.set(ci.get()); }

  9. Type Parameters with Upper Bounds public class NumberCell<T extends Number> { private T value; public T get() { return value; } public void set(T t) { value = t; } public String toString() { return value.toString(); } // T is at least a Number, so it supports intValue()‏ public int sum(int x) { return value.intValue() + x; } } • The extend keyword specifies an upper bound for T • Can be used with both classes and interfaces • Can have multiple bounds: • T extends MyClass & MyInterface

  10. Cell<Integer> is not compatible with Cell<Object> static void assign(Cell<Object> co, Object o) { co.set(o); } void main() { Cell<Integer> ci = new Cell<Integer>(); assign(ci, new Integer(10)); // Compiler error:// Cannot convert from Cell<Integer> to Cell<Object>// Otherwise, the following code would break at run time assign(ci, "abc"); Integer n = ci.get(); System.out.println(n.intValue()); } • The same conformance issue as with covariant input arguments

  11. A better version of assign() static<T> void assign(Cell<T> co, T o) { co.set(o); } void main() { Cell<Integer> ci = new Cell<Integer>(); assign(ci, new Integer(10)); // Now it works }

  12. The Wildcard: <?> static<T, C extends Cell<T>> boolean isNullA(C c) { T t = c.get(); return t == null; } static<C extends Cell<?>> boolean isNullB(C c) { return c.get() == null; } static boolean isNullC(Cell<?> c) { return c.get() == null; } • If a type parameter is used exactly once – and this occurrence is inside an upper bound – it can be replaced with a wildcard • If a type parameter is used exactly once – and this occurrence is as a type of formal parameter of the method – it can be replaced with its upper bound

  13. Wildcard: Type Checking Rules static boolean isNullC(Cell<?> c) { ... } • Inside isNullC(): • ? Cannot be used to declare the type of a variable • c.get() return an Object • c.set(o) is not allowed, even if o is of type Object. • Exception to this rule: c.set(null) is allowed • For every type Y, the type X<?> is a supertype of X<Y>

  14. Lower Bounds public static class Cell<T> { private T value; public T get() { return value; } public void set(T t) { value = t; } public void copyTo(Cell<? super T> c) { c.set(value); } } • The super keyword specifies a lower bound for a wild-card • Cannot be used with regular type parameters

  15. Implementation of Generics: Type Erasure • Compiling a Generic class: Cell<T> • Check: Type correctness • Erase: Replace T with its upper bound (Object)‏ • Compile: to byte code • Compiling an instantiation: Cell<String> c; • Replace: The instantiated type with the raw type • Annotate: add a “footnote” specifying the substitution:c: T = String • The annotation is saved in the class file declaring c • Compiling a field access or a message send: c.get()‏ • Obtain: The annotation of the receiver variable, c • Check: Actual method parameters against the actual type parameters • (None in this example)‏ • Downcast: insert a cast of the return type to the actual type parameter, String

  16. Type Erasure • Benefit of Erasure: • Binary compatibility with older libraries: List<String> is translated to type List (raw type )‏ • Code compiled using a "pre-generics" class works correctly • Code written using a "pre-generics" class still compiles and work • Drawback of Erasure • generic type information is not known at runtime • List<Integer> and List<String> refer to List • type variables cannot be used in newexpressions • Type is unknown at runtime

  17. Type Erasure: Example • Generic form class Foo<T extends Number> { T m(Set<? Extends T> s) { } } • Erased form: class Foo { Number m(Set s){} }

  18. Erasure: Loophole List<Integer> list = new ArrayList<Integer>(); Object o = list; List<Object> err = (List<Object>) o; // Warning: Unchecked Conversion err.add("abc"); Integer i = list.get(0); // Runtime error: cast from String to Integer • Unchecked conversion warning • Issued by the compiler when we use an instantiated type in a position where only the raw type is available • Usually appears when we cast to an instantiated generic type • The downcast check will be partial • Indicates a danger of type errors at run time • Avoid at all costs

More Related