Joseph D. Darcy's Sun Weblog

Joseph D. Darcy's Sun Weblog


20080527 Tuesday May 27, 2008

Indiana Jones: Ants aren't like scorpions!

There have been many years and many miles since Indiana Jones and the Last Crusade, and after seeing Indiana Jones and the Kingdom of the Crystal Skull this weekend, I think the franchise is a little worse for wear. In the opening action sequence, I was was surprised to learn Soviet gunpowder in the 1950's apparently included iron filings in addition to sulfur, charcoal and potassium nitrate since the powder was magnetic! Somewhere in editing, I'm convinced the line "Ants aren't like scorpions!" must have been deleted, a line I hope will be added back for the DVD edition.

(2008-05-27 10:00:00.0) Permalink Comments [2]

20080516 Friday May 16, 2008

A Twisty Maze of Little Molieres

Described as the French "Shakespeare in Love," I watched the film "Molière" about the famous but new-to-me 17th century French playwright and one verbal exchange stuck with me. The exchange is described in the wikipedia article about Molière:

In Le Bourgeois Gentilhomme, the title character, M. Jourdain, composes a love note as follows: "Beautiful marchioness, your beautiful eyes make me die from love" ("Belle marquise, vos beaux yeux me font mourir d'amour"). He then asks his philosophy teacher to rephrase the sentence which he does by shuffling the words in nearly every single way ("Beautiful marchioness, from love," etc.). M. Jourdain then asks which phrasing is best and the teacher promptly replies that the first is best. The phrase "Belle marquise..." is now used to indicate that two different sentences mean the same thing.

I was immediately reminded of the Adventure game's many variants of "A twisty maze of little passages, all different." Exploring the space of combinatorial permutations has had recognized literary value for longer than I thought!

(2008-05-16 10:00:00.0) Permalink

20080514 Wednesday May 14, 2008

JavaOne: Writing the next great Java book

Catching up post-JavaOne, I was glad to have gone a bit outside my usual core Java SE track sessions on Thursday evening by attending "Writing the Next Great Java™ Technology Book." Moderated by book editor Greg Doench, the panel of distinguished authors, Brian Goetz, Josh Bloch, Kathy Sierra, and Bert Bates, gave advice ranging from why to start writing a book to how to complete one. Below are my recollections of the bof.

Brian advised to treat writing a book like running a software release, including version control over the text and code samples, as well as automated building and testing of any code. Brian's quote from Churchill,

Writing a book is an adventure. To begin with, it is a toy and an amusement; then it becomes a mistress, and then it becomes a master, and then a tyrant. The last phase is that just as you are about to be reconciled to your servitude, you kill the monster, and fling him out to the public.

—Winston Churchill

certainly rang true for me in scaled down ways for some of the writing and other projects I've done. (I have many partially written blog entries, some over a year old; the toy stage doesn't last very long!) Brian also gave a warning on the scope writing a book: it will take twice as long as you think it will; there is a substantial amount of editing and revising even when the book is seemingly near completion.

Josh spoke of the importance of passion for the subject matter and knowing what you want to say. Additionally, he thought it was essential to have a diverse slate of reviewers who were representative of the book's audience; for example, one of the early readers of "Effective Java" was the teenaged son of the Java series editor. Good reviewers need a willingness to let the author know when the book is incorrect or needs improvement. To Josh, Strunk and White remains a model of clarity. He also explained how threats from family members can be a helpful motivation to finish a book!

I've read books by Brian and Josh, but I haven't read Kathy and Bert's "Head First Java," which takes a less traditional, more graphical, approach to technical writing. Bert listed a number of books, including What the Best College Teachers Do and Efficiency in Learning, as having important insights to help manage the cognitive load of readers. Kathy only used one slide, but had the audience do several interactive exercises, including staring down our nearest neighbor. (With our forward facing eyes, people are predators; facing down a full room of predators tickles the innate "fight or flight" response :-) She described most technical books as providing an "I suck." experience for the reader, an experience they didn't want to encourage in "Head First." Part of reducing the likelihood of suckage comes from skillfully leaving things out. However, books should strive for a high-resolution experience; in a California-sensitive analogy, many don't regard wine as merely a binary red/white beverage. For Kathy, a goal of a technical books is for the reader's reaction to not be about the author or the book itself, but rather the difference made to the reader.

Besides books, I think the panel's advice is useful for other forms of writing too, having strong reviewers and keeping a concern for your reader are broadly applicable, and I'll keep their suggestions in mind for my future blogging.

(2008-05-14 22:56:55.0) Permalink Comments [1]

20080512 Monday May 12, 2008

API Design: Interfaces versus Abstract Classes

Jake Gittes: Why are you doing it?
How much better can you eat?
What can you buy that you can't already afford?
Noah Cross: The future, Mr. Gitts, the future.
—Chinatown


Quoting, Effective Java, first edition, Item 16: Prefer Interfaces to abstract classes

To summarize, an interface is generally the best way to define a type that permits multiple implementations. An exception to this rule is the case where ease of evolution is deemed more important than flexibility and power.

As discussed in that item, the ease of evolution of abstract classes comes from the ability to add new methods having "reasonable default implementations" without almost surely causing source of all existing subtypes to no longer compile. The flexibility and power of interfaces involve ease of retrofitting to existing classes, allowing nonhierarchical type relations, and so on. An additional benefit of interfaces is the ability to use dynamic proxies; one notable use of dynamic proxies is creating the annotation objects returned at runtime by getAnnotation. One potential difference not worth considering with modern virtual machines is the speed difference between invoking a method on an interface versus invoking a method on a class.

While there is a sound rationale backing the conventional wisdom, in my estimation the compatible evolution advantages of abstract classes are smaller than they appear at first, further tipping the balance in favor of using interfaces in more situations.

The two alternatives to be considered to define the initial desired type abstraction are:

  • Declare an interface.

  • Declare an abstract class, all of whose initial methods are public and abstract.

In neither case are fields being defined. In both cases a skeletal abstract implementation class, like java.util.AbstractList, could be used to share implementation code. If the type abstraction is defined by an abstract class, the skeletal class and abstract class might be able to be combined, saving a type compared to the pair of an interface plus a skeletal class. However, forcing all implementations to be based on the same skeletal class may be awkward. Interfaces can easily have multiple independent skeletal helper classes. Subclasses can blunt inheritance issues by using an intermediate subclass to abstract-ify any problematic implementations from the parent.

Table 1 outlines the different kinds of compatibility impacts, source, binary, and behavioral, from adding a method to an interface and an abstract class. The effects of adding a method to an abstract class depend on whether or not the added method is abstract or has an implementation. For the purposes of discussion, we will assume the method does have an implementation (otherwise, there would be no advantage to using an abstract class).

Table 1 — Compatibility summary of adding a method
Interface Abstract class
Binary compatibility Adding a method to an interface is binary compatible. Note that existing clients will continue to link, but attempted calls to the missing new method will result in an AbstractMethodError. Adding a method to an abstract class is binary compatible.
Source compatibility Adding a method to an interface has the full range from possible impacts, from being binary-preserving source compatible to breaking compilation. Adding a method to an abstract class has the full range from possible impacts, from being binary-preserving source compatible to breaking compilation.
Behavioral compatibility No direct behavioral impact to existing code calling existing methods. No direct behavioral impact for the cases under consideration.

Technically, adding a method to an interface and adding a method to an abstract class are both binary compatible since programs using those types will continue to link. However, in the case of an interface type, if a program calls the new method on an existing implementation of the interface (unless the implementation presciently had a method with a matching signature declared), an AbstractMethodError will be thrown, which is an awkward situation to recover from. Also, for the call to the new interface method to work on an existing implementor of the old interface, the method in the implementor must be an exact match, signature and return type, for the added method; if the return type in the implementor is a subtype of the added method, a covariant return, a recompile of the implementor is needed to create the bridge method joining the method from the interface with the method declared in the class.

Adding a method to an interface has a wide range of possible source compatibility effects on existing code. It is possible that an implementation anticipated future developments and already has a method matching the newly added method. In that case, adding the method is binary-preserving source compatible with that particular class. Of course in general it is much more likely that existing implementations do not already have the new method, in which case they won't compile against the modified interface declaration. Therefore, the worst possible outcome is that existing implementations will stop compiling after the method is added to the interface; this worst case outcome is also the most likely outcome in the absence of other information.

Adding a concrete method to an abstract class also has a range of source compatibility outcomes. If no existing extending class has a method with the new name, there is no conflict and the addition is binary-preserving source compatible given the set of actual programs. If not the expected outcome, this is certainly the hoped for outcome of adding a method to an abstract class! However, it is possible existing subclass already declare a method with the new name. If the parameter types match but the return types conflict, existing subclasses will stop compiling after the method is added. If the parameter types are not the same, an overloading situation is introduced or expanded. This can change method resolution of call sites using the existing subclass, which may or may not lead to behaviorally equivalent class files since different methods might be called. One technique to avoid changing resolution at existing call sites is for the new method to include in its parameter list a new type added at the same time as the method. If the new type is not related to existing types, then no method in an existing subclass will interact with the new method during method resolution. Therefore, the worst possible outcome is that some existing subclasses will stop compiling after the method is added to the abstract class; this can be avoided depending on the parameter list of the new method, at the potential cost of introducing new overloadings that change existing method resolution.

Not counting introspective operations like core reflection, adding methods to an interface or abstract class does not have much direct appreciable behavioral compatibility impact because adding methods doesn't directly affect the code run by existing clients of the class. If an abstract class were not at the conceptual root of a type hierarchy, adding a concrete method could intercept calls to a method with the same signature in the superclass. However, if the children of an abstract superclass already have a concrete implementation for the newly added method, existing calls to the children's method would not be intercepted by the method added in the superclass.

Since adding a method to an interface or an abstract class is binary compatibly and in both cases the worst case source compatibility outcome is breaking compilation of existing subtypes, any evolution advantage of abstract classes hinges on the ability to have a reasonable default implementation for new methods. But what can such a new method implementation really do? Some viable options are:

  • Throw new UnsupportedOperationException or some other exception.

  • Call existing methods on the abstract class.

  • A no-op method.

(Other sorts of behavior could potentially be added to skeletal classes, but those classes aren't an alternative to interfaces.) Adding a default implementation that throws an exception isn't necessarily very useful; throwing AbstractMethodError would mimic adding a method to an interface! If the functionality of the new method can be expressed in terms of existing methods on the abstract class, the new method could also be written as convenience static method in a helper class. In that case, the convenience method could just as easily be written in terms of methods on an interface instead. Proposals for extension methods would add syntactic support for this helper class pattern. A no-op method could be added to optionally advise subclasses to some condition or event, but it would have no useful effect on existing subclasses. While it is straightforward to add simple concrete methods to an abstract class, with sufficient advance planning, such methods could also be automatically added to implementations of an interface at compile time.

Starting in JDK 6, Java compilers must support standardized annotation processing. Annotation processing is a general meta-programming framework not directly tied to annotations. Before annotation processing, the types being compiled can be incomplete, including references to types to be generated during annotation processing. The to-be-generated types can include the superclass of a class being compiled. Supporting the generation of superclasses is a very powerful technique for modifying the semantics of the child class. In this case, a class implementing an interface expected to change in the future could refer to a private superclass. With the original definition of the interface, the superclass would be empty. However, when methods were added to the interface, the annotation processor could generate implementations of those methods in the superclass. This would have the effect of adding the new methods to the class at compile time. Annotations could drive what the synthesized implementation actually did, such as throw an exception or a no-op.

Compared to adding methods to an interface, adding concrete methods to an abstract class seems to be much more compatible. However, both operations are binary compatible, and while adding a method to an abstract class usually has a better "average" impact on existing subtypes, the worst possible impact is the same, breaking the compilation of existing code. As for the functionality that can be added in a concrete method, convenience methods can be put in separate class and the other sorts of limited functionality methods that can readily be added could also be generated via annotation processing for implementors of an interface. Therefore, the practical evolutionary benefits of using an abstract class rather than an interface should be considered carefully since interfaces may still be a better choice when limited evolution is anticipated.

(2008-05-12 18:43:29.0) Permalink Comments [4]

20080508 Thursday May 08, 2008

JavaOne: Java + You = ...

In this year's JavaOne pavilion, you can get shirt's printed with your own answer to this year's conference theme posed as a question

JAVA + YOU = ?

While "JAVAYOU" would be a string-centric programmatic answer, with my floating-point czar hat on, my answer to this summation is "K9K4", which I computed with the following program:


public class JavaPlusYouSum {
    private static final String JAVA = "JAVA";
    private static final String YOU  = "YOU";
    private static final int RADIX = 36;

    public static void main(String... args) {
	int sum =
	    Integer.parseInt(JAVA, RADIX) +
	    Integer.parseInt(YOU, RADIX);
	
	System.out.printf(JAVA + " + " + YOU + " = " + 
			  Integer.toString(sum, RADIX));
    }
}

However, I'm confident less numerical answers will be more useful and satisfying in most contexts :-)

(2008-05-08 10:00:00.0) Permalink

20080501 Thursday May 01, 2008

OpenJDK: jtreg and regression tests

Huzzah! Through the dedicated efforts of Jon and others, jtreg is now open sourced! The jtreg program is the test harness used to run the regression tests that come with the JDK sources.

The JCK tests verify properties that should be true of all implementations of a given Java SE specification. The JDK regression tests are different; while many of them test properties that should be true of all implementations, some regression tests look at properties we want to be true of our JDK implementation but are not strictly required by the specification. Therefore, while a failing regression test most likely indicates a problem, in some cases the failure may not be a correctness issue per se. This situation is certainly feasible with ports of the JDK to operating systems sufficiently different than windows, Solaris, and Linux; shell tests are especially susceptible to those OS differences. Creating new shell tests should be avoided if possible and the porting effort may include updating regression tests to make them aware of the new platform.

(2008-05-01 22:45:00.0) Permalink

Calendar

« May 2008 »
SunMonTueWedThuFriSat
    
2
3
4
5
6
7
9
10
11
13
15
17
18
19
20
21
22
23
24
25
26
28
29
30
31
       
Today

RSS Feeds

XML
All
/Annotation Processing
/General
/Java
/JavaOne
/Numerics
/OpenJDK

Search

Links

    Blogroll
  • Download the JRE

    News

Navigation



Referers

Today's Page Hits: 44