Mastering Generics in Effective Java: A Practical Guide for Java Programmers

undefined
 
Effective Java: 3
rd
 Edition
Generics
 
Last Updated: Spring 2023
 
Agenda
 
Material From Joshua Bloch
Effective Java: Programming Language Guide
Cover Items 26-31, 33
“Generics” Chapter
Bottom Line:
Generics are safer, than raw types
But generics are also more complex
Raw types are allowed for backwards
compatibility
 
 
Item 26: Don’t 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
List<E>
 is a 
generic
 interface
List<String>
 is a parameterized type
String
 is the actual type parameter corresponding to 
E
 
 
Example: Replacing raw types
 
// Now a raw collection type – 
don’t do this
   private final Collection stamps = …; // Contains only Stamps
// Erroneous insertion of coin into stamp collection
   stamps.add(new Coin(…));   // Oops!  We’re set up for ClassCastException later
 
// Parameterized collection type - typesafe
     private final Collection<Stamp> stamps = …;
     
stamps.add(new Coin(…));  // result is instead a 
compile
 time error, which is 
good
 
// Now a raw iterator type – 
don’t 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 (explicit) cast
       …// Do something with the stamp
}
 
Example: Mixing generic and raw
types
 
// Uses raw type (List) – 
fails at runtime
   public static void main(String[] args) {
      List<String> strings = new ArrayList<String>();
      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.java:10: 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 won’t compile (see Item 25)
   private static void unsafeAdd( 
List<Object> list
, Object o) { list.add(o);}
 
Example: Using Wildcards
 
// 
Use of raw type for unknown element type – don’t 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 (Set<?> s1, Set<?> s2) {
      int result = 0;
      for (Object o1: s1)
         { if (s2.contains(o1)) result ++; }
      return result;
   }
 
// We’ll revisit this type of example in Item 27
 
 
Example: Using Wildcards
 
 
// Do the question marks really buy you anything?
// Answer: Wildcard is typesafe,
// because you can’t add *anything* (except null) to Collection<?>
 
// Two exceptions:  Raw types ok in
1)
Class Literals:  List.class, not List<String>.class
2)
instanceof operator
   if (o instanceof Set) {   // raw type ok
     Set<?> m = (Set<?>) o;  // Wildcard type
 
// Why the exceptions?  Compatibility with old Java
 
 
 
 
Terminology
 
Term
    
Example
   
Item
Parameterized type
  
List<String>
   
Item 23
Actual type parameter
  
String
    
Item 23
Generic type
   
List<E>
    
Items 23, 26
Formal type parameter
  
E
    
Item 23
Unbounded wildcard type 
 
List<?>
    
Item 23
Raw type
   
List
    
Item 23
Bounded type parameter
 
<E extends Number>
  
Item 26
Recursive type bound
  
<T extends Comparable<T>>
 
Item 27
Bounded wildcard type
  
List<? extends Number>
 
Item 28
Generic method
   
static <E> List<E> asList(E[] a)
 
Item 27
Type token
   
String.class
   
Item 29
 
 
 
 
Item 27: 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 
@SuppressWarnings
 annotation
Some are easy:
 
Set<Lark> exaltation = new HashSet();              // warning
  Set<Lark> exaltation = new HashSet <Lark>();  // no warning
 
Example: Suppressing
Warnings
 
public <T> T[] toArray (T[] a) {
    if (a.length < size)
       return (T[]) Arrays.copyOf(elements, size, a.getClass());
    System.arraycopy(elements, 0, a, 0, size);
    if (a.length > size)  a[size] = null;
    return a; }
The compiler generates a warning:
  ArrayList.java:305: warning [unchecked] unchecked cast
  found   : Object[], required T[]
      return (T[]) Arrays.copyOf(elements, size, a.getClass());
Suppressing the warning:
if (a.length < size) {
    // This cast is correct because the array we’re creating
    // is of the same type as the one passed in, which is T[]
    @SuppressWarnings(“unchecked”)
      T[] result = (T[]) Arrays.copyOf(elements, size, a.getClass());
      return result; }
 
Item 28: Prefer Lists to Arrays
 
Lists play well with generics
Generic array creation not typesafe (hence illegal)
No new List<E>[], new List<String>[] , or new E[]
Arrays are covariant; generics are invariant
If Sub is a subtype of Super
Then Sub[] is a subtype of Super[]
But List<Sub> is 
not
 a subtype of List<Super>
Arrays are reified; generics are erased
Generics are compile time only
 
Example: Covariance vs.
Invariance
 
 
// Fails at runtime
Object[] objectArray = new Long[1];
objectArray[0] = “I don’t fit in!”;           // Throws ArrayStoreException
 
 
// Won’t compile
List<Object> o1 = new ArrayList<Long>();
o1.add(“I don’t 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, we’ll see how to relax this.
 
 
 
 
 
Example: Illustrating type (non)
safety
 
 
// Why generic array creation is illegal – won’t compile
1) List<String>[] stringLists = new List<String>[1];  // won’t compile
2) List<Integer> intList = Arrays.asList(42);
3) Object[] objects = stringLists;
4) objects[0] = intList;
5) String s = stringLists[0].get(0);    // compiler generated cast to String
 
Suppose 1) compiled (it won’t)
2) Creates and initializes  a List<Integer> with one element
3) Stores the List<String> object into an Object array variable,
    note, this is legal because arrays are covariant
4) Stores the List<Integer> 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, we’ve stored a List<Integer> instance into an array that is declared
    to hold only List<String> instances.  So, we get a ClassCastException
 
 
 
Example: Chooser class
 
 
// 
Chooser – a class badly in need of generics
 
public class Chooser {
   private final Object[] choiceArray;
 
   public Chooser(Collection choices) {
      choiceArray = choices.toArray();
   }
 
   public Object choose() {
      Random rnd = ThreadLocalRandom.current();
      return choiceArrary[rnd.nextInt(choiceArray.length)];
   }
}
Flaw: client must always cast return value; hence no type safety
Flaw: what if collection is empty?
 
 
 
Example: First cut at fixing
 
 
// 
A first cut at making Chooser generic – won’t compile
 
public class Chooser 
<T>
 {
   private final 
T
[] choiceArray;
 
   public Chooser(Collection
<T> 
choices) {
      choiceArray = choices.toArray();
      // could fix with choiceArray = 
(T[]) 
choices.toArray();
   }
   public 
T
 choose() {
      Random rnd = ThreadLocalRandom.current();
      return choiceArrary[rnd.nextInt(choiceArray.length)];
   }
}
Compiler objects to “choiceArray = choices.toArray()”;
Fix still yields a warning
 
 
 
Example: prefer lists to arrays
 
// 
List-based chooser – typesafe (plus handling the empty case well)
 
// abstract invariant – Chooser objects are never empty
public class Chooser<T> {
   private final 
List<T> 
choiceList;
   // rep-inv:  choiceList is not empty
 
    // reject empty collections to establish rep invariant
   public Chooser(Collection choices) {
      if (choices.size() == 0) throw new IAE(…);
      choiceList = 
new ArrayList<>(choices);
   }
 
   // now this method is total
   public 
T 
choose() {
      Random rnd = ThreadLocalRandom.current();
      return 
choiceList.get(rnd.nextInt(choiceList.size()));
   }
}
 
 
 
 
Item 29: 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) {
      Stack<String> stack = new Stack<String>();
      for (String arg: args) { stack.push(arg);}
      while (!stack.isEmpty()) { …stack.pop()…}
 }
 
Example: 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];
      elements[size] = null;
      return result;
   }
 
   // remainder of Stack omitted – See Bloch
 
Example: Converting collection to
generics
 
public class Stack 
<E>
 {                 
// First cut at generics – won’t 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
  // @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
      elements[size] = null;
      return result;
   }
 
Item 30: Favor Generic
Methods
 
Just as classes benefit from generics
So do methods
Writing generic methods is similar to
writing generic types
 
Example: 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 <E> Set <E> union (Set <E> s1, Set  <E> s2)  {
   Set <E> result = new HashSet <E> (s1);
   result.addAll(s2);
   return result;
}
 
// The first <E> is the type parameter list
 
// Example from the java.util.Collection
// The generics can get a bit redundant…
Map <String, List<String>> anagrams = new HashMap<String, List<String>>();
 
Example: Recursive Type
Bound (1)
 
/*
  * Returns the maximum value in a list
  * what must be true of type T?
  * what exceptions are thrown and when?
  */
public  static <?????????>  T  max (List <T> list)
 
This is a great example because it is *so* simple.
We’re just finding the max value!
 
Yet the corner cases are tricky.
It’s one of the rare places in 
Effective Java
 where Bloch errs.
 
 
 
Example: Recursive Type
Bound (2)
 
/*
  * what must be true of type T?
  */
public  static <?????????>  T  max (List <T> list)
 
Example: Recursive Type
Bound (3)
 
/*
  * what exceptions are thrown and when?
  */
public  static <T extends Comparable<T>>  T  max (List <T> list)
 
 
   @throws NPE if
 
 
   @throws NSEE (or IAE) if
 
   @throws CCE if
 
Example: Recursive Type
Bound (4)
 
/*
  * what exceptions are thrown and when?
  */
public  static <T extends Comparable<T>>  T  max (List <T> list)
 
 
   @throws NPE if  list is null or contains null values
 
 
   @throws NSEE (or IAE) if list is empty
 
   @throws CCE if list contains mutually incomparable objects
 
Example: Recursive Type
Bound (5)
 
// Returns the maximum value in a list – uses recursive type bound
public  static <T extends Comparable<T>>  T  max (List <T> list) {
   Iterator <T> i = list.iterator();
   T result = i.next();
   while (i.hasNext()) {
       T t = i.next();       // Note: no need for a cast
       if (t.compareTo(result) > 0)
           result = t;
   }
   return result;
}
 
// Bloch’s solution:  How does he do on the contract?
 
Example: Recursive Type
Bound (6)
 
// Returns the maximum value in a list – uses recursive type bound
public  static <T extends Comparable<T>>  T  max (List <T> list) {
   if (list.size() == 0) throw new NSEE(…);   // or IAE
   T result = list.get(0);
 
   for (T t : list) {   // simpler code, slightly less efficient, but correct!
       if (t.compareTo(result) > 0)
           result = t;
   }
   return result;
}
 
// One way to correct Bloch’s solution
 
Item 31: Use bounded wildcards
to increase API Flexibility
 
public class Stack 
<E>
 {         
// First cut at generics – won’t work
   public Stack()
   public void push( 
E
 e )
   public 
E
 pop()
   public boolean isEmpty()
}
 
  //  
pushAll method without a wildcard type – deficient!
      public void pushAll( 
Iterable<E>
 src) {
         for (E e : src) { push(e); }
      }
 //  
wildcard type for parameter that serves as an E producer
      public void pushAll( 
Iterable<? extends E>
 src) {
         for (E e : src) { push(e); }
      }
// 
wildcard type for parameter that serves as an E consumer
     public void popAll ( 
Collection<? super E>
 dst) {
         while (!isEmpty()) { dst.add(pop()); }
    }
 
The PECS mnemonic
 
// 
PECS – producer extends, consumer super
// Recall earlier example
public static <E> Set <E> union (Set <E> s1, Set  <E> s2)
 
// Are parameters consumers or producers? ( 
Producers
, so, extend)
 
public static <E> Set <E> union (Set <? extends E> s1, Set  <? extends E> s2)
 
// Note that return type should still be Set<E>, not Set <? extends E>
// otherwise, clients will have to use wildcards…
 
 
Set<Integer> integers = …
Set<Double> doublse = …
Set<Number> numbers = union ( integers, doubles);  // compiler error
 
Set<Number> numbers = union
.<Number>
 ( integers, doubles);  // type parameter works
 
// max example
public static <T extends Comparable<T>>            T max (List <T>              list )   // original
public static <T extends Comparable<? super T>> T max (List<? extends T> list)   // PECS
 
 
 
Item 33: Consider typesafe
heterogeneous Containers
 
// 
Typesafe heterogeneous container pattern – implementation
public class Favorites
 
   private 
Map<Class<?>, Object> favorites
 = new HashMap(<Class<?>, Object>();
 
   public <T> void putFavorite(Class<T> type, T instance) {
       if (type == null) { throw new NPE… }
       favorites.put (type, instance);
   }
 
   public <T> T getFavorite(Class<T> type) {
        return 
type.cast(favorites.get(type));
   }
 
// Fairly subtle stuff…
 
 
 
 
Slide Note
Embed
Share

Explore the world of generics in Java through Joshua Bloch's guidance in "Effective Java". Learn the importance of avoiding raw types, leveraging parameterized types for type safety, handling mixing of generic and raw types, and implementing wildcards for flexible and typesafe coding. Enhance your Java programming skills with key insights and examples provided in this comprehensive material.

  • Java programming
  • Generics
  • Effective Java
  • Joshua Bloch
  • Parameterized types

Uploaded on Sep 30, 2024 | 0 Views


Download Presentation

Please find below an Image/Link to download the presentation.

The content on the website is provided AS IS for your information and personal use only. It may not be sold, licensed, or shared on other websites without obtaining consent from the author. Download presentation by click this link. If you encounter any issues during the download, it is possible that the publisher has removed the file from their server.

E N D

Presentation Transcript


  1. Effective Java: 3rdEdition Generics Last Updated: Spring 2023

  2. Agenda Material From Joshua Bloch Effective Java: Programming Language Guide Cover Items 26-31, 33 Generics Chapter Bottom Line: Generics are safer, than raw types But generics are also more complex Raw types are allowed for backwards compatibility 2 Generics

  3. Item 26: 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 List<E> is a generic interface List<String> is a parameterized type String is the actual type parameter corresponding to E 3 Generics

  4. Example: Replacing raw types // Now a raw collection type don t do this private final Collection stamps = ; // Contains only Stamps // Erroneous insertion of coin into stamp collection stamps.add(new Coin( )); // Oops! We re set up for ClassCastException later // Parameterized collection type - typesafe private final Collection<Stamp> stamps = ; stamps.add(new Coin( )); // result is instead a compile time error, which is good // Now a raw iterator type don t 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 (explicit) cast // Do something with the stamp } 4 Generics

  5. Example: Mixing generic and raw types // Uses raw type (List) fails at runtime public static void main(String[] args) { List<String> strings = new ArrayList<String>(); 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.java:10: 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 won t compile (see Item 25) private static void unsafeAdd( List<Object> list, Object o) { list.add(o);} 5 Generics

  6. Example: Using Wildcards // Use of raw type for unknown element type don t 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 (Set<?> s1, Set<?> s2) { int result = 0; for (Object o1: s1) { if (s2.contains(o1)) result ++; } return result; } // We ll revisit this type of example in Item 27 6 Generics

  7. Example: Using Wildcards // Do the question marks really buy you anything? // Answer: Wildcard is typesafe, // because you can t add *anything* (except null) to Collection<?> // Two exceptions: Raw types ok in Class Literals: List.class, not List<String>.class instanceof operator if (o instanceof Set) { // raw type ok Set<?> m = (Set<?>) o; // Wildcard type 1) 2) // Why the exceptions? Compatibility with old Java 7 Generics

  8. Terminology Term Parameterized type Actual type parameter Generic type Formal type parameter Unbounded wildcard type Raw type Bounded type parameter Recursive type bound Bounded wildcard type Generic method Type token Example List<String> String List<E> E List<?> List <E extends Number> <T extends Comparable<T>> List<? extends Number> static <E> List<E> asList(E[] a)Item 27 String.class Item Item 23 Item 23 Items 23, 26 Item 23 Item 23 Item 23 Item 26 Item 27 Item 28 Item 29 8 Generics

  9. Item 27: 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 @SuppressWarnings annotation Some are easy: Set<Lark> exaltation = new HashSet(); // warning Set<Lark> exaltation = new HashSet <Lark>(); // no warning 9 Generics

  10. Example: Suppressing Warnings public <T> T[] toArray (T[] a) { if (a.length < size) return (T[]) Arrays.copyOf(elements, size, a.getClass()); System.arraycopy(elements, 0, a, 0, size); if (a.length > size) a[size] = null; return a; } The compiler generates a warning: ArrayList.java:305: warning [unchecked] unchecked cast found : Object[], required T[] return (T[]) Arrays.copyOf(elements, size, a.getClass()); Suppressing the warning: if (a.length < size) { // This cast is correct because the array we re creating // is of the same type as the one passed in, which is T[] @SuppressWarnings( unchecked ) T[] result = (T[]) Arrays.copyOf(elements, size, a.getClass()); return result; } 10 Generics

  11. Item 28: Prefer Lists to Arrays Lists play well with generics Generic array creation not typesafe (hence illegal) No new List<E>[], new List<String>[] , or new E[] Arrays are covariant; generics are invariant If Sub is a subtype of Super Then Sub[] is a subtype of Super[] But List<Sub> is not a subtype of List<Super> Arrays are reified; generics are erased Generics are compile time only 11 Generics

  12. Example: Covariance vs. Invariance // Fails at runtime Object[] objectArray = new Long[1]; objectArray[0] = I don t fit in! ; // Throws ArrayStoreException // Won t compile List<Object> o1 = new ArrayList<Long>(); o1.add( I don t 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, we ll see how to relax this. 12 Generics

  13. Example: Illustrating type (non) safety // Why generic array creation is illegal won t compile 1) List<String>[] stringLists = new List<String>[1]; // won t compile 2) List<Integer> intList = Arrays.asList(42); 3) Object[] objects = stringLists; 4) objects[0] = intList; 5) String s = stringLists[0].get(0); // compiler generated cast to String Suppose 1) compiled (it won t) 2) Creates and initializes a List<Integer> with one element 3) Stores the List<String> object into an Object array variable, note, this is legal because arrays are covariant 4) Stores the List<Integer> 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, we ve stored a List<Integer> instance into an array that is declared to hold only List<String> instances. So, we get a ClassCastException 13 Generics

  14. Example: Chooser class // Chooser a class badly in need of generics public class Chooser { private final Object[] choiceArray; public Chooser(Collection choices) { choiceArray = choices.toArray(); } public Object choose() { Random rnd = ThreadLocalRandom.current(); return choiceArrary[rnd.nextInt(choiceArray.length)]; } } Flaw: client must always cast return value; hence no type safety Flaw: what if collection is empty? 14 Generics

  15. Example: First cut at fixing // A first cut at making Chooser generic won t compile public class Chooser <T> { private final T[] choiceArray; public Chooser(Collection<T> choices) { choiceArray = choices.toArray(); // could fix with choiceArray = (T[]) choices.toArray(); } public T choose() { Random rnd = ThreadLocalRandom.current(); return choiceArrary[rnd.nextInt(choiceArray.length)]; } } Compiler objects to choiceArray = choices.toArray() ; Fix still yields a warning 15 Generics

  16. Example: prefer lists to arrays // List-based chooser typesafe (plus handling the empty case well) // abstract invariant Chooser objects are never empty public class Chooser<T> { private final List<T> choiceList; // rep-inv: choiceList is not empty // reject empty collections to establish rep invariant public Chooser(Collection choices) { if (choices.size() == 0) throw new IAE( ); choiceList = new ArrayList<>(choices); } // now this method is total public T choose() { Random rnd = ThreadLocalRandom.current(); return choiceList.get(rnd.nextInt(choiceList.size())); } } 16 Generics

  17. Item 29: 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) { Stack<String> stack = new Stack<String>(); for (String arg: args) { stack.push(arg);} while (!stack.isEmpty()) { stack.pop() } } 17 Generics

  18. Example: 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]; elements[size] = null; return result; } // remainder of Stack omitted See Bloch 18 Generics

  19. Example: Converting collection to generics public class Stack <E> { // First cut at generics won t 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 // @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 elements[size] = null; return result; } 19 Generics

  20. Item 30: Favor Generic Methods Just as classes benefit from generics So do methods Writing generic methods is similar to writing generic types 20 Generics

  21. Example: 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 <E> Set <E> union (Set <E> s1, Set <E> s2) { Set <E> result = new HashSet <E> (s1); result.addAll(s2); return result; } // The first <E> is the type parameter list // Example from the java.util.Collection // The generics can get a bit redundant Map <String, List<String>> anagrams = new HashMap<String, List<String>>(); 21 Generics

  22. Example: Recursive Type Bound (1) /* * Returns the maximum value in a list * what must be true of type T? * what exceptions are thrown and when? */ public static <?????????> T max (List <T> list) This is a great example because it is *so* simple. We re just finding the max value! Yet the corner cases are tricky. It s one of the rare places in Effective Java where Bloch errs. 22 Generics

  23. Example: Recursive Type Bound (2) /* * what must be true of type T? */ public static <?????????> T max (List <T> list) 23 Generics

  24. Example: Recursive Type Bound (3) /* * what exceptions are thrown and when? */ public static <T extends Comparable<T>> T max (List <T> list) @throws NPE if @throws NSEE (or IAE) if @throws CCE if 24 Generics

  25. Example: Recursive Type Bound (4) /* * what exceptions are thrown and when? */ public static <T extends Comparable<T>> T max (List <T> list) @throws NPE if list is null or contains null values @throws NSEE (or IAE) if list is empty @throws CCE if list contains mutually incomparable objects 25 Generics

  26. Example: Recursive Type Bound (5) // Returns the maximum value in a list uses recursive type bound public static <T extends Comparable<T>> T max (List <T> list) { Iterator <T> i = list.iterator(); T result = i.next(); while (i.hasNext()) { T t = i.next(); // Note: no need for a cast if (t.compareTo(result) > 0) result = t; } return result; } // Bloch s solution: How does he do on the contract? 26 Generics

  27. Example: Recursive Type Bound (6) // Returns the maximum value in a list uses recursive type bound public static <T extends Comparable<T>> T max (List <T> list) { if (list.size() == 0) throw new NSEE( ); // or IAE T result = list.get(0); for (T t : list) { // simpler code, slightly less efficient, but correct! if (t.compareTo(result) > 0) result = t; } return result; } // One way to correct Bloch s solution 27 Generics

  28. Item 31: Use bounded wildcards to increase API Flexibility public class Stack <E> { // First cut at generics won t work public Stack() public void push( E e ) public E pop() public boolean isEmpty() } // pushAll method without a wildcard type deficient! public void pushAll( Iterable<E> src) { for (E e : src) { push(e); } } // wildcard type for parameter that serves as an E producer public void pushAll( Iterable<? extends E> src) { for (E e : src) { push(e); } } // wildcard type for parameter that serves as an E consumer public void popAll ( Collection<? super E> dst) { while (!isEmpty()) { dst.add(pop()); } } 28 Generics

  29. The PECS mnemonic // PECS producer extends, consumer super // Recall earlier example public static <E> Set <E> union (Set <E> s1, Set <E> s2) // Are parameters consumers or producers? ( Producers, so, extend) public static <E> Set <E> union (Set <? extends E> s1, Set <? extends E> s2) // Note that return type should still be Set<E>, not Set <? extends E> // otherwise, clients will have to use wildcards Set<Integer> integers = Set<Double> doublse = Set<Number> numbers = union ( integers, doubles); // compiler error Set<Number> numbers = union.<Number> ( integers, doubles); // type parameter works // max example public static <T extends Comparable<T>> T max (List <T> list ) // original public static <T extends Comparable<? super T>> T max (List<? extends T> list) // PECS 29 Generics

  30. Item 33: Consider typesafe heterogeneous Containers // Typesafe heterogeneous container pattern implementation public class Favorites private Map<Class<?>, Object> favorites = new HashMap(<Class<?>, Object>(); public <T> void putFavorite(Class<T> type, T instance) { if (type == null) { throw new NPE } favorites.put (type, instance); } public <T> T getFavorite(Class<T> type) { return type.cast(favorites.get(type)); } // Fairly subtle stuff 30 Generics

More Related Content

giItT1WQy@!-/#giItT1WQy@!-/#giItT1WQy@!-/#giItT1WQy@!-/#giItT1WQy@!-/#giItT1WQy@!-/#