Java library evolution puzzlers

Exploring puzzling code to figure out why it doesn't do what seems obvious is one way to improve your programming skills. Java gurus Joshua Bloch and Neal Gafter took this tactic to heart when they wrote the popular Java Puzzlers book, presenting numerous puzzles that offer insight into the Java language. For example, Bloch and Gafter asked us to figure out why the following code fragment outputs cafebabe instead of 1cafebabe:

System.out.println(Long.toHexString(0x100000000L + 0xcafebabe));

Now a new set of puzzlers has emerged, inspired by the original Java Puzzlers. The Java Library Evolution Puzzlers focus on source and binary code compatibility between client programs and the libraries that these programs use. Created by JavaWorld contributor Jens Dietrich, an Associate Professor in the Engineering School at New Zealand's Massey University, the Java Library Evolution Puzzlers survey attempts to gauge how well Java developers understand the ramifications of making changes to libraries and then compiling or not compiling their client programs.

Each puzzler focuses on a client program and a pair of library JAR files. It's always possible for the client program to be compiled and executed with the first version of the JAR file. However, what happens when the client program is recompiled against the second version of the library? Also, what happens when the client program is executed with the second library version without recompiling the client? It might not be that easy to solve all of the survey's 25 library evolution puzzlers:

  • Adding a Method to an Interface
  • Removing a Method from an Interface 1
  • Removing a Method from an Interface 2
  • Removing a Method from an Interface 3
  • Specializing a Method Return Type 1
  • Specializing a Method Return Type 2
  • Specializing a Method Return Type 3
  • Specializing a Method Return Type 4
  • Generalizing a Method Parameter Type 1
  • Generalizing a Method Parameter Type 2
  • Generalizing a Method Parameter Type 3
  • Adding an Exception to a Method 1
  • Adding an Exception to a Method 2
  • Specializing an Exception
  • Removing an Exception from a Method 1
  • Removing an Exception from a Method 2
  • Primitive vs Wrapper Types 1
  • Primitive vs Wrapper Types 2
  • Changing a Generic Type Parameter 1
  • Changing a Generic Type Parameter 2
  • Changing the Value of a Constant 1
  • Changing the Value of a Constant 2
  • Changing the Value of a Constant 3
  • Changing the Value of a Constant 4
  • Nested Classes

Consider "Adding a Method to an Interface." As its name suggests, the second version of a library's interface has a method added to it. How does this affect the client program? This puzzler presents the following code:

// This interface is stored in its own Foo.java file source
// file and compiled into lib-1.0.jar.

package lib.addtointerface; 
public interface Foo {
   public void foo();
}

// This variant of the previous interface is stored in its
// own Foo.java source file and compiled into lib-2.0.jar.

package lib.addtointerface; 
public interface Foo {
   public void foo();
   public void bar();
}

package addtointerface;
import lib.addtointerface.*;
public class Main implements Foo {
   @Override public void foo() {
      System.out.println("foo");
   }
   public static void main(String[] args) {
      new Main().foo(); 
   }
}

The puzzler then asks the following pair of questions:

  1. Can the version of the client program compiled with lib-1.0.jar be executed with lib-2.0.jar?
  2. Can the client program be compiled and then executed with lib-2.0.jar?

For the first question, you respond by choosing one of the following options:

  • no, an error occurs
  • yes, but the behaviour of the program changes
  • yes, and the behaviour of the program does not change
  • other (please specify)

For the second question, you respond by choosing one of the following options:

  • no, compilation fails
  • yes, but the behaviour of the program is different from the program version compiled and executed with lib-1.0.jar
  • yes, and the behaviour of the program is the same as the program version compiled and executed with lib-1.0.jar
  • other (please specify)

If you'd like to take part in this survey, point your browser to the survey's Java Library Evolution Puzzlers - Full Version page and follow the instructions. Although there are long and short forms of the survey, I recommend that you take the long form to maximize your understanding of library evolution. If you're looking for answers after completing the survey, you'll find them near the end of the presentation.pdf file.

By the way, if you're still trying to figure out why System.out.println(Long.toHexString(0x100000000L + 0xcafebabe)); outputs cafebabe instead of 1cafebabe, the Java Puzzlers sample chapter has the answer.

What's next?

Next time, I launch a three-part series that answers various questions related to the java.lang.Object class and its various methods. In Part 1, I overview Object and examine its clone() and equals() methods.

Notice to our Readers
We're now using social media to take your comments and feedback. Learn more about this here.