Upgrade to Pro — share decks privately, control downloads, hide ads and more …

Java Generics

Java Generics

This is a presentation i did in WSO2 about Java Generics. Java Generics were introduced in Java 5 back in 2004 and it has been a quite remarkable concept in making the implementations generic enough so that you don't have to worry about what objects it handles.

Gayashan Amarasinghe

November 20, 2014
Tweet

More Decks by Gayashan Amarasinghe

Other Decks in Programming

Transcript

  1. History Introduced in Java 5 Heavily utilized in the Collections

    API Also known as “Parameterized Types”
  2. Before Generics List coins = new ArrayList(); // contains only

    coins coins.add(new Coin(2)); coins.add(new Stamp()); Coin coin = (Coin) coins.get(0); Stamp stamp = (Stamp) coins.get(1); What if, Coin coin = (Coin) coins.get(1); // throws ClassCastException
  3. Before Generics - Issues • You can add any object

    type to any collection. • Only when you try to retrieve, in runtime you will detect errors. (ClassCastException) • Now you’ll have to search through the code base to identify where you have put a stamp into a coins collection!
  4. After Generics List<Coin> coins = new ArrayList<Coin>(); coins.add(new Coin(2)); coins.add(new

    Stamp()); // compile error Coin coin = coins.get(0); Stamp stamp = coins.get(1); // compile error What if, Coin coin = coins.get(1); // compile error
  5. After Generics Compile time error detection - modern IDEs utilize

    this heavily to guide No manual casts are needed More expressive code
  6. Raw types vs Generics 1. List objects = new ArrayList();

    2. List<Object> objects = new ArrayList<Object>(); [1] has opted out of generic type checking. [2] has explicitly told the compiler that it can hold objects of any type. ALWAYS use generics instead of Raw types! public static void main(String[] args) { List<String> strings = new ArrayList<String>(); unsafeAdd(strings, new Integer(42)); String s = strings.get(0); // Compiler-generated cast } private static void unsafeAdd(List list, Object o) { list.add(o); // unchecked call to add(E) }
  7. Subtyping rules List objects = new ArrayList(); List<Strings> strings =

    new ArrayList<String>(); objects = strings; // legal operation But, List<Object> objects = new ArrayList<Object>(); List<String> strings = new ArrayList<String>(); objects = strings; // compile error
  8. Subtyping rules List<String> is a sub type of List But

    List<String> is not a subtype of List<Object> For any two distinct types Type1 and Type2 , List<Type1> is neither a subtype nor a supertype of List<Type2>.
  9. Introducing Wildcards static void printElements(List list) { for (Object obj

    : list){ System.out.println(obj); } } This works, but this is very dangerous. Why?
  10. Introducing Wildcards Can we do this? static void printElements(List<Object> list)

    { for (Object obj : list){ System.out.println(obj); } } Remember subtyping rules? List<String> strings = new ArrayList<String>(); printElements(strings); // compile error
  11. Introducing Wildcards static void printElements(List<?> list) { for (Object obj

    : list){ System.out.println(obj); } } ? is the wildcard type Wildcards - type safe and flexible
  12. Unbounded Wildcards • Collection<?> is a collection of some unknown

    type • Most general parameterized type • You can’t put any element (other than null) into a Collection<?> • You can’t assume anything of the type of the object you get out as well. ◦ You can only treat them as Objects
  13. Bounded Wildcards • Wildcard types can have upper and lower

    bounds • A List<? extends Shape> is a List of items that have unknown type but are all at least Shapes. - upper bound • A List<? super Shape> is a List of items that have unknown type but are all at most Shapes. - lower bound
  14. PECS principle Producer extends, Consumer super Case 1: You want

    to go through the collection and do things with each item. Then the collection is a producer, so you should use Collection<? extends Thing>. Case 2: You want to add things to the collection. Then the collection is a consumer, so you should use Collection<? super Thing>.
  15. Generic methods Methods can be parameterized as well! public static

    <E> Set<E> union(Set<E> s1, Set<E> s2) Type Parameter list • The compiler figures out the type parameter by examining the types of method argument - via type inference Type parameter representing element type
  16. Generic methods • Type inference Set<String> fruits = new HashSet<String>(Arrays.

    asList(“apple”, “orange”)); Set<String> veges = new HashSet<String>(Arrays. asList(“carrot”, “cabbage”)); Set<String> combined = union(fruits, veges);
  17. Generic methods - more static void fromArrayToCollection(Object[] a, Collection<?> c)

    { for (Object o : a) { c.add(o); // compile error } } Way to fix this - use generic types, static <T> void fromArrayToCollection(T[] a, Collection<T> c) { for (T o : a) { c.add(o); // Correct } }
  18. Generic methods - when? If return type doesn’t depend on

    type parameter and, neither any other argument in the method - use Wildcards Need to express dependencies among the types of one or more arguments to a method and/or its return type - use generic methods String[] sa = new String[100]; Collection<String> cs = new ArrayList<String>(); // T inferred to be String fromArrayToCollection(sa, cs);
  19. How Generics are Implemented • Rather than change every JVM

    between Java 1.4 and 1.5, they chose to use erasure. • After compiler does the type checking it discards the generics, and JVM never sees them! • Works similar to this - ◦ List<String> becomes List ◦ Use of type variables are replaced by their upper bound (usually Object) ◦ Casts are inserted to preserve type correctness
  20. Pros and Cons of Erasure Good - Backward compatibility is

    preserved. (can be used with legacy non-generic libraries) Bad - Cannot find out what type a generic class is using, at run-time. Eg: class Sample<T>{ void method(Object item){ if(item instanceof T) { ... } // compile error T anotherItem = new T(); // compile error T[] itemArray = new T[5]; // compile error } }
  21. Summary Use Generics ALWAYS! - Don’t use raw types Favour

    generic classes - Object-based collection Favour generic methods - static utility methods Use bounded wildcards to increase API flexibility Do not ignore compiler warnings!