Title: Effective Java: Generics
1Effective Java Generics
2Agenda
- Material From Joshua Bloch
- Effective Java Programming Language Guide
- Cover Items 23-29
- Generics Chapter
- Bottom Line
- Generics are safer, than raw types
- But generics are also more complex
- Raw types are allowed for backwards compatibility
3Item 23 Dont Use Raw Types in New Code
- A class (interface) with one or more type
parameters is a generic class (interface) - Examples
- List is a raw type
- ListltEgt is a generic interface
- ListltStringgt is a parameterized type
- String is the actual type parameter corresponding
to E
4Example Replacing raw types
- // Now a raw collection type dont do this
- private final Collection stamps //
Contains only Stamps - // Erroneous insertion of coin into stamp
collection - stamps.add(new Coin()) // Oops! Were set
up for ClassCastException later - // Parameterized collection type - typesafe
- private final CollectionltStampgt stamps
- stamps.add(new Coin()) // result is
instead a compile time error, which is good - // Now a raw iterator type dont do this!
- for (Iterator I stamps.iterator()
i.hasNext() ) - Stamp s (Stamp) i.next() // Throws
ClassCastException - // Do something with the stamp
-
- // for-each loop over parameterized collection
typesafe - for (Stamp s stamps) // No cast
- // Do something with the stamp
5Example Mixing generic and raw types
- // Uses raw type (List) fails at runtime
- public static void main(String args)
- ListltStringgt strings new
ArrayListltStringgt() - unsafeAdd(strings, new Integer(42))
- String s strings.get(0) //Exception
from compiler generated cast -
- // note use of raw types
- private static void unsafeAdd(List list,
Object o) - list.add(o)
-
- // There is a compile time warning
- Test.java10 warning unchecked call to add(E)
in raw type List - list.add(o)
-
- // If we ignore the warning, and run the program,
we get a ClassCastException - // where the compiler inserted the cast
- // If we try the following, it wont compile (see
Item 25) - private static void unsafeAdd( ListltObjectgt
list, Object o) list.add(o)
6Example Using Wildcards
- // Use of raw type for unknown element type
dont do this! - static int numElementsInCommonSet (Set s1, Set
s2) - int result 0
- for (Object o1 s1)
- if (s2.contains(o1)) result
- return result
-
- // Unbounded wildcard type typesafe and
flexible - static int numElementsInCommonSet (Setlt?gt s1,
Setlt?gt s2) - int result 0
- for (Object o1 s1)
- if (s2.contains(o1)) result
- return result
-
- // Well revisit this type of example in Item 27
7Example Using Wildcards
- // Do the question marks really buy you anything?
- // Answer Wildcard is typesafe,
- // because you cant add anything (except null)
to Collectionlt?gt - // Two exceptions Raw types ok in
- Class Literals List.class, not
ListltStringgt.class - instanceof operator
- if (o instanceof Set) // raw type ok
- Setlt?gt m (Setlt?gt) o // Wildcard type
- // Why the exceptions? Compatibility with old
Java -
-
-
8Item 24 Eliminate Unchecked Warnings
- Generics result in many compiler warnings
- Eliminate them
- As a last resort, suppress the warnings
- Do so as at local a level as possible
- Options are class down to local declaration
- Use the _at_SuppressWarnings annotation
- Some are easy
- SetltLarkgt exaltation new HashSet()
// warning - SetltLarkgt exaltation new HashSet ltLarkgt()
// no warning
9Example Suppressing Warnings
- public ltTgt T to Array (T a)
- if (a.length lt size)
- return (T) Arrays.copyOf(elements, size,
a.getClass()) - System.arraycopy(elements, 0, a, 0, size)
- if (a.length gt size) a.size null
- return a
- The compiler generates a warning
- ArrayList.java305 warning unchecked
unchecked cast - found Object, required T
- return (T) Arrays.copyOf(elements, size,
a.getClass()) - Suppressing the warning
- if (a.length lt size)
- // This cast is correct because the array
were creating - // is of the same type as the one passed in,
which is T - _at_SuppressWarnings(unchecked)
- T result (T) Arrays.copyOf(elements,
size, a.getClass()) - return result
10Item 25 Prefer Lists to Arrays
- Lists play well with generics
- Generic array creation not typesafe (hence
illegal) - No new ListltEgt, new ListltStringgt , or new E
- Arrays are covariant, Generics are Invariant
- If Sub is a subtype of Super
- Then Sub is a subtype of Super
- But ListltSubgt is not a subtype of ListltSupergt
- Arrays are reified Generics are erased
- Generics are compile time only
11Example Covariance vs. Invariance
- // Fails at runtime
- Object objectArray new Long1
- objectArray0 I dont fit in! //
Throws ArrayStoreException - // Wont compile
- ListltObjectgt o1 new ArrayListltLonggt()
- o1.add(I dont fit in!)
// Incompatible types - Not compiling is better than a runtime exception.
- This is basically an argument for why invariance
is preferable to covariance for generics. - Later, well see how to relax this.
-
-
12Example Illustrating type (non) safety
- // Why generic array creation is illegal wont
compile - 1) ListltStringgt stringLists new
ListltStringgt1 // wont compile - 2) ListltIntegergt intList Arrays.asList(42)
- 3) Object objects stringLists
- 4) Objects0 intList
- 5) String s stringLists0.get(0) //
compiler generated cast to String - Suppose 1) compiled (it wont)
- 2) Creates and initializes a ListltIntegergt with
one element - 3) Stores the ListltStringgt object into an Object
array variable, - note, this is legal because arrays are
covariant - 4) Stores the ListltIntegergt into the sole element
of the Object array - this succeeds because generics are
implemented by erasure. - The runtime type is simply List, so there
is no exception - 5) Now, weve stored a ListltIntegergt instance
into an array that is declared - to hold only ListltStringgt instances. So, we
get a ClassCastException -
13Example Reduction Function
- // Reduction without generics, and with
concurrency flaw! - static Object reduce(List list, Function f,
Object initVal) - synchronized (list)
- Object result initVal
- for (Object o list)
- result f.apply(result, o)
-
-
- interface Function
- Object apply (Object arg1, Object arg2)
-
-
- Flaw Calls alien method from synchronized
region - Flaw Doesnt use generics
14Example Continued
- // Reduction without generics or concurrency flaw
- static Object reduce(List list, Function f,
Object initVal) - Object snapshot list.toArray() // Locks
list internally - Object result initVal
- for (Object o snapshot)
- result f.apply(result, o)
-
-
-
- Flaw Still doesnt use generics
15Example Continued
- // Naïve generic version of reduction wont
compile - static ltEgt E reduce(List ltEgt list, Function ltEgt
f, E initVal) - // E snapshot list.toArray() // Wont
compile - E snapshot (E) list.toArray() // Still
generates a warning - E result initVal
- for (E e snapshot)
- result f.apply(result, e)
-
-
- // Generic interface
- interface FunctionltTgt
- T apply (T arg1, T arg2)
-
- Flaw Wont compile without cast
- Flaw Cast still results in warning (that needs
to be suppressed) - Flaw Possible (with minor variations) to get
ClassCastException at runtime
16Example Using Lists instead of Arrays
- // List-based generic reduction
- static ltEgt E reduce(List ltEgt list, Function ltEgt
f, E initVal) - List ltEgt snapshot
- synchronized (list)
- snapshot new ArrayListltEgt(list)
-
- E result initVal
- for (E e snapshot)
- result f.apply(result, e)
-
-
-
- Bonus No possibility of ClassCastException at
runtime
17Item 26 Favor Generic Types
- Parameterize collection declarations
- Use the generic types
- Implementer has to work harder
- But clients have type safety
- Stack example How to support this?
- public static void main (String args)
- StackltStringgt stack new StackltStringgt()
- for (String arg args) stack.push(arg)
- while (!stack.isEmpty()) stack.pop()
-
18Example Converting collection to generics
- public class Stack // Original
Version no generics - private Object elements
- private int size 0
- private static final int CAP 16
-
- public Stack() elements new Object CAP
- public void push( Object e )
- ensureCapacity()
- elements size e
-
- public Object pop()
- if (size 0) throw new ISE()
- Object result elements --size
- elementssize null
- return result
-
- // remainder of Stack omitted See Bloch
19Example Converting collection to generics
- public class Stack ltEgt // First
cut at generics wont work - private E elements //
Alternate 2 Leave as Object - private int size 0
- private static final int CAP 16
-
- public Stack() elements new E CAP
// error generic array creation - // Alternate 1 new (E)
Object CAP // warning - // _at_SuppressWarning(unchecked)
- //public Stack() elements new (E) Object
CAP // warning suppressed -
- public void push( E e )
- ensureCapacity()
- elements size e
-
- public E pop()
- if (size 0) throw new ISE()
- E result elements --size // Error
for Alternate 2 also cast and suppress warning - elementssize null
- return result
20Item 27 Favor Generic Methods
- Just as classes benefit from generics
- So do methods
- Writing generic methods is similar to writing
generic types
21Example Generic method
- // Uses raw types unacceptable! (Item 23)
- public static Set union (Set s1, Set s2)
- Set result new HashSet(s1) //
Generates a warning - result.addAll(s2)
// Generates a warning - return result
-
- // Generic method
- public static ltEgt Set ltEgt union (Set ltEgt s1,
Set ltEgt s2) - Set ltEgt result new HashSet ltEgt (s1)
- result.addAll(s2)
- return result
-
- // The first ltEgt is the type parameter list
- // Example from the java.util.Collection
- // The generics can get a bit redundant
- Map ltString, ListltStringgtgt anagrams new
HashMapltString, ListltStringgtgt() -
22Example Recursive Type Bound
- // Returns the maximum value in a list uses
recursive type bound - public static ltT extends ComparableltTgtgt T max
(List ltTgt list) - Iterator ltTgt i list.iterator()
- T result i.next()
- while (i.hasNext())
- T t i.next() // Note no need for
a cast - if (t.compareTo(result) gt 0)
- result t
-
- return result
-
- Questions
- What happens if the list is empty?
- What does a Liskov contract look like for this
method? - Note the type parameter ltT extends
ComparableltTgtgt - Note the return type T
-
23Item 28 Use bounded wildcards to increase API
Flexibility
- public class Stack ltEgt // First cut at
generics wont work - public Stack()
- public void push( E e )
- public E pop()
- public boolean isEmpty()
-
- // pushAll method without a wildcard type
deficient! - public void pushAll( IterableltEgt src)
- for (E e src) push(e)
-
- // wildcard type for parameter that serves as
an E producer - public void pushAll( Iterablelt? extends Egt
src) - for (E e src) push(e)
-
- // wildcard type for parameter that serves as an
E consumer - public void popAll ( Collectionlt? super Egt
dst) - while (!isEmpty()) dst.add(pop())
-
24The PECS mnemonic
- // PECS producer extends, consumer super
- // Recall earlier example
- public static ltEgt Set ltEgt union (Set ltEgt s1, Set
ltEgt s2) - // Are parameters consumers or producers? (
Producers, so, extend) - public static ltEgt Set ltEgt union (Set lt? extends
Egt s1, Set lt? extends Egt s2) - // Note that return type should still be SetltEgt,
not Set lt? extends Egt - // otherwise, clients will have to use wildcards
- SetltIntegergt integers
- SetltDoublegt doublse
- SetltNumbergt numbers union ( integers, doubles)
// compiler error - SetltNumbergt numbers union.ltNumbergt ( integers,
doubles) // type parameter works - // max example
25Item 29 Consider typesafe heterogeneous
Containers
- // Typesafe heterogeneous container pattern
implementation - public class Favorites
- private MapltClasslt?gt, Objectgt favorites new
HashMap(ltClasslt?gt, Objectgt() - public ltTgt void putFavorite(ClassltTgt type, T
instance) - if (type null) throw new NPE
- favorites.put (type, instance)
-
- public ltTgt T getFavorite(ClassltTgt type)
- return type.cast(favorites.get(type))
-
- // Fairly subtle stuff