Successfully reported this slideshow.
We use your LinkedIn profile and activity data to personalize ads and to show you more relevant ads. You can change your ad preferences anytime.

Effective Java - Still Effective After All These Years

8,808 views

Published on

Joshua Bloch serves up a few Java Puzzlers as an appetizer before and as dessert after the main course on Effective Java.

Organized By: Silicon Valley Web JUG - Van Riper and Kevin Nilson
Hosted and Sponsored By: Google
Video By: Marakana

Effective Java - Still Effective After All These Years

  1. 1. Effective Java TM : Still Effective, After All These Years (+ Appetizers and Dessert) Joshua Bloch
  2. 2. Appetizers
  3. 3. Fraser 1908
  4. 4. Fraser 1908
  5. 5. Fraser 1908
  6. 6. Todorovic 1997
  7. 7. Todorovic 1997
  8. 8. Todorovic 1997
  9. 9. 1. “Life’s Persistent Questions” <ul><li>public class SimpleQuestion { </li></ul><ul><li>static boolean yesOrNo(String s) { </li></ul><ul><li>s = s.toLowerCase(); </li></ul><ul><li>if (s.equals(&quot;yes&quot;) || s.equals(&quot;y&quot;) || s.equals(&quot;t&quot;)) </li></ul><ul><li>s = &quot;true&quot;; </li></ul><ul><li>return Boolean.getBoolean(s); </li></ul><ul><li>} </li></ul><ul><li>public static void main(String[] args) { </li></ul><ul><li>System.out.println( </li></ul><ul><li>yesOrNo(&quot;true&quot;) + &quot; &quot; + yesOrNo(&quot;YeS&quot;)); </li></ul><ul><li>} </li></ul><ul><li>} </li></ul>
  10. 10. What Does It Print? <ul><li>public class SimpleQuestion { </li></ul><ul><li>static boolean yesOrNo(String s) { </li></ul><ul><li>s = s.toLowerCase(); </li></ul><ul><li>if (s.equals(&quot;yes&quot;) || s.equals(&quot;y&quot;) || s.equals(&quot;t&quot;)) </li></ul><ul><li>s = &quot;true&quot;; </li></ul><ul><li>return Boolean.getBoolean(s); </li></ul><ul><li>} </li></ul><ul><li>public static void main(String[] args) { </li></ul><ul><li>System.out.println( </li></ul><ul><li>yesOrNo(&quot;true&quot;) + &quot; &quot; + yesOrNo(&quot;YeS&quot;)); </li></ul><ul><li>} </li></ul><ul><li>} </li></ul>(a) false false (b) true false (c) true true (d) None of the above
  11. 11. <ul><li>(a) false false </li></ul><ul><li>(b) true false </li></ul><ul><li>(c) true true </li></ul><ul><li>(d) None of the above </li></ul><ul><li>The Boolean.getBoolean method does not do what you think it does </li></ul>What Does It Print?
  12. 12. What does Boolean.getBoolean do? <ul><li>public static boolean getBoolean(String name) </li></ul><ul><ul><li>Returns true if and only if the system property named by the argument exists and is equal to the string  &quot;true&quot; . (Beginning with version 1.0.2 of the Java platform, the test of this string is case insensitive.) A system property is accessible through getProperty, a method defined by the System class. </li></ul></ul><ul><ul><li>If there is no property with the specified name, or if the specified name is empty or null, then false is returned. </li></ul></ul>
  13. 13. Another Look <ul><li>public class SimpleQuestion { </li></ul><ul><li>static boolean yesOrNo(String s) { </li></ul><ul><li>s = s.toLowerCase(); </li></ul><ul><li>if (s.equals(&quot;yes&quot;) || s.equals(&quot;y&quot;) || s.equals(&quot;t&quot;)) </li></ul><ul><li>s = &quot;true&quot;; </li></ul><ul><li>return Boolean.getBoolean(s); // Ouch! </li></ul><ul><li>} </li></ul><ul><li>public static void main(String[] args) { </li></ul><ul><li>System.out.println( </li></ul><ul><li>yesOrNo(&quot;true&quot;) + &quot; &quot; + yesOrNo(&quot;YeS&quot;)); </li></ul><ul><li>} </li></ul><ul><li>} </li></ul>
  14. 14. You Could Fix it Like This... <ul><li>public class SimpleQuestion { </li></ul><ul><li>static boolean yesOrNo(String s) { </li></ul><ul><li>s = s.toLowerCase(); </li></ul><ul><li>if (s.equals(&quot;yes&quot;) || s.equals(&quot;y&quot;) || s.equals(&quot;t&quot;)) </li></ul><ul><li>s = &quot;true&quot;; </li></ul><ul><li>return Boolean. parse Boolean(s); </li></ul><ul><li>} </li></ul><ul><li>public static void main(String[] args) { </li></ul><ul><li>System.out.println( </li></ul><ul><li>yesOrNo(&quot;true&quot;) + &quot; &quot; + yesOrNo(&quot;YeS&quot;)); </li></ul><ul><li>} </li></ul><ul><li>} </li></ul>
  15. 15. But This is Even Better... <ul><li>public class SimpleQuestion { </li></ul><ul><li>static boolean yesOrNo(String s) { </li></ul><ul><li>s = s.toLowerCase(); </li></ul><ul><li>return s.equals(&quot;yes&quot;) || s.equals(&quot;y&quot;) || </li></ul><ul><li>s.equals(&quot;true&quot;)|| s.equals(&quot;t&quot;); </li></ul><ul><li>} </li></ul><ul><li>public static void main(String[] args) { </li></ul><ul><li>System.out.println( </li></ul><ul><li>yesOrNo(&quot;true&quot;) + &quot; &quot; + yesOrNo(&quot;YeS&quot;)); </li></ul><ul><li>} </li></ul><ul><li>} </li></ul>
  16. 16. The Moral <ul><li>Strange and terrible methods lurk in libraries </li></ul><ul><ul><li>Some have innocuous sounding names </li></ul></ul><ul><li>If your code misbehaves </li></ul><ul><ul><li>Make sure you’re calling the right methods </li></ul></ul><ul><ul><li>Read the library documentation </li></ul></ul><ul><li>For API designers </li></ul><ul><ul><li>Don’t violate principle of least astonishment </li></ul></ul><ul><ul><li>Don’t violate the abstraction hierarchy </li></ul></ul><ul><ul><li>Don’t use similar names for wildly different behaviors </li></ul></ul>
  17. 17. <ul><li>public class Searching { </li></ul><ul><li>public static void main(String[] args) { </li></ul><ul><li>String[] strings = { &quot;0&quot;, &quot;1&quot;, &quot;2&quot;, &quot;3&quot;, &quot;4&quot;, &quot;5&quot; }; </li></ul><ul><li>// Translate String array into List of Integer </li></ul><ul><li>List<Integer> integers = new ArrayList<Integer>(); </li></ul><ul><li>for (String s : strings) </li></ul><ul><li>integers.add(Integer.valueOf(s)); </li></ul><ul><li>System.out.println( </li></ul><ul><li>Collections.binarySearch(integers, 1, cmp)); </li></ul><ul><li>} </li></ul><ul><li>static Comparator<Integer> cmp = new Comparator<Integer>() { </li></ul><ul><li>public int compare(Integer i, Integer j) { </li></ul><ul><li>return i < j ? -1 : (i == j ? 0 : 1); </li></ul><ul><li>} </li></ul><ul><li>}; </li></ul><ul><li>} </li></ul>2. “Searching for the One”
  18. 18. <ul><li>public class Searching { </li></ul><ul><li>public static void main(String[] args) { </li></ul><ul><li>String[] strings = { &quot;0&quot;, &quot;1&quot;, &quot;2&quot;, &quot;3&quot;, &quot;4&quot;, &quot;5&quot; }; </li></ul><ul><li>// Translate String array into List of Integer </li></ul><ul><li>List<Integer> integers = new ArrayList<Integer>(); </li></ul><ul><li>for (String s : strings) </li></ul><ul><li>integers.add(Integer.valueOf(s)); </li></ul><ul><li>System.out.println( </li></ul><ul><li>Collections.binarySearch(integers, 1, cmp)); </li></ul><ul><li>} </li></ul><ul><li>static Comparator<Integer> cmp = new Comparator<Integer>() { </li></ul><ul><li>public int compare(Integer i, Integer j) { </li></ul><ul><li>return i < j ? -1 : (i == j ? 0 : 1); </li></ul><ul><li>} </li></ul><ul><li>}; </li></ul><ul><li>} </li></ul>What Does It Print? (a) 0 (b) 1 (c) -2 (d) None of the above
  19. 19. <ul><li>(a) 0 </li></ul><ul><li>(b) 1 </li></ul><ul><li>(c) -2 (in practice) </li></ul><ul><li>(d) None of the above: unspecified (in theory) </li></ul><ul><li>The Comparator is broken; autoboxing is tricky. </li></ul>What Does It Print?
  20. 20. <ul><li>public class Searching { </li></ul><ul><li>public static void main(String[] args) { </li></ul><ul><li>String[] strings = { &quot;0&quot;, &quot;1&quot;, &quot;2&quot;, &quot;3&quot;, &quot;4&quot;, &quot;5&quot; }; </li></ul><ul><li>// Translate String array into List of Integer </li></ul><ul><li>List<Integer> integers = new ArrayList<Integer>(); </li></ul><ul><li>for (String s : strings) </li></ul><ul><li>integers.add(Integer.valueOf(s)); </li></ul><ul><li>System.out.println( </li></ul><ul><li>Collections.binarySearch(integers, 1, cmp)); </li></ul><ul><li>} </li></ul><ul><li>static Comparator<Integer> cmp = new Comparator<Integer>() { </li></ul><ul><li>public int compare(Integer i, Integer j) { </li></ul><ul><li>return i < j ? -1 : ( i == j ? 0 : 1); </li></ul><ul><li>} </li></ul><ul><li>}; </li></ul><ul><li>} </li></ul>Another Look
  21. 21. <ul><li>static Comparator<Integer> cmp = new Comparator<Integer>() { </li></ul><ul><li>public int compare(Integer i, Integer j) { </li></ul><ul><li>return i < j ? -1 : (i > j ? 1 : 0) ; </li></ul><ul><li>} </li></ul><ul><li>}; </li></ul><ul><li>static Comparator<Integer> cmp = new Comparator<Integer>() { </li></ul><ul><li>public int compare(Integer i, Integer j) { </li></ul><ul><li>return i < j ? -1 : ( i.equals(j) ? 0 : 1); </li></ul><ul><li>} </li></ul><ul><li>}; </li></ul>You Could Fix it in Either of These Ways
  22. 22. <ul><li>static Comparator<Integer> cmp = new Comparator<Integer>() { </li></ul><ul><li>public int compare(Integer iBoxed, Integer jBoxed) { </li></ul><ul><li>// Unbox arguments to force value comparison </li></ul><ul><li>int i = iBoxed; </li></ul><ul><li>int j = jBoxed; </li></ul><ul><li>return i < j ? -1 : (i == j ? 0 : 1); </li></ul><ul><li>} </li></ul><ul><li>}; </li></ul>But This is (Arguably) Better
  23. 23. The Moral <ul><li>Autoboxing blurs but does not erase distinction between primitives and boxed primitives </li></ul><ul><li>Only four of the six comparison operators work on boxed primitives </li></ul><ul><ul><li>< , > , <= , and >= work </li></ul></ul><ul><ul><li>== and != do not work! </li></ul></ul><ul><li>It’s very hard to test for broken comparators </li></ul><ul><li>Even Josh Bloch and Neal Gafter make misteaks </li></ul><ul><ul><li>We got this wrong in the solution to Puzzle 65 ( Java Puzzlers , Addison-Wesley, 2005, 1 st Printing) </li></ul></ul>
  24. 24. The Main Course
  25. 25. First Edition, 2001; Second Edition, 2008 What’s New in the Second Edition? <ul><li>Chapter 5: Generics </li></ul><ul><li>Chapter 6: Enums and Annotations </li></ul><ul><li>One or more items on all other Java 5 language features </li></ul><ul><li>Threads chapter renamed Concurrency </li></ul><ul><ul><li>Completely rewritten for java.util.concurrent </li></ul></ul><ul><li>All existing items updated to reflect current best practices </li></ul><ul><li>A few items added to reflect newly important patterns </li></ul><ul><li>First edition had 57 items; second has 78 </li></ul>
  26. 26. Agenda <ul><li>Generics Items 28, 29 </li></ul><ul><li>Enum types Item 40 </li></ul><ul><li>Varargs Item 42 </li></ul><ul><li>Concurrency Item 69 </li></ul><ul><li>Serialization Item 78 </li></ul>
  27. 27. Item 28: Wildcards for API Flexibility <ul><li>Unlike arrays, generic types are invariant </li></ul><ul><ul><li>That is, List<String> is not a subtype of List<Object> </li></ul></ul><ul><ul><li>Good for compile-time type safety, but inflexible </li></ul></ul><ul><li>Wildcard types provide additional API flexibility </li></ul><ul><ul><li>List<String> is a subtype of List<? extends Object> </li></ul></ul><ul><ul><li>List<Object> is a subtype of List<? super String> </li></ul></ul>
  28. 28. A Mnemonic for Wildcard Usage <ul><li>PECS — P roducer e xtends, C onsumer s uper </li></ul><ul><ul><li>For a T producer, use Foo<? extends T> </li></ul></ul><ul><ul><li>For a T consumer, use Foo<? super T> </li></ul></ul><ul><li>Only applies to input parameters </li></ul><ul><ul><li>Don’t use wildcard types as return types </li></ul></ul>Guess who?
  29. 29. Flex your PECS (1) <ul><li>Suppose you want to add bulk methods to Stack<E> </li></ul><ul><li>void pushAll(Collection<E> src); </li></ul><ul><li>void popAll(Collection<E> dst); </li></ul>
  30. 30. Flex your PECS (1) <ul><li>Suppose you want to add bulk methods to Stack<E> </li></ul><ul><li>void pushAll(Collection< ? extends E > src); </li></ul><ul><li>– src is an E producer </li></ul><ul><li>void popAll(Collection<E> dst); </li></ul>
  31. 31. Flex your PECS (1) <ul><li>Suppose you want to add bulk methods to Stack<E> </li></ul><ul><li>void pushAll(Collection< ? extends E > src); </li></ul><ul><li>– src is an E producer </li></ul><ul><li>void popAll(Collection< ? super E > dst); </li></ul><ul><li>– dst is an E consumer </li></ul>
  32. 32. Flex your PECS (1) What does it buy you? <ul><li>void pushAll(Collection< ? extends E > src); </li></ul><ul><li>void popAll(Collection< ? super E > dst); </li></ul><ul><li>Caller can now pushAll from a Collection<Long> or a Collection<Number> onto a Stack<Number> </li></ul><ul><li>Caller can now popAll into a Collection<Object> or a Collection<Number> from a Stack<Number> </li></ul>
  33. 33. Flex your PECS (2) <ul><li>Consider this generic method: </li></ul><ul><ul><li>public static <E> Set<E> union(Set<E> s1, Set<E> s2) </li></ul></ul>
  34. 34. Flex your PECS (2) <ul><li>Consider this generic method </li></ul><ul><ul><li>public static <E> Set<E> union(Set <? extends E> s1, </li></ul></ul><ul><ul><li>Set <? extends E> s2) </li></ul></ul><ul><li>Both s1 and s2 are E producers </li></ul><ul><li>No wildcard type for return value </li></ul><ul><ul><li>Wouldn’t make the API any more flexible </li></ul></ul><ul><ul><li>Would force user to deal with wildcard types explicitly </li></ul></ul><ul><ul><li>User should not have to think about wildcards to use your API </li></ul></ul>
  35. 35. Flex your PECS (2) Truth In Advertising – It Doesn’t Always “Just Work” <ul><li>This code won’t compile  </li></ul><ul><ul><li>Set<Integer> ints = ... ; </li></ul></ul><ul><ul><li>Set<Double> doubles = ... ; </li></ul></ul><ul><ul><li>Set<Number> numbers = union(ints, doubles); </li></ul></ul><ul><li>The compiler says </li></ul><ul><ul><li>Union.java:14: incompatible types </li></ul></ul><ul><ul><li>found : Set <Number & Comparable<? extends Number & </li></ul></ul><ul><ul><li>Comparable<?>>> </li></ul></ul><ul><ul><li>required: Set<Number> </li></ul></ul><ul><ul><li>Set<Number> numbers = union(integers, doubles); </li></ul></ul><ul><ul><li>^ </li></ul></ul><ul><li>The fix – provide an explicit type parameter </li></ul><ul><ul><li>Set<Number> nums = Union.<Number> union(ints, doubles); </li></ul></ul>
  36. 36. Summary, in Tabular Form Foo<?> ( Independent of T ) Foo<? e xtends T> ( Covariant in T ) No Foo<? s uper T> ( Contravariant in T ) Foo<T> ( Invariant in T ) Yes No Yes Parameter C onsumes T Instances? Input Parameter P roduces T Instances?
  37. 37. Filling in The Blanks Foo<?> ( Independent of T ) Foo<? extends T> ( Covariant in T ) No Foo<? super T> ( Contravariant in T ) Foo<T> ( Invariant in T ) Yes No Yes Parameter Consumes T Instances? Parameter Produces T Instances?
  38. 38. Item 29: How to Write A Container With an Arbitrary Number of Type Parameters <ul><li>Typically, containers are parameterized </li></ul><ul><ul><li>For example: Set<E> , Map<K, V> </li></ul></ul><ul><ul><li>Limits you to a fixed number of type parameters </li></ul></ul><ul><li>Sometimes you need more flexibility </li></ul><ul><ul><li>Consider a DatabaseRow class </li></ul></ul><ul><ul><li>You need one type parameter for each column </li></ul></ul><ul><ul><li>Number of columns varies from instance to instance </li></ul></ul>
  39. 39. The Solution: Typesafe Heterogeneous Container Pattern <ul><li>Parameterize selector instead of container </li></ul><ul><ul><li>For DatabaseRow , DatabaseColumn is selector </li></ul></ul><ul><li>Present selector to container to get data </li></ul><ul><li>Data is strongly typed at compile time </li></ul><ul><li>Allows for unlimited type parameters </li></ul>
  40. 40. Example: A Favorites Database API and Client <ul><li>// Typesafe heterogeneous container pattern - API </li></ul><ul><li>public class Favorites { </li></ul><ul><li>public <T> void putFavorite(Class<T> type, T instance); </li></ul><ul><li>public <T> T getFavorite(Class<T> type); </li></ul><ul><li>} </li></ul><ul><li>// Typesafe heterogeneous container pattern - client </li></ul><ul><li>public static void main(String[] args) { </li></ul><ul><li>Favorites f = new Favorites(); </li></ul><ul><li>f.putFavorite(String.class, &quot;Java&quot;); </li></ul><ul><li>f.putFavorite(Integer.class, 0xcafebabe); </li></ul><ul><li>f.putFavorite(Class.class, ThreadLocal.class); </li></ul><ul><li>String s = f.getFavorite(String.class); </li></ul><ul><li>int i = f.getFavorite(Integer.class); </li></ul><ul><li>Class<?> favoriteClass = f.getFavorite(Class.class); </li></ul><ul><li>System.out.println(&quot; printf(&quot;%s %x %s%n&quot;, </li></ul><ul><li>favoriteString, favoriteInteger, favoriteClass); </li></ul><ul><li>} </li></ul>
  41. 41. Example: A Favorites Database Implementation <ul><li>public class Favorites { </li></ul><ul><li>private Map< Class<?>, Object > favorites = </li></ul><ul><li>new HashMap<Class<?>, Object>(); </li></ul><ul><li>public <T> void putFavorite( Class<T> type, T instance ) { </li></ul><ul><li>if (type == null) </li></ul><ul><li>throw new NullPointerException(&quot;Type is null&quot;); </li></ul><ul><li>favorites.put(type, instance); </li></ul><ul><li>} </li></ul><ul><li>public <T> T getFavorite(Class<T> type) { </li></ul><ul><li>return type.cast( favorites.get(type) ) ; </li></ul><ul><li>} </li></ul><ul><li>} </li></ul>
  42. 42. Agenda <ul><li>Generics Items 28, 29 </li></ul><ul><li>Enum types Item 40 </li></ul><ul><li>Varargs Item 42 </li></ul><ul><li>Concurrency Item 69 </li></ul><ul><li>Serialization Item 78 </li></ul>
  43. 43. Item 40: Prefer 2-element enums to booleans <ul><li>Which would you rather see in code, this: </li></ul><ul><li>double temp = thermometer.getTemp( true ); </li></ul><ul><li>or this: </li></ul><ul><li>double temp = thermometer.getTemp( TemperatureScale.FAHRENHEIT ); </li></ul><ul><li>With static import, you can even have this: </li></ul><ul><li>double temp = thermometer.getTemp( FAHRENHEIT ); </li></ul>
  44. 44. Advantages of 2-Element enums Over booleans <ul><li>Code is easier to read </li></ul><ul><li>Code is easier to write (especially with IDE) </li></ul><ul><li>Less need to consult documentation </li></ul><ul><li>Smaller probability of error </li></ul><ul><li>Much better for API evolution </li></ul>
  45. 45. Evolution of a 2-Element enum <ul><li>Version 1 </li></ul><ul><li>public enum TemperatureScale { FAHRENHEIT, CELSIUS } </li></ul><ul><li>Version 2 </li></ul><ul><li>public enum TemperatureScale { FAHRENHEIT, CELSIUS, KELVIN } </li></ul><ul><li>Version 3 </li></ul><ul><li>public enum TemperatureScale { </li></ul><ul><li>FAHRENHEIT, CELSIUS, KELVIN; </li></ul><ul><li>public abstract double toCelsius(double temp); </li></ul><ul><li>... // Implementations of toCelsius omitted </li></ul><ul><li>} </li></ul>
  46. 46. Agenda <ul><li>Generics Items 28, 29 </li></ul><ul><li>Enum types Item 40 </li></ul><ul><li>Varargs Item 42 </li></ul><ul><li>Concurrency Item 69 </li></ul><ul><li>Serialization Item 78 </li></ul>
  47. 47. Item 42: Two Useful Idioms for Varargs <ul><li>// Simple use of varargs </li></ul><ul><li>static int sum(int... args) { </li></ul><ul><li>int sum = 0; </li></ul><ul><li>for (int arg : args) </li></ul><ul><li>sum += arg; </li></ul><ul><li>return sum; </li></ul><ul><li>} </li></ul>
  48. 48. Suppose You Want to Require at Least One Argument <ul><li>// The WRONG way to require one or more arguments! </li></ul><ul><li>static int min(int... args) { </li></ul><ul><li>if (args.length == 0) </li></ul><ul><li>throw new IllegalArgumentException( </li></ul><ul><li>&quot;Too few arguments&quot;); </li></ul><ul><li>int min = args[0]; </li></ul><ul><li>for (int i = 1; i < args.length; i++) </li></ul><ul><li>if (args[i] < min) </li></ul><ul><li>min = args[i]; </li></ul><ul><li>return min; </li></ul><ul><li>} </li></ul><ul><li>Fails at runtime if invoked with no arguments </li></ul><ul><li>It's ugly – explicit validity check on number of args </li></ul><ul><li>Interacts poorly with for-each loop </li></ul>
  49. 49. The Right Way <ul><li>static int min( int firstArg, int... remainingArgs ) { </li></ul><ul><li>int min = firstArg; </li></ul><ul><li>for (int arg : remainingArgs) </li></ul><ul><li>if (arg < min) </li></ul><ul><li>min = arg; </li></ul><ul><li>return min; </li></ul><ul><li>} </li></ul><ul><li>Won’t compile if you try to invoke with no arguments </li></ul><ul><li>No validity check necessary </li></ul><ul><li>Works great with for-each loop </li></ul>
  50. 50. Varargs when Performance is Critical <ul><li>// These static factories are real </li></ul><ul><li>Class EnumSet<E extends Enum<E>> { </li></ul><ul><li>static <E> EnumSet<E> of(E e); </li></ul><ul><li>static <E> EnumSet<E> of(E e1, E e2) </li></ul><ul><li>static <E> EnumSet<E> of(E e1, E e2, E e3) </li></ul><ul><li>static <E> EnumSet<E> of(E e1, E e2, E e3, E e4) </li></ul><ul><li>static <E> EnumSet<E> of(E e1, E e2, E e3, E e4, E e5); </li></ul><ul><li>static <E> EnumSet<E> of(E first, E... rest) </li></ul><ul><li>... // Remainder omitted </li></ul><ul><li>} </li></ul><ul><li>Avoids cost of array allocation if fewer that n args </li></ul>
  51. 51. Agenda <ul><li>Generics Items 28, 29 </li></ul><ul><li>Enum types Item 40 </li></ul><ul><li>Varargs Item 42 </li></ul><ul><li>Concurrency Item 69 </li></ul><ul><li>Serialization Item 78 </li></ul>
  52. 52. Item 69: Use ConcurrentHashMap But Use it Right! <ul><li>Concurrent collections manage synchronization internally </li></ul><ul><ul><li>Lock striping, non-blocking algorithms, etc. </li></ul></ul><ul><li>Combines high concurrency and performance </li></ul><ul><li>Synchronized collections nearly obsolete </li></ul><ul><li>Use ConcurrentHashMap , not Collections.synchronizedMap() </li></ul>
  53. 53. With Concurrent Collections, You Can't Combine Operations Atomically <ul><li>private static final ConcurrentMap<String, String> map = </li></ul><ul><li>new ConcurrentHashMap<String, String>(); </li></ul><ul><li>// Interning map atop ConcurrentMap -- BROKEN! </li></ul><ul><li>public static String intern(String s) { </li></ul><ul><li>synchronized(map) { // ALWAYS wrong! </li></ul><ul><li>String result = map.get(s); </li></ul><ul><li>if (result == null) { </li></ul><ul><li>map.put(s, s); </li></ul><ul><li>result = s; </li></ul><ul><li>} </li></ul><ul><li>return result; </li></ul><ul><li>} </li></ul><ul><li>} </li></ul>
  54. 54. You Could Fix it Like This... <ul><li>// Interning map atop ConcurrentMap - works, but slow! </li></ul><ul><li>public static String intern(String s) { </li></ul><ul><li>String previousValue = map. putIfAbsent (s, s); </li></ul><ul><li>return previousValue == null ? s : previousValue; </li></ul><ul><li>} </li></ul><ul><li>Calls putIfAbsent every time it reads a value </li></ul><ul><li>Unfortunately, this usage is very common </li></ul>
  55. 55. But This is Much Butter <ul><li>// Interning map atop ConcurrentMap - the right way! </li></ul><ul><li>public static String intern(String s) { </li></ul><ul><li>String result = map.get(s); </li></ul><ul><li>if (result == null) { </li></ul><ul><li>result = map.putIfAbsent(s, s); </li></ul><ul><li>if (result == null) </li></ul><ul><li>result = s; </li></ul><ul><li>} </li></ul><ul><li>return result; </li></ul><ul><li>} </li></ul><ul><li>Calls putIfAbsent only if map doesn't contain entry </li></ul><ul><li>250% faster on my machine, and far less contention </li></ul>
  56. 56. One More “Solution” That Doesn’t Work <ul><li>// Interning map atop ConcurrentMap - SLOW AND BROKEN </li></ul><ul><li>public static String intern(String s) { </li></ul><ul><li>map.putIfAbsent(s, s); // Ignores return value </li></ul><ul><li>return s; // Fails if map already contained string! </li></ul><ul><li>} </li></ul><ul><li>This bug is surprisingly common! </li></ul><ul><li>We found 15% of putIfAbsent uses ignore result </li></ul>
  57. 57. Summary <ul><li>Synchronized collections are largely obsolete </li></ul><ul><li>Use ConcurrentHashMap and friends </li></ul><ul><li>Never synchronize on a concurrent collection </li></ul><ul><li>Use putIfAbsent (and friends) properly </li></ul><ul><ul><li>Only call putIfAbsent if get returns null </li></ul></ul><ul><ul><li>And always check the return value of putIfAbsent </li></ul></ul>
  58. 58. Agenda <ul><li>Generics Items 28, 29 </li></ul><ul><li>Enum types Item 40 </li></ul><ul><li>Varargs Item 42 </li></ul><ul><li>Concurrency Item 69 </li></ul><ul><li>Serialization Item 78 </li></ul>
  59. 59. Item 74: Serialization is Fraught with Peril <ul><li>Implementation details leak into public API </li></ul><ul><ul><li>Serialized form derived from implementation </li></ul></ul><ul><li>Instances created without invoking constructor </li></ul><ul><ul><li>Constructors may establish invariants, and instance methods maintain them, yet they can be violated </li></ul></ul><ul><li>Doesn't combine well with final fields </li></ul><ul><ul><li>You’re forced to make them nonfinal or use reflection </li></ul></ul><ul><li>The result: increased maintenance cost, likelihood of bugs, security problems, </li></ul><ul><li>There is a better way! </li></ul>
  60. 60. The Serialization Proxy Pattern The basic idea is very simple <ul><li>Don’t serialize instances of your class; instead, serialize instances of a small, struct-like class class that concisely represents it </li></ul><ul><li>Then reconstitute instances of your class at deserialization time using only its public APIs! </li></ul>
  61. 61. The Serialization Proxy Pattern Step-by-step (1) <ul><li>Design a struct-like proxy class that concisely represents logical state of class to be serialized </li></ul><ul><li>Declare the proxy as a static nested class </li></ul><ul><li>Provide one constructor for the proxy, which takes an instance of the enclosing class </li></ul><ul><ul><li>No need for consistency checks or defensive copies </li></ul></ul>
  62. 62. The Serialization Proxy Pattern Step-by-step (2) <ul><li>Put writeReplace method on enclosing class </li></ul><ul><li>// You can always use exactly this code </li></ul><ul><li>private Object writeReplace() { </li></ul><ul><li>return new SerializationProxy(this); </li></ul><ul><li>} </li></ul><ul><li>Put a readResolve method on the proxy </li></ul><ul><ul><li>Use any methods in the public API of the enclosing class to reconstitute the instance </li></ul></ul>
  63. 63. A Real-Life Example EnumSet's Serialization Proxy <ul><li>private static class SerializationProxy<E extends Enum<E>> </li></ul><ul><li>implements Serializable { </li></ul><ul><li>private final Class<E> elementType; </li></ul><ul><li>private final Enum[] elements; </li></ul><ul><li>SerializationProxy(EnumSet<E> set) { </li></ul><ul><li>elementType = set.elementType; </li></ul><ul><li>elements = set.toArray(EMPTY_ENUM_ARRAY); </li></ul><ul><li>} </li></ul><ul><li>private Object readResolve() { </li></ul><ul><li>EnumSet<E> result = EnumSet.noneOf (elementType); </li></ul><ul><li>for (Enum e : elements) </li></ul><ul><li>result.add ((E)e); </li></ul><ul><li>return result; </li></ul><ul><li>} </li></ul><ul><li>private static final long serialVersionUID = ... ; </li></ul><ul><li>} </li></ul>
  64. 64. Truth in Advertising The Serialization Proxy Pattern is not a Panacea <ul><li>Incompatible with extendable classes </li></ul><ul><li>Incompatible with some classes whose object graphs contain circularities </li></ul><ul><li>Adds 15% to cost of serialization/deserialization </li></ul><ul><li>But when it’s applicable, it's the easiest way to robustly serialize complex objects </li></ul>
  65. 65. Key Ideas to Take Home <ul><li>Remember the PECS mnemonic for wildcards </li></ul><ul><li>When a fixed number of type parameters won’t do, use a Typesafe Heterogeneous Container </li></ul><ul><li>Prefer two-element enums to booleans </li></ul><ul><li>Never synchronize on a concurrent collection; use putIfAbsent , and check the return value </li></ul><ul><li>When your plans call for serialization, remember the Serialization Proxy pattern </li></ul>
  66. 66. Shameless Commerce Division <ul><li>There’s plenty more where that came from! </li></ul>
  67. 67. Dessert
  68. 68. 3. “When Words Collide” <ul><li>public class PrintWords { </li></ul><ul><li>public static void main(String[] args) { </li></ul><ul><li>System.out.println( </li></ul><ul><li>Words.FIRST + &quot; &quot; + Words.SECOND + &quot; &quot; + Words.THIRD); </li></ul><ul><li>} </li></ul><ul><li>} </li></ul><ul><li>public class Words { // Compile PrintWords against this version </li></ul><ul><li>public static final String FIRST = &quot;the&quot;; </li></ul><ul><li>public static final String SECOND = null; </li></ul><ul><li>public static final String THIRD = &quot;set&quot;; </li></ul><ul><li>} </li></ul><ul><li>public class Words { // Run against this version </li></ul><ul><li>public static final String FIRST = &quot;physics&quot;; </li></ul><ul><li>public static final String SECOND = &quot;chemistry&quot;; </li></ul><ul><li>public static final String THIRD = &quot;biology&quot;; </li></ul><ul><li>} </li></ul>
  69. 69. What Does It Print? <ul><li>public class PrintWords { </li></ul><ul><li>public static void main(String[] args) { </li></ul><ul><li>System.out.println( </li></ul><ul><li>Words.FIRST + &quot; &quot; + Words.SECOND + &quot; &quot; + Words.THIRD); </li></ul><ul><li>} </li></ul><ul><li>} </li></ul><ul><li>public class Words { // Compile PrintWords against this version </li></ul><ul><li>public static final String FIRST = &quot;the&quot;; </li></ul><ul><li>public static final String SECOND = null; </li></ul><ul><li>public static final String THIRD = &quot;set&quot;; </li></ul><ul><li>} </li></ul><ul><li>public class Words { // Run against this version </li></ul><ul><li>public static final String FIRST = &quot;physics&quot;; </li></ul><ul><li>public static final String SECOND = &quot;chemistry&quot;; </li></ul><ul><li>public static final String THIRD = &quot;biology&quot;; </li></ul><ul><li>} </li></ul>(a) the null set (b) physics chemistry biology (c) Throws exception (d) None of the above
  70. 70. <ul><li>(a) the null set </li></ul><ul><li>(b) physics chemistry biology </li></ul><ul><li>(c) Throws exception </li></ul><ul><li>(d) None of the above: the chemistry set </li></ul><ul><li>Constant variables are inlined. </li></ul>What Does It Print?
  71. 71. What exactly is a Constant Variable? <ul><li>Roughly speaking, a final primitive or String variable whose value is a compile-time constant </li></ul><ul><ul><li>See JLS 4.12.4, 13.4.9, 15.28 for the gory details </li></ul></ul><ul><li>A final String variable initialized to null is not a constant variable </li></ul><ul><li>Note that enums are not constant expressions </li></ul>
  72. 72. Another Look <ul><li>public class PrintWords { </li></ul><ul><li>public static void main(String[] args) { </li></ul><ul><li>System.out.println( </li></ul><ul><li>Words.FIRST + &quot; &quot; + Words.SECOND + &quot; &quot; + Words.THIRD); </li></ul><ul><li>} </li></ul><ul><li>} </li></ul><ul><li>public class Words { // Compile PrintWords against this version </li></ul><ul><li>public static final String FIRST = &quot;the&quot;; // Constant </li></ul><ul><li>public static final String SECOND = null; // Not a constant! </li></ul><ul><li>public static final String THIRD = &quot;set&quot;; // Constant </li></ul><ul><li>} </li></ul><ul><li>public class Words { // Run against this version </li></ul><ul><li>public static final String FIRST = &quot;physics&quot;; </li></ul><ul><li>public static final String SECOND = &quot;chemistry&quot;; </li></ul><ul><li>public static final String THIRD = &quot;biology&quot;; </li></ul><ul><li>} </li></ul>
  73. 73. How Do You Prevent Constants From Being Inlined? <ul><li>// Returns its argument </li></ul><ul><li>private static String ident (String s) { </li></ul><ul><li>return s; </li></ul><ul><li>} </li></ul><ul><li>// None of these fields are constant variables </li></ul><ul><li>public class Words { </li></ul><ul><li>public static final String FIRST = ident( &quot;the&quot; ) ; </li></ul><ul><li>public static final String SECOND = ident( null ) ; </li></ul><ul><li>public static final String THIRD = ident( &quot;set&quot; ) ; </li></ul><ul><li>} </li></ul>
  74. 74. The Moral <ul><li>Constant variables are inlined </li></ul><ul><ul><li>Only primitives and strings can be constant </li></ul></ul><ul><ul><li>null is not a constant </li></ul></ul><ul><li>If you change the value of a constant variable without recompiling its clients, their behavior becomes very confusing </li></ul><ul><li>Use constant variables only for entities whose value will never change </li></ul><ul><ul><li>Use ident for final fields whose value may change </li></ul></ul>
  75. 75. Au revoir
  76. 76. Effective Java TM : Still Effective, After All These Years Joshua Bloch

×