Effective Java TM : Still Effective, After All These Years Joshua - - PowerPoint PPT Presentation

effective java tm
SMART_READER_LITE
LIVE PREVIEW

Effective Java TM : Still Effective, After All These Years Joshua - - PowerPoint PPT Presentation

Effective Java TM : Still Effective, After All These Years Joshua Bloch Effective Java: Still Effective After All These Years 1 Good Morning! This is not really a keynote There will be code lots of it But first, a bit of eye candy


slide-1
SLIDE 1

Effective Java: Still Effective After All These Years 1

Effective Java

TM :

Still Effective, After All These Years

Joshua Bloch

slide-2
SLIDE 2

Java Puzzlers: The Never-Ending Saga 2

Good Morning!

  • This is not really a keynote
  • There will be code – lots of it
  • But first, a bit of eye candy
slide-3
SLIDE 3

Java Puzzlers: The Never-Ending Saga 3

Fraser 1908

slide-4
SLIDE 4

Java Puzzlers: The Never-Ending Saga 4

Fraser 1908

slide-5
SLIDE 5

Java Puzzlers: The Never-Ending Saga 5

Fraser 1908

slide-6
SLIDE 6

Java Puzzlers: The Never-Ending Saga 6

Todorovic 1997

slide-7
SLIDE 7

Java Puzzlers: The Never-Ending Saga 7

Todorovic 1997

slide-8
SLIDE 8

Java Puzzlers: The Never-Ending Saga 8

Todorovic 1997

slide-9
SLIDE 9

Java Puzzlers: The Never-Ending Saga 9

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
slide-10
SLIDE 10

Java Puzzlers: The Never-Ending Saga 10

Agenda

  • Generics

Items 28, 29

  • Enum types

Item 40

  • Varargs

Item 42

  • Concurrency

Item 69

  • Serialization

Item 78

slide-11
SLIDE 11

Java Puzzlers: The Never-Ending Saga 11

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>

slide-12
SLIDE 12

Java Puzzlers: The Never-Ending Saga 12

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?

slide-13
SLIDE 13

Java Puzzlers: The Never-Ending Saga 13

Flex your PECS (1)

  • Suppose you want to add bulk methods to Stack<E>

void pushAll(Collection<E> src);

void popAll(Collection<E> dst);

slide-14
SLIDE 14

Java Puzzlers: The Never-Ending Saga 14

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

slide-15
SLIDE 15

Java Puzzlers: The Never-Ending Saga 15

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

slide-16
SLIDE 16

Java Puzzlers: The Never-Ending Saga 16

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>
  • r a Collection<Number> onto a Stack<Number>
  • Caller can now popAll into a Collection<Object>
  • r a Collection<Number> from a Stack<Number>
slide-17
SLIDE 17

Java Puzzlers: The Never-Ending Saga 17

Flex your PECS (2)

  • Consider this generic method:

public static <E> Set<E> union(Set<E> s1, Set<E> s2)

slide-18
SLIDE 18

Java Puzzlers: The Never-Ending Saga 18

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

slide-19
SLIDE 19

Java Puzzlers: The Never-Ending Saga 19

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

slide-20
SLIDE 20

Java Puzzlers: The Never-Ending Saga 20

Summary, in Tabular Form

Input Parameter Produces T Instances? Parameter Consumes 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)

slide-21
SLIDE 21

Java Puzzlers: The Never-Ending Saga 21

Filling in The Blanks

Parameter Produces T Instances? Parameter Consumes 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)

slide-22
SLIDE 22

Java Puzzlers: The Never-Ending Saga 22

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

slide-23
SLIDE 23

Java Puzzlers: The Never-Ending Saga 23

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
slide-24
SLIDE 24

Java Puzzlers: The Never-Ending Saga 24

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.setFavorite(String.class, "Java"); f.setFavorite(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); }

slide-25
SLIDE 25

Java Puzzlers: The Never-Ending Saga 25

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

slide-26
SLIDE 26

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

slide-27
SLIDE 27

Java Puzzlers: The Never-Ending Saga 27

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

slide-28
SLIDE 28

Java Puzzlers: The Never-Ending Saga 28

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
slide-29
SLIDE 29

Java Puzzlers: The Never-Ending Saga 29

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 }

slide-30
SLIDE 30

Java Puzzlers: The Never-Ending Saga 30

Agenda

  • Generics

Items 28, 29

  • Enum types

Item 40

  • Varargs

Item 42

  • Concurrency

Item 69

  • Serialization

Item 78

slide-31
SLIDE 31

Java Puzzlers: The Never-Ending Saga 31

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

slide-32
SLIDE 32

Java Puzzlers: The Never-Ending Saga 32

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

slide-33
SLIDE 33

Java Puzzlers: The Never-Ending Saga 33

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

slide-34
SLIDE 34

Java Puzzlers: The Never-Ending Saga 34

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

slide-35
SLIDE 35

Java Puzzlers: The Never-Ending Saga 35

Agenda

  • Generics

Items 28, 29

  • Enum types

Item 40

  • Varargs

Item 42

  • Concurrency

Item 69

  • Serialization

Item 78

slide-36
SLIDE 36

Java Puzzlers: The Never-Ending Saga 36

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()

slide-37
SLIDE 37

Java Puzzlers: The Never-Ending Saga 37

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

slide-38
SLIDE 38

Java Puzzlers: The Never-Ending Saga 38

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

slide-39
SLIDE 39

Java Puzzlers: The Never-Ending Saga 39

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

slide-40
SLIDE 40

Java Puzzlers: The Never-Ending Saga 40

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

slide-41
SLIDE 41

Java Puzzlers: The Never-Ending Saga 41

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

  • API designers: make it easy to do the right thing
slide-42
SLIDE 42

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

slide-43
SLIDE 43

Java Puzzlers: The Never-Ending Saga 43

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!
slide-44
SLIDE 44

Java Puzzlers: The Never-Ending Saga 44

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!

slide-45
SLIDE 45

Java Puzzlers: The Never-Ending Saga 45

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

slide-46
SLIDE 46

Java Puzzlers: The Never-Ending Saga 46

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

slide-47
SLIDE 47

Java Puzzlers: The Never-Ending Saga 47

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 = ... ; }

slide-48
SLIDE 48

Java Puzzlers: The Never-Ending Saga 48

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

slide-49
SLIDE 49

Java Puzzlers: The Never-Ending Saga 49

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

slide-50
SLIDE 50

Java Puzzlers: The Never-Ending Saga 50

Shameless Commerce Division

  • There’s plenty more where that came from!
slide-51
SLIDE 51

Effective Java: Still Effective After All These Years 51

Effective Java

TM :

Still Effective, After All These Years

Joshua Bloch