|
The Java Specialists' Newsletter
Issue 163 2008-08-13
Category:
Book Review
Java version: Java 4,5,6 Book Review: Effective Java 2nd Editionby Dr. Heinz M. KabutzAbstract:
Joshua Bloch has at long last published an updated version
of Effective Java. An essential guide for professional
Java programmers who are interested in producing high
quality code, this book is also very readable. In this
newsletter we describe some of the nuggets found in the
book.
Welcome to the 163rd issue of The Java(tm) Specialists' Newsletter, read by approximately
50000 readers in 115
countries. We are back on Crete, our home for
almost 2 years. It is one of the friendliest places on this
planet. Last week Java Champion Kirk Pepperdine and I
were swimming at the beach of Marathi with our families, when
a Cretan started chatting to me. We talked in German for
about 5-10 minutes,
after which he invited us to come visit him at his house
amongst the orange trees, chat a bit and maybe drink some
Tsikoudia.
The Cretan hospitality is truly something special and we will
be sure to take him up on his invite in the next few months.
Jazoon 2008 was
fantastic, well organised and with some interesting speakers.
I had a great time chatting with Joshua Bloch and even
received an autographed copy of his new book. Whilst in
Switzerland
we had an opportunity to listen to a private audition by
Kristian
Cvetkovic, a 17 year old "wunderkind" who in the next
years will become a big name in classical piano. His piano
performance was truly spectacular, unlike anything I had
heard before.
After Jazoon, we flew to South Africa, where we visited
friends and family. Our children received private soccer
tuition from my father-in-law Gregory Kytides, who was voted
the best football player in his professional club in 1968. We
also spent a few days relaxing amongst zebras and giraffes
with my mother and sister. I found out on the first
night that 3G had not made its appearance amongst the
warthogs, so from then on, I just left my computer locked
away. If you ever visit Cape Town, take a trip out to
Buffelsfontein,
but let your customers know in advance that you will be
out of range.
This month, I would like to review Effective
Java 2nd Edition
by Joshua Bloch . If you enjoy the book, you will
definitely love my new Java Specialist Master
Course, which is filled with practical everyday
advice to improve your Java software development skills, lots
of it quite similar to the book.
Please contact
us for more information or visit our
website.
Upcoming Java Specialist Master Courses:
- please click here to sign up.
As from May 2010, we are also offering this course on the island of Crete. We
only accept 6 students per class in Crete, due to the size of our conference
room. Please book early to avoid disappointment!
San Jose CA, Mar 16-19 2010, $3500 Ottawa, Canada, Mar 22-25 2010, $3500 Oslo, Norway, Apr 13-16 2010, Kr 24500 Montreal, Canada, Apr 20-23 2010, $3500 Toronto, Canada, May 17-20 2010, $3500 Chania, Crete, May 25-28, Jun 29-Jul 2 or Aug 24-27 2010, €2500
In-house courses if these dates or locations do not suit you - click here for more information. Book Review: Effective Java 2nd Edition
Effective Java 2nd Edition is brimming
with good ideas for the professional Java programmer. One of
the advantages of owning this book is that it settles a lot
of arguments. Not sure when to use checked exceptions?
Simply open the book, look for the item that describes
checked exceptions and read what Joshua Bloch thinks on the
topic. Since Joshua wrote a great deal of the JDK, you can
learn from the master.
In Item 1, Joshua starts with a nice technique for
constructing generic objects with minimal duplication.
Typically when we initialize a parameterized variable, we
need to write the generic parameter twice, as in:
Map<String, List<String>> m =
new HashMap<String, List<String>>();
This is repetitive and boring to write. Instead, we see
a nice technique that takes care of these details for
us, using a technique called type inference, where the
compiler discovers what the generic parameters are supposed
to be:
import java.util.*;
public class GenericFactory {
public static <K, V> HashMap<K,V> newHashMap() {
return new HashMap<K,V>();
}
public static <E> ArrayList<E> newArrayList() {
return new ArrayList<E>();
}
// etc.
}
By statically importing the GenericFactory, we can now
replace the wordy declaration with:
Map<String, List<String>> m = newHashMap();
Another nice idiom is "Consider a Builder when faced with
many constructor parameters". Very similar in principle to
the idea of using fluent
interfaces, it allows you to be more specific in what
each parameter means. Consider the following class with
constructor:
public class NutritionFacts {
private final int servingSize; // (mL) required
private final int servings; // (per container) required
private final int calories; // optional
private final int fat; // (g) optional
private final int sodium; // (mg) optional
private final int carbohydrate; // (g) optional
public NutritionFacts(int servingSize, int servings) {
this(servingSize, servings, 0);
}
public NutritionFacts(int servingSize, int servings,
int calories) {
this(servingSize, servings, calories, 0);
}
public NutritionFacts(int servingSize, int servings,
int calories, int fat) {
this(servingSize, servings, calories, fat, 0);
}
public NutritionFacts(int servingSize, int servings,
int calories, int fat, int sodium) {
this(servingSize, servings, calories, fat, sodium, 0);
}
public NutritionFacts(int servingSize, int servings,
int calories, int fat, int sodium,
int carbohydrate) {
this.servingSize = servingSize;
this.servings = servings;
this.calories = calories;
this.fat = fat;
this.sodium = sodium;
this.carbohydrate = carbohydrate;
}
}
To construct an instance of NutritionFacts representing a can
of soda, we would need to use this telescoping constructor.
If we do not know one of the parameters, we pass in some
default number such as 0 to mark it as being unset. This
information ends up in the client code. It is also easy to
accidentally swap around two parameters, especially if they
are of the same type.
Another option is to follow the JavaBeans approach and to
define a bunch of setter methods. This can lead to an
invalid state in the NutritionFacts object, if for example
the initialization is stopped whilst we are setting the
various parameters.
A really nice alternative is to use the Builder pattern. We
define a static inner class Builder, whose job it is to
collect the parameters and then construct the object in one
fell swoop.
// Builder Pattern
public class NutritionFacts {
private final int servingSize;
private final int servings;
private final int calories;
private final int fat;
private final int sodium;
private final int carbohydrate;
public static class Builder {
// Required parameters
private final int servingSize;
private final int servings;
// Optional parameters - initialized to default values
private int calories = 0;
private int fat = 0;
private int carbohydrate = 0;
private int sodium = 0;
public Builder(int servingSize, int servings) {
this.servingSize = servingSize;
this.servings = servings;
}
public Builder calories(int val) {
calories = val;
return this;
}
public Builder fat(int val) {
fat = val;
return this;
}
public Builder carbohydrate(int val) {
carbohydrate = val;
return this;
}
public Builder sodium(int val) {
sodium = val;
return this;
}
public NutritionFacts build() {
return new NutritionFacts(this);
}
}
private NutritionFacts(Builder builder) {
servingSize = builder.servingSize;
servings = builder.servings;
calories = builder.calories;
fat = builder.fat;
sodium = builder.sodium;
carbohydrate = builder.carbohydrate;
}
}
We can now use this static inner class as follows:
NutritionFacts sodaDrink = new NutritionFacts.Builder(240, 8).
calories(100).sodium(35).carbohydrate(27).build();
There is Only One Elvis
One of the new features of Java 5 is enums. Joshua spends a
few pages discussing how enums can be (ab)used for writing a
serializable Singleton. The serializable Singleton approach
in his first book had a flaw that allowed determined hackers
to still create instances.
My first comment is that Singletons are often a bad design
decision in object oriented programs.
My second comment is, if you really have to use a Singleton,
is it really such a good idea to make this poor construct
also Serializable? I almost want to ask my readers for
suggestions, but fear that it will bring forth a deluge of
supposedly good use cases which might take a few years to
sort through :-)
If you really want to make a Singleton serializable, my
approach would be to stick with the standard Singleton
approach, but use a serialization proxy (Item 78 in the
book). The code is a bit more involved, but you are not
restricted by the enum mechanism.
Let's assume we have an Elvis instance (we all know
there can be only one):
import java.util.Arrays;
public class Elvis {
private static final Elvis INSTANCE = new Elvis();
private volatile String[] songs =
{"Hound Dog", "Heartbreak Hotel"};
public static Elvis getInstance() { return INSTANCE; }
private Elvis() { }
public void leaveTheBuilding() { }
public void printFavorites() {
System.out.println(Arrays.toString(songs));
}
public void setFavouriteSongs(String[] songs) {
this.songs = songs.clone();
}
}
In my approach, we can make this Serializable, without even
temporarily constructing another Elvis object, by using the
writeReplace() and readResolve() methods.
import java.io.*;
import java.util.Arrays;
public class Elvis implements Serializable {
private static final Elvis INSTANCE = new Elvis();
private volatile String[] songs =
{"Hound Dog", "Heartbreak Hotel"};
public static Elvis getInstance() { return INSTANCE; }
private Elvis() { }
public void leaveTheBuilding() { }
public void setFavouriteSongs(String[] songs) {
this.songs = songs.clone();
}
public void printFavorites() {
System.out.println(Arrays.toString(songs));
}
// this should never be invoked
private Object readResolve() { return INSTANCE; }
private void readObject(ObjectInputStream ex)
throws IOException {
throw new InvalidObjectException("Cannot load another Elvis");
}
private Object writeReplace() {
return new ElvisSerializableForm(songs);
}
private static class ElvisSerializableForm
implements Serializable {
private final String[] songs;
public ElvisSerializableForm(String[] favoriteSongs) {
this.songs = favoriteSongs;
}
public Object readResolve() {
Elvis.INSTANCE.setFavouriteSongs(songs);
return Elvis.INSTANCE;
}
}
}
When you try to serialize an Elvis, it calls the
writeReplace() method on that object, which then instead
serializes an ElvisSerializableForm instance. This is a
container for the state of the Elvis Singleton, in this case
our favourite songs. When the object is deserialized, the
ObjectInputStream reads in an ElvisSerializableForm instead
of an Elvis and then calls the readResolve() method on that
object, which returns the Singleton Elvis instance.
In Effective Java 2nd Ed, we see a hack where someone
constructed an Elvis object in binary form and then simply read that
into the ObjectInputStream (for more information, please have
a look at Item 77 in Effective Java 2nd
Ed ). The hack did not work with my approach, because
I never create any additional Elvis instances, not even
temporarily whilst deserializing.
Joshua Bloch's solution is much shorter than my rather
involved approach. It uses the enum
mechanism to avoid all of the worry about serialization.
Of course, it also does not really serialize the object
itself. The state is certainly never serialized for an enum,
so you could just as well have marked all the fields
transient. Besides that, in this solution, instead of having
a getInstance() method, we have a public static
field INSTANCE. Here is the solution proposed
in the book:
public enum Elvis {
INSTANCE;
private String[] favoriteSongs =
{"Hound Dog", "Heartbreak Hotel"};
public void printFavorites() {
System.out.println(Arrays.toString(favoriteSongs));
}
}
Autoboxing Dangers
In his item "Prefer primitive types to boxed primitives",
Joshua shows several examples of why autoboxing is dangerous.
Here is one, which can lead to an incorrectly sorted list,
but not necessarily:
// Broken comparator - can you spot the flaw?
Comparator<Integer> naturalOrder = new Comparator<Integer>() {
public int compare(Integer first, Integer second) {
return first < second ? -1 : (first == second ? 0 : 1);
}
};
Another example adds up a bunch of numbers, using autoboxing.
Here we see it:
// Hideously slow program! Can you spot the object creation?
public static void main(String[] args) {
Long sum = 0L;
for (long i = 0; i <= Integer.MAX_VALUE; i++) {
sum += i;
}
System.out.println(sum);
}
By changing the local variable sum to be of type
long, we can run through the addition
approximately 7x faster.
In my Java Specialist
Master Course, we look at ways to make code
faster. If we were to simply profile and tune the algorithm
above to not use autoboxing, we might end up with code that
is 7x faster. However, before we do that, we teach that you
should first look at whether we could change the algorithm.
For example, the current algorithm is O(n). We could change
it to be
O(1) by simply calculating the total from 0 up to MAX_VALUE.
To add this up, use the formula:
sum(1..n) = n * (n+1)/2
Java Specialist Book of the Year 2008
If there is one book to buy this year, this has to be it.
Filled to the brim with excellent advice and written in such
interesting prose that I managed to pay attention, even on
the beach at Marathi on some sweltering afternoons.
I never actually nominated my "Books of the Year" for
previous years, so here goes.
2005 Head First Design Patterns
2006 Java Concurrency in Practice
2007 Java Generics and Collections
2008 Effective Java 2nd Edition
As I look at this fine book lying on my desk, with Joshua's
autograph in the front, it irks me that after just one read,
it has fallen apart (and I mean this literally). That
usually happens when you buy old novels in second-hand
stores, not with brand new essential reference books. Book
publishers must be under pressure nowadays, with all the free
information available on the internet, but skimping on the
binding is not a way to endear readers. Or maybe I was just
unlucky? Or perhaps suncream is a solvent for book binding
glue? Maybe we should develop an island style printing
mechanism with waterproof paper ... :-)
Kind regards
Heinz
Book Review Articles
Related Java Course
|