Your SlideShare is downloading. ×
Effective Java - Still Effective After All These Years
Upcoming SlideShare
Loading in...5
×

Thanks for flagging this SlideShare!

Oops! An error has occurred.

×
Saving this for later? Get the SlideShare app to save on your phone or tablet. Read anywhere, anytime – even offline.
Text the download link to your phone
Standard text messaging rates apply

Effective Java - Still Effective After All These Years

7,756

Published on

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

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

2 Comments
50 Likes
Statistics
Notes
No Downloads
Views
Total Views
7,756
On Slideshare
0
From Embeds
0
Number of Embeds
16
Actions
Shares
0
Downloads
0
Comments
2
Likes
50
Embeds 0
No embeds

Report content
Flagged as inappropriate Flag as inappropriate
Flag as inappropriate

Select your reason for flagging this presentation as inappropriate.

Cancel
No notes for slide
  • The twisted cord illusion , also known as the Fraser figure , was described by psychologist James Fraser in 1908. The letters “ C AFE babe ” are set straight and true; the perceived tilt is illusory.
  • Todorovic’s gradient chessboard illusion.
  • But enough of pleasantries. It’s time for Effective Java
  • This is the third talk I’ve given on the second edition. The first concentrated on generics, the second on enums. This one is a bit of dog’s breakfast.
  • Most of this material has not been included in previous talks, but the first two Items are repeats, because I think they bear repeating
  • Probably the hardest part of generic types in Java is wildcards. They allow you to increase the flexibility of your APIs, but they’re confusing. I’m going to teach you a mnemonic that cuts through the confusion. Transition: Properly used, wildcards can give you the flexibility that you’re used to from arrays, coupled with the type-safety of generics. So how do you use wildcards properly?
  • It’s simply, really: just use your PECS!
  • Here’s how the declarations would look without wildcards. But these declarations aren’t very flexible. The src and dst types have to match the stack is exactly.
  • And what do these wildcard declarations buy you?
  • And what do these wildcard declarations buy you?
  • Java’s type inference has its limitations, and our revised union type signature demonstrates one.
  • Here’s our mnemonic in tabular form. If you’re using a parameter to produce T instances, declare it Foo<? extends T>; if you’re using it to consume T instances, declare it Foo<? super T>. But the table raises a question: what if you’re using a parameter to produce and consume T instances, say adding and removing T’s from a collection, or neither producing nor consuming T’s, say removing elements from a collection.
  • As you can see from this expanded table, you can’t use wildcards if you’re both adding and removing T’s: the intersection of Foo<? extends T> and Foo<? super T> is Foo<T>, and that’s the type you have to use if you’re both producing and consuming T’s. If you’re neither producing nor consuming T’s, you can accept a Foo of any type, which is an unbounded wildcard.
  • We call this mind-expanding pattern the Typesafe Heterogeneous Container, or THC for short. The idea is very simple. … Let’s take a look at a complete example.
  • Here we have a data type that allows you to store a few of your fa-vo-rite things. In this example, the Favorites instance take the part of the database row, and the Class object takes the part of the column Now let’s take a look at the implementation. It’s surprisingly simple!
  • Notice that the type of the map does not ensure the required relationship between key and value, but that’s OK. The cast method is the dynamic analog of Java’s cast operator. It simply checks that its argument is an instance of the type represented by the Class object. If so, it returns the argument; otherwise it throws a ClassCastException.
  • As a reward for suffering all that complicated stuff, the next two sections are light and fluffy
  • Go through code. This works, but you can do much better!
  • One thing about varargs is that it allocates an array under the covers. Normally this doesn’t matter much, but it can matter in performance critical situations. You can use an idiom very similar to this one to deal with the problem.
  • Of course you can pick the value of n that works best for your application. If you choose it carefully, you almost never pay for the array allocation Mention premature optimization!
  • Now I want to talk a bit about concurrent collections, and ConcurrentHashMap, in particular
  • But be careful
  • Finally, I want to tell you about a serialization pattern that I really like
  • What exactly is a constant variable? Sounds like an oxymoron, doesn’t it?
  • Mention: consturctor would do for String, but ident trick is valuable for primitives. Enums often superior
  • Transcript

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

    ×