Running on Java 24-ea+25-3155 (Preview)
Home of The JavaSpecialists' Newsletter

163Book Review: Effective Java 2nd Edition

Author: Dr. Heinz M. KabutzDate: 2008-08-13Java Version: 1.4Category: Book Review
 

Abstract: 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 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 [ISBN 0321356683] . 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.

javaspecialists.teachable.com: Please visit our new self-study course catalog to see how you can upskill your Java knowledge.

Book Review: Effective Java 2nd Edition

Effective Java 2nd Edition [ISBN 0321356683] 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 [ISBN 0321356683] ). 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.

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

 

Comments

We are always happy to receive comments from our readers. Feel free to send me a comment via email or discuss the newsletter in our JavaSpecialists Slack Channel (Get an invite here)

When you load these comments, you'll be connected to Disqus. Privacy Statement.

Related Articles

Browse the Newsletter Archive

About the Author

Heinz Kabutz Java Conference Speaker

Java Champion, author of the Javaspecialists Newsletter, conference speaking regular... About Heinz

Superpack '23

Superpack '24 Our entire Java Specialists Training in one huge bundle more...

Free Java Book

Dynamic Proxies in Java Book
Java Training

We deliver relevant courses, by top Java developers to produce more resourceful and efficient programmers within their organisations.

Java Consulting

We can help make your Java application run faster and trouble-shoot concurrency and performance bugs...