effective javatm still effective, after all these years...

76
Effective Java: Still Effective After All These Years 1 Effective Java TM : Still Effective, After All These Years (+ Appetizers and Dessert) Joshua Bloch

Upload: others

Post on 16-Jun-2020

5 views

Category:

Documents


0 download

TRANSCRIPT

Page 1: Effective JavaTM Still Effective, After All These Years ...files.meetup.com/1381525/still-effective.ppt.pdf · Still Effective, After All These Years (+ Appetizers and Dessert) Joshua

Effective Java: Still Effective After All These Years 1

Effective JavaTM : Still Effective, After All These Years

(+ Appetizers and Dessert)

Joshua Bloch

Page 2: Effective JavaTM Still Effective, After All These Years ...files.meetup.com/1381525/still-effective.ppt.pdf · Still Effective, After All These Years (+ Appetizers and Dessert) Joshua

Java Puzzlers: The Never-Ending Saga 2

Appetizers

Page 3: Effective JavaTM Still Effective, After All These Years ...files.meetup.com/1381525/still-effective.ppt.pdf · Still Effective, After All These Years (+ Appetizers and Dessert) Joshua

Java Puzzlers: The Never-Ending Saga 3

Fraser 1908

Page 4: Effective JavaTM Still Effective, After All These Years ...files.meetup.com/1381525/still-effective.ppt.pdf · Still Effective, After All These Years (+ Appetizers and Dessert) Joshua

Java Puzzlers: The Never-Ending Saga 4

Fraser 1908

Page 5: Effective JavaTM Still Effective, After All These Years ...files.meetup.com/1381525/still-effective.ppt.pdf · Still Effective, After All These Years (+ Appetizers and Dessert) Joshua

Java Puzzlers: The Never-Ending Saga 5

Fraser 1908

Page 6: Effective JavaTM Still Effective, After All These Years ...files.meetup.com/1381525/still-effective.ppt.pdf · Still Effective, After All These Years (+ Appetizers and Dessert) Joshua

Java Puzzlers: The Never-Ending Saga 6

Todorovic 1997

Page 7: Effective JavaTM Still Effective, After All These Years ...files.meetup.com/1381525/still-effective.ppt.pdf · Still Effective, After All These Years (+ Appetizers and Dessert) Joshua

Java Puzzlers: The Never-Ending Saga 7

Todorovic 1997

Page 8: Effective JavaTM Still Effective, After All These Years ...files.meetup.com/1381525/still-effective.ppt.pdf · Still Effective, After All These Years (+ Appetizers and Dessert) Joshua

Java Puzzlers: The Never-Ending Saga 8

Todorovic 1997

Page 9: Effective JavaTM Still Effective, After All These Years ...files.meetup.com/1381525/still-effective.ppt.pdf · Still Effective, After All These Years (+ Appetizers and Dessert) Joshua

Java Puzzlers: The Never-Ending Saga 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")); } }

Page 10: Effective JavaTM Still Effective, After All These Years ...files.meetup.com/1381525/still-effective.ppt.pdf · Still Effective, After All These Years (+ Appetizers and Dessert) Joshua

Java Puzzlers: The Never-Ending Saga 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

Page 11: Effective JavaTM Still Effective, After All These Years ...files.meetup.com/1381525/still-effective.ppt.pdf · Still Effective, After All These Years (+ Appetizers and Dessert) Joshua

Java Puzzlers: The Never-Ending Saga 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?

Page 12: Effective JavaTM Still Effective, After All These Years ...files.meetup.com/1381525/still-effective.ppt.pdf · Still Effective, After All These Years (+ Appetizers and Dessert) Joshua

Java Puzzlers: The Never-Ending Saga 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.

Page 13: Effective JavaTM Still Effective, After All These Years ...files.meetup.com/1381525/still-effective.ppt.pdf · Still Effective, After All These Years (+ Appetizers and Dessert) Joshua

Java Puzzlers: The Never-Ending Saga 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")); } }

Page 14: Effective JavaTM Still Effective, After All These Years ...files.meetup.com/1381525/still-effective.ppt.pdf · Still Effective, After All These Years (+ Appetizers and Dessert) Joshua

Java Puzzlers: The Never-Ending Saga 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.parseBoolean(s); }

public static void main(String[] args) { System.out.println( yesOrNo("true") + " " + yesOrNo("YeS")); } }

Page 15: Effective JavaTM Still Effective, After All These Years ...files.meetup.com/1381525/still-effective.ppt.pdf · Still Effective, After All These Years (+ Appetizers and Dessert) Joshua

Java Puzzlers: The Never-Ending Saga 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")); } }

Page 16: Effective JavaTM Still Effective, After All These Years ...files.meetup.com/1381525/still-effective.ppt.pdf · Still Effective, After All These Years (+ Appetizers and Dessert) Joshua

Java Puzzlers: The Never-Ending Saga 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

Page 17: Effective JavaTM Still Effective, After All These Years ...files.meetup.com/1381525/still-effective.ppt.pdf · Still Effective, After All These Years (+ Appetizers and Dessert) Joshua

Java Puzzlers: The Never-Ending Saga 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”

Page 18: Effective JavaTM Still Effective, After All These Years ...files.meetup.com/1381525/still-effective.ppt.pdf · Still Effective, After All These Years (+ Appetizers and Dessert) Joshua

Java Puzzlers: The Never-Ending Saga 18

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); } }; }

What Does It Print?

(a) 0 (b) 1 (c) -2 (d) None of the above

Page 19: Effective JavaTM Still Effective, After All These Years ...files.meetup.com/1381525/still-effective.ppt.pdf · Still Effective, After All These Years (+ Appetizers and Dessert) Joshua

Java Puzzlers: The Never-Ending Saga 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?

Page 20: Effective JavaTM Still Effective, After All These Years ...files.meetup.com/1381525/still-effective.ppt.pdf · Still Effective, After All These Years (+ Appetizers and Dessert) Joshua

Java Puzzlers: The Never-Ending Saga 20

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); } }; }

Another Look

Page 21: Effective JavaTM Still Effective, After All These Years ...files.meetup.com/1381525/still-effective.ppt.pdf · Still Effective, After All These Years (+ Appetizers and Dessert) Joshua

Java Puzzlers: The Never-Ending Saga 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

Page 22: Effective JavaTM Still Effective, After All These Years ...files.meetup.com/1381525/still-effective.ppt.pdf · Still Effective, After All These Years (+ Appetizers and Dessert) Joshua

Java Puzzlers: The Never-Ending Saga 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

Page 23: Effective JavaTM Still Effective, After All These Years ...files.meetup.com/1381525/still-effective.ppt.pdf · Still Effective, After All These Years (+ Appetizers and Dessert) Joshua

Java Puzzlers: The Never-Ending Saga 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, 1st Printing)

Page 24: Effective JavaTM Still Effective, After All These Years ...files.meetup.com/1381525/still-effective.ppt.pdf · Still Effective, After All These Years (+ Appetizers and Dessert) Joshua

Java Puzzlers: The Never-Ending Saga 24

The Main Course

Page 25: Effective JavaTM Still Effective, After All These Years ...files.meetup.com/1381525/still-effective.ppt.pdf · Still Effective, After All These Years (+ Appetizers and Dessert) Joshua

Java Puzzlers: The Never-Ending Saga 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

Page 26: Effective JavaTM Still Effective, After All These Years ...files.meetup.com/1381525/still-effective.ppt.pdf · Still Effective, After All These Years (+ Appetizers and Dessert) Joshua

Java Puzzlers: The Never-Ending Saga 26

Agenda

•  Generics Items 28, 29 •  Enum types Item 40 •  Varargs Item 42 •  Concurrency Item 69 •  Serialization Item 78

Page 27: Effective JavaTM Still Effective, After All These Years ...files.meetup.com/1381525/still-effective.ppt.pdf · Still Effective, After All These Years (+ Appetizers and Dessert) Joshua

Java Puzzlers: The Never-Ending Saga 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>

Page 28: Effective JavaTM Still Effective, After All These Years ...files.meetup.com/1381525/still-effective.ppt.pdf · Still Effective, After All These Years (+ Appetizers and Dessert) Joshua

Java Puzzlers: The Never-Ending Saga 28

A Mnemonic for Wildcard Usage

•  PECS—Producer extends, Consumer super ─  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?

Page 29: Effective JavaTM Still Effective, After All These Years ...files.meetup.com/1381525/still-effective.ppt.pdf · Still Effective, After All These Years (+ Appetizers and Dessert) Joshua

Java Puzzlers: The Never-Ending Saga 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);

Page 30: Effective JavaTM Still Effective, After All These Years ...files.meetup.com/1381525/still-effective.ppt.pdf · Still Effective, After All These Years (+ Appetizers and Dessert) Joshua

Java Puzzlers: The Never-Ending Saga 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);

Page 31: Effective JavaTM Still Effective, After All These Years ...files.meetup.com/1381525/still-effective.ppt.pdf · Still Effective, After All These Years (+ Appetizers and Dessert) Joshua

Java Puzzlers: The Never-Ending Saga 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

Page 32: Effective JavaTM Still Effective, After All These Years ...files.meetup.com/1381525/still-effective.ppt.pdf · Still Effective, After All These Years (+ Appetizers and Dessert) Joshua

Java Puzzlers: The Never-Ending Saga 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>

Page 33: Effective JavaTM Still Effective, After All These Years ...files.meetup.com/1381525/still-effective.ppt.pdf · Still Effective, After All These Years (+ Appetizers and Dessert) Joshua

Java Puzzlers: The Never-Ending Saga 33

Flex your PECS (2)

•  Consider this generic method: public static <E> Set<E> union(Set<E> s1, Set<E> s2)

Page 34: Effective JavaTM Still Effective, After All These Years ...files.meetup.com/1381525/still-effective.ppt.pdf · Still Effective, After All These Years (+ Appetizers and Dessert) Joshua

Java Puzzlers: The Never-Ending Saga 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

Page 35: Effective JavaTM Still Effective, After All These Years ...files.meetup.com/1381525/still-effective.ppt.pdf · Still Effective, After All These Years (+ Appetizers and Dessert) Joshua

Java Puzzlers: The Never-Ending Saga 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);

Page 36: Effective JavaTM Still Effective, After All These Years ...files.meetup.com/1381525/still-effective.ppt.pdf · Still Effective, After All These Years (+ Appetizers and Dessert) Joshua

Java Puzzlers: The Never-Ending Saga 36

Summary, in Tabular Form

Input Parameter Produces T Instances?

Param

eter C

onsumes T

Instances?

Yes No

Yes Foo<T>

(Invariant in T) Foo<? super T>

(Contravariant in T)

No Foo<? extends T>

(Covariant in T) Foo<?>

(Independent of T)

Page 37: Effective JavaTM Still Effective, After All These Years ...files.meetup.com/1381525/still-effective.ppt.pdf · Still Effective, After All These Years (+ Appetizers and Dessert) Joshua

Java Puzzlers: The Never-Ending Saga 37

Filling in The Blanks

Parameter Produces T Instances?

Param

eter C

onsumes T

Instances?

Yes No

Yes Foo<T>

(Invariant in T) Foo<? super T>

(Contravariant in T)

No Foo<? extends T>

(Covariant in T) Foo<?>

(Independent of T)

Page 38: Effective JavaTM Still Effective, After All These Years ...files.meetup.com/1381525/still-effective.ppt.pdf · Still Effective, After All These Years (+ Appetizers and Dessert) Joshua

Java Puzzlers: The Never-Ending Saga 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

Page 39: Effective JavaTM Still Effective, After All These Years ...files.meetup.com/1381525/still-effective.ppt.pdf · Still Effective, After All These Years (+ Appetizers and Dessert) Joshua

Java Puzzlers: The Never-Ending Saga 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

Page 40: Effective JavaTM Still Effective, After All These Years ...files.meetup.com/1381525/still-effective.ppt.pdf · Still Effective, After All These Years (+ Appetizers and Dessert) Joshua

Java Puzzlers: The Never-Ending Saga 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, "Java"); 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("printf("%s %x %s%n", favoriteString, favoriteInteger, favoriteClass); }

Page 41: Effective JavaTM Still Effective, After All These Years ...files.meetup.com/1381525/still-effective.ppt.pdf · Still Effective, After All These Years (+ Appetizers and Dessert) Joshua

Java Puzzlers: The Never-Ending Saga 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("Type is null"); favorites.put(type, instance); }

public <T> T getFavorite(Class<T> type) { return type.cast(favorites.get(type)); } }

Page 42: Effective JavaTM Still Effective, After All These Years ...files.meetup.com/1381525/still-effective.ppt.pdf · Still Effective, After All These Years (+ Appetizers and Dessert) Joshua

Java Puzzlers: The Never-Ending Saga 42

Agenda

•  Generics Items 28, 29 •  Enum types Item 40 •  Varargs Item 42 •  Concurrency Item 69 •  Serialization Item 78

Page 43: Effective JavaTM Still Effective, After All These Years ...files.meetup.com/1381525/still-effective.ppt.pdf · Still Effective, After All These Years (+ Appetizers and Dessert) Joshua

Java Puzzlers: The Never-Ending Saga 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);

Page 44: Effective JavaTM Still Effective, After All These Years ...files.meetup.com/1381525/still-effective.ppt.pdf · Still Effective, After All These Years (+ Appetizers and Dessert) Joshua

Java Puzzlers: The Never-Ending Saga 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

Page 45: Effective JavaTM Still Effective, After All These Years ...files.meetup.com/1381525/still-effective.ppt.pdf · Still Effective, After All These Years (+ Appetizers and Dessert) Joshua

Java Puzzlers: The Never-Ending Saga 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 }

Page 46: Effective JavaTM Still Effective, After All These Years ...files.meetup.com/1381525/still-effective.ppt.pdf · Still Effective, After All These Years (+ Appetizers and Dessert) Joshua

Java Puzzlers: The Never-Ending Saga 46

Agenda

•  Generics Items 28, 29 •  Enum types Item 40 •  Varargs Item 42 •  Concurrency Item 69 •  Serialization Item 78

Page 47: Effective JavaTM Still Effective, After All These Years ...files.meetup.com/1381525/still-effective.ppt.pdf · Still Effective, After All These Years (+ Appetizers and Dessert) Joshua

Java Puzzlers: The Never-Ending Saga 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; }

Page 48: Effective JavaTM Still Effective, After All These Years ...files.meetup.com/1381525/still-effective.ppt.pdf · Still Effective, After All These Years (+ Appetizers and Dessert) Joshua

Java Puzzlers: The Never-Ending Saga 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( "Too few arguments"); 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

Page 49: Effective JavaTM Still Effective, After All These Years ...files.meetup.com/1381525/still-effective.ppt.pdf · Still Effective, After All These Years (+ Appetizers and Dessert) Joshua

Java Puzzlers: The Never-Ending Saga 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

Page 50: Effective JavaTM Still Effective, After All These Years ...files.meetup.com/1381525/still-effective.ppt.pdf · Still Effective, After All These Years (+ Appetizers and Dessert) Joshua

Java Puzzlers: The Never-Ending Saga 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

Page 51: Effective JavaTM Still Effective, After All These Years ...files.meetup.com/1381525/still-effective.ppt.pdf · Still Effective, After All These Years (+ Appetizers and Dessert) Joshua

Java Puzzlers: The Never-Ending Saga 51

Agenda

•  Generics Items 28, 29 •  Enum types Item 40 •  Varargs Item 42 •  Concurrency Item 69 •  Serialization Item 78

Page 52: Effective JavaTM Still Effective, After All These Years ...files.meetup.com/1381525/still-effective.ppt.pdf · Still Effective, After All These Years (+ Appetizers and Dessert) Joshua

Java Puzzlers: The Never-Ending Saga 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()

Page 53: Effective JavaTM Still Effective, After All These Years ...files.meetup.com/1381525/still-effective.ppt.pdf · Still Effective, After All These Years (+ Appetizers and Dessert) Joshua

Java Puzzlers: The Never-Ending Saga 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; } }

Page 54: Effective JavaTM Still Effective, After All These Years ...files.meetup.com/1381525/still-effective.ppt.pdf · Still Effective, After All These Years (+ Appetizers and Dessert) Joshua

Java Puzzlers: The Never-Ending Saga 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

Page 55: Effective JavaTM Still Effective, After All These Years ...files.meetup.com/1381525/still-effective.ppt.pdf · Still Effective, After All These Years (+ Appetizers and Dessert) Joshua

Java Puzzlers: The Never-Ending Saga 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

Page 56: Effective JavaTM Still Effective, After All These Years ...files.meetup.com/1381525/still-effective.ppt.pdf · Still Effective, After All These Years (+ Appetizers and Dessert) Joshua

Java Puzzlers: The Never-Ending Saga 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

Page 57: Effective JavaTM Still Effective, After All These Years ...files.meetup.com/1381525/still-effective.ppt.pdf · Still Effective, After All These Years (+ Appetizers and Dessert) Joshua

Java Puzzlers: The Never-Ending Saga 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

Page 58: Effective JavaTM Still Effective, After All These Years ...files.meetup.com/1381525/still-effective.ppt.pdf · Still Effective, After All These Years (+ Appetizers and Dessert) Joshua

Java Puzzlers: The Never-Ending Saga 58

Agenda

•  Generics Items 28, 29 •  Enum types Item 40 •  Varargs Item 42 •  Concurrency Item 69 •  Serialization Item 78

Page 59: Effective JavaTM Still Effective, After All These Years ...files.meetup.com/1381525/still-effective.ppt.pdf · Still Effective, After All These Years (+ Appetizers and Dessert) Joshua

Java Puzzlers: The Never-Ending Saga 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!

Page 60: Effective JavaTM Still Effective, After All These Years ...files.meetup.com/1381525/still-effective.ppt.pdf · Still Effective, After All These Years (+ Appetizers and Dessert) Joshua

Java Puzzlers: The Never-Ending Saga 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!

Page 61: Effective JavaTM Still Effective, After All These Years ...files.meetup.com/1381525/still-effective.ppt.pdf · Still Effective, After All These Years (+ Appetizers and Dessert) Joshua

Java Puzzlers: The Never-Ending Saga 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

Page 62: Effective JavaTM Still Effective, After All These Years ...files.meetup.com/1381525/still-effective.ppt.pdf · Still Effective, After All These Years (+ Appetizers and Dessert) Joshua

Java Puzzlers: The Never-Ending Saga 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

Page 63: Effective JavaTM Still Effective, After All These Years ...files.meetup.com/1381525/still-effective.ppt.pdf · Still Effective, After All These Years (+ Appetizers and Dessert) Joshua

Java Puzzlers: The Never-Ending Saga 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 = ... ; }

Page 64: Effective JavaTM Still Effective, After All These Years ...files.meetup.com/1381525/still-effective.ppt.pdf · Still Effective, After All These Years (+ Appetizers and Dessert) Joshua

Java Puzzlers: The Never-Ending Saga 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

Page 65: Effective JavaTM Still Effective, After All These Years ...files.meetup.com/1381525/still-effective.ppt.pdf · Still Effective, After All These Years (+ Appetizers and Dessert) Joshua

Java Puzzlers: The Never-Ending Saga 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

Page 66: Effective JavaTM Still Effective, After All These Years ...files.meetup.com/1381525/still-effective.ppt.pdf · Still Effective, After All These Years (+ Appetizers and Dessert) Joshua

Java Puzzlers: The Never-Ending Saga 66

Shameless Commerce Division

•  There’s plenty more where that came from!

Page 67: Effective JavaTM Still Effective, After All These Years ...files.meetup.com/1381525/still-effective.ppt.pdf · Still Effective, After All These Years (+ Appetizers and Dessert) Joshua

Java Puzzlers: The Never-Ending Saga 67

Dessert

Page 68: Effective JavaTM Still Effective, After All These Years ...files.meetup.com/1381525/still-effective.ppt.pdf · Still Effective, After All These Years (+ Appetizers and Dessert) Joshua

Java Puzzlers: The Never-Ending Saga 68

3. “When Words Collide”

public class PrintWords { public static void main(String[] args) { System.out.println( Words.FIRST + " " + Words.SECOND + " " + Words.THIRD); } }

public class Words { // Compile PrintWords against this version public static final String FIRST = "the"; public static final String SECOND = null; public static final String THIRD = "set"; }

public class Words { // Run against this version public static final String FIRST = "physics"; public static final String SECOND = "chemistry"; public static final String THIRD = "biology"; }

Page 69: Effective JavaTM Still Effective, After All These Years ...files.meetup.com/1381525/still-effective.ppt.pdf · Still Effective, After All These Years (+ Appetizers and Dessert) Joshua

Java Puzzlers: The Never-Ending Saga 69

What Does It Print?

public class PrintWords { public static void main(String[] args) { System.out.println( Words.FIRST + " " + Words.SECOND + " " + Words.THIRD); } }

public class Words { // Compile PrintWords against this version public static final String FIRST = "the"; public static final String SECOND = null; public static final String THIRD = "set"; }

public class Words { // Run against this version public static final String FIRST = "physics"; public static final String SECOND = "chemistry"; public static final String THIRD = "biology"; }

(a) the null set (b) physics chemistry biology (c) Throws exception (d) None of the above

Page 70: Effective JavaTM Still Effective, After All These Years ...files.meetup.com/1381525/still-effective.ppt.pdf · Still Effective, After All These Years (+ Appetizers and Dessert) Joshua

Java Puzzlers: The Never-Ending Saga 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?

Page 71: Effective JavaTM Still Effective, After All These Years ...files.meetup.com/1381525/still-effective.ppt.pdf · Still Effective, After All These Years (+ Appetizers and Dessert) Joshua

Java Puzzlers: The Never-Ending Saga 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

Page 72: Effective JavaTM Still Effective, After All These Years ...files.meetup.com/1381525/still-effective.ppt.pdf · Still Effective, After All These Years (+ Appetizers and Dessert) Joshua

Java Puzzlers: The Never-Ending Saga 72

Another Look

public class PrintWords { public static void main(String[] args) { System.out.println( Words.FIRST + " " + Words.SECOND + " " + Words.THIRD); } }

public class Words { // Compile PrintWords against this version public static final String FIRST = "the"; // Constant public static final String SECOND = null; // Not a constant! public static final String THIRD = "set"; // Constant }

public class Words { // Run against this version public static final String FIRST = "physics"; public static final String SECOND = "chemistry"; public static final String THIRD = "biology"; }

Page 73: Effective JavaTM Still Effective, After All These Years ...files.meetup.com/1381525/still-effective.ppt.pdf · Still Effective, After All These Years (+ Appetizers and Dessert) Joshua

Java Puzzlers: The Never-Ending Saga 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("the"); public static final String SECOND = ident(null); public static final String THIRD = ident("set"); }

Page 74: Effective JavaTM Still Effective, After All These Years ...files.meetup.com/1381525/still-effective.ppt.pdf · Still Effective, After All These Years (+ Appetizers and Dessert) Joshua

Java Puzzlers: The Never-Ending Saga 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

Page 75: Effective JavaTM Still Effective, After All These Years ...files.meetup.com/1381525/still-effective.ppt.pdf · Still Effective, After All These Years (+ Appetizers and Dessert) Joshua

Java Puzzlers: The Never-Ending Saga 75

Au revoir

Page 76: Effective JavaTM Still Effective, After All These Years ...files.meetup.com/1381525/still-effective.ppt.pdf · Still Effective, After All These Years (+ Appetizers and Dessert) Joshua

Effective Java: Still Effective After All These Years 76

Effective JavaTM : Still Effective, After All These Years

Joshua Bloch