Monday Jun 13, 2005

Creating Nested (Complex) Java Annotations

When creating new annotations, it doesn't take long to get to the point of wanting to use what are referred to in the JSR-175 specification as complex annotations. Before we talk about complex annotations, as a reminder, non-complex, simple annotations look like this when used:

@SimpleAnnotation(a="...", b=3)
public Object foo() {...}
In this simple case, annotation members are scalar values, in this case "..." and 3.

By contrast, complex annotations may contain other annotations in a nested fashion. They look like this when used:

@ComplexAnnotation(
    @SimpleAnnotation(a="...", b=3)
)
public Object foo() {...}
Complex annotations can also define arrays of members too:
@ReallyComplexAnnotation(
    { @SimpleAnnotation(a="...", b=3), @SimpleAnnotation(a="...", b=4) }
)
public Object foo() {...}
Let's look at the declarations for the annotations above. The simple annotation is pretty straightforward:
@Target(ElementType.METHOD)
public @interface SimpleAnnotation {
    public String a();
    public int b();
)
The declaration of the first complex annotation example above is the following:
@Target(ElementType.METHOD)
public @interface ComplexAnnotation {
    public SimpleAnnotation value();
)
This is not too much different at first glance, but note that the type of the member value is now an annotation itself. This is how complex, nested annotations work. In fact, it's illegal for an annotation to declare a member that is not either a simple type (int, float, String, etc.) or an annotation. For example, you can't define an annotation that returns java.lang.Object. The reason for this is that annotation values must be constant expressions to the compiler, and there is no way to express an instance of java.lang.Object in source code such that it can be statically evaluated by the Java compiler. (You might at this point argue that string literals in source code can be concatenated with plus signs--doesn't that result in a non-constant expression? No, it doesn't. This is actually a trick of the compiler; it statically concatenates string literals at compile-time, so in the compiled code there is no difference between "ABC" and "A"+"BC".)

Let's move on the the next example where we declare an array member:

@Target(ElementType.METHOD)
public @interface ReallyComplexAnnotation {
    public SimpleAnnotation[] value();
)
Very straightforward; just add the square brackets onto the type, just like you're used to doing elsewhere in Java code.

Now here comes something a bit trickier. Annotation members can have default values so that users of the annotation don't need to specify a value for a member in every annotation. In this sense, the member value is optional in that it can be omitted by the user of the annotation, though the value will be the default specified by the annotation type. The way to declare this is quite easy:

@Target(ElementType.METHOD)
public @interface SimpleAnnotation {
    public String a();
    public int b() default -1;
)
Now, the user of the annotation can leave out the value of b, in which case its value will be -1:
@SimpleAnnotation(a="...")
public Object foo() {...}
So far, so good. Now, what happens when we want to specify a default for an array member? The logical default for an array would be an empty array, but how do we express this? Based on your previous Java experience, you may be tempted to use one of the following:
@Target(ElementType.METHOD)
public @interface ReallyComplexAnnotation {
    public SimpleAnnotation[] value() default new SimpleAnnotation[0]; // wrong!
)

@Target(ElementType.METHOD)
public @interface ReallyComplexAnnotation {
    public SimpleAnnotation[] value() default SimpleAnnotation[0]; // wrong!
)

@Target(ElementType.METHOD)
public @interface ReallyComplexAnnotation {
    public SimpleAnnotation[] value() default null; // wrong!
)
Each of these is incorrect, however. The first is not a constant expression (the keyword new necessarily indicates an expression that can only be evaluated at runtime). The second is simply not valid Java. The third, specifying null, though seemingly a constant expression, is always an invalid expression according to the JSR-175 specification.

Here's the proper way to declare the empty array:

@Target(ElementType.METHOD)
public @interface ReallyComplexAnnotation {
    public SimpleAnnotation[] value() default {}; // right!
)
You should recognize this expression, though I admit it wasn't immediately obvious to me.

OK, now it's time for Final Jeopardy: how do you declare a default for a non-array annotation member? If you're like me, you're immediately tempted to specify null, because that's what we normally do in Java code. But, remember, null is always an invalid default expression according to the spec. Here are some variations you might try:

@Target(ElementType.METHOD)
public @interface ReallyComplexAnnotation {
    public SimpleAnnotation value() default null; // wrong!
)

@Target(ElementType.METHOD)
public @interface ReallyComplexAnnotation {
    public SimpleAnnotation value() default SimpleAnnotation; // wrong!
)

@Target(ElementType.METHOD)
public @interface ReallyComplexAnnotation {
    public SimpleAnnotation value() default new SimpleAnnotation(); // wrong!
)

// arghhh!!
The compiler error for each of these attempts is "annotation value must be an annotation". This is utterly confusing at first, but does give you a hint. We need to return an annotation as the default, so how do we get one? How do we normally get other annotations? We simply type them:
@Target(ElementType.METHOD)
public @interface ReallyComplexAnnotation {
    public SimpleAnnotation value() default @SimpleAnnotation(a="..."); // right!
)
This seems completely counter-intuitive at first because we are using the "annotate X" syntax with the @ sign, but there is no apparent "X" to be annotated here. Furthermore, the target for the annotation seems to be ignored. Indeed, this seems like an inconsistency, but think of this syntax instead as specifying the annotation that will be used by default if no annotation is specified. If you think of it this way, it is pretty obvious how this works--it's as if the author had typed the default annotation in their code. Because everything is a constant at compile-time, there is no inconsistency in terms of specifying the annotation here instead of in the user's code.

Monday Apr 11, 2005

Bridge methods in compiled Java classes

Some Java 1.5 trivia for Monday:

Q: What are bridge methods?

A: Bridge methods are mentioned in the JavaDoc for java.lang.reflect.Method, but there is no mention of them in the second edition of the JLS. I hunted around a little bit and found this excellent description. In a nutshell, bridge methods are a side effect of type erasure as the compiler compiles generic types. Just like the automatic addition of null constructors to classes that don't declare any constructors, bridge methods are some of the hidden magic the Java compiler performs on our behalf.

In the example given in the article, there are two compareTo() methods in the compiled class. The bridge method is the one with the parameter type of Object. When using reflection, you may want to know whether a Method instance you are looking at is (or is not) a bridge method. Thankfully, the Method class now includes a method called isBridge() to allow you to determine if the method is a bridge method or not. Just remember that in subclasses/implementations of a generic type like Comparator, the bridge method is generally the one that you want to find instead of the original method! That is, the bridge method is the one that satisifies the contract of the supertype; the original method is just another arbitrary method as far as the compiler is concerned.

Thursday Mar 17, 2005

Sexy Swing Techniques

Maybe I'm the last to know, but Romain Guy has a very interesting blog detailing many cool and interesting techniques to sexify your Swing applications. For example, he integrates Java 3D into some Swing apps (in a useful way no less) and mimics some of MacOS X's UI features. The best compliment I can give is that none of his apps looks like Swing apps. :) Most of his entries have source code and a link to see demos in action using WebStart. Very impressive Romain, keep it up!

Wednesday Mar 02, 2005

Handy XForms Tutorial

Here's a nice XForms tutorial from our friends over at W3 Schools.

Monday Feb 28, 2005

A "Generic" Parody

For those of you confused, elated, or otherwise opinionated about the addition of generics to Java, check out this hilarious parody from Spud over at madbean.com.

Saturday Feb 12, 2005

Chiba: a Java-based XForms implementation

From the Chiba homepage: "Chiba is an Open Source Java Implementation of the W3C XForms standard 'that represents the next generation of forms for the Web'." Unlike most open source projects, the documentation for Chiba is terrific. Also check out the official XForms page at the W3C.

Monday Feb 07, 2005

The Missing Link: From Objective-C To Java

I took a different professional trajectory than most of my peers: I received a degree in mechanical engineering but quickly decided to become a "corporate" developer using Paradox, Delphi, VB, and PL/SQL instead. Although I'd been programming since about age 10 on various pre-PC machines, to get where I am today, I had to slowly work my way down through layers of abstraction and technology on my own without the benefit of a formal computer science education (this has its benefits I suppose, but I can't really recommend it). As a mechanical engineering student a couple of years before the Web was invented (yikes!), my exposure to then-current technology was limited. For a time, various engineering algorithms in FORTRAN running on VAX mainframes were a daily headache (along with a semester's addiction to a MUD), though over time there were increasing uses for Macintosh word-processing and DOS-based MATLAB. In any case, as a result of this history, I'm generally quite interested in programming languages and various technologies outside my daily experience. So, over the weekend, I did some reading about Mac OS X and Cocoa to look at things from another perspective. This led me quickly to Objective-C, Cocoa's language of choice.

In the past, I'd skimmed information on Objective-C, but at the time its similarity to Java escaped me. I'd also previously studied Smalltalk and saw the similarities to Java there, but it wasn't until I read Apple's Objective-C description that I realized that much of the Smalltalk I saw in Java was filtered through Objective-C and OpenStep instead (of which the latter, not coincidentally, my employer was a big part). That Objective-C was an inspiration for Java isn't exactly a secret, yet no one seems to ever mention it, even here at Sun. What I was most surprised by, however, was the sheer level of similarity between the two languages.

Objective-C is so similar to Java that it's a fairly trivial matter to adjust one's Java mindset to it. Many of the things I love about Java are present in Objective-C. For example, Objective-C encourages interface-based development using what it calls protocols. Objective-C also has a strong runtime component, which normalizes its usage across platforms and offers a very high level of built-in capability. Objective-C even has a couple of features I wish were carried over into Java, like categories. (Poor choice of name but oh, how I've wished for such a feature...) There are some significant differences too, like the Smalltalk-inspired syntax addition to C and the fact that message selectors (method names) exist in a global namespace without regard to number or type of message parameters (this is similar to XML without namespaces, which was a problem when it was initially released). The lack of garbage collection is also a major difference.

When reading the Objective-C language description, it's easy to see how certain features were streamlined to create their Java counterparts. For example, it's obvious that Objective-C's use for the id pseudo-type apart from the NSObject formal type was obviated in Java by eliminating the C legacy. One day I'll have to ask James what his perspective is on the amount that Objective-C influenced Java. To me, however, although I know it wasn't the only influence, when reading about Objective-C, it's almost impossible not to feel that Java evolved as a network-aware, small-footprint alternative. There certainly was, and still is, a place for Java as a distinct language and platform, but in some ways it's too bad Objective-C didn't go further. There are surely many reasons for this, but I credit Objective-C's legacy as one stumbling block. Its C and NeXTSTEP origins introduced cruft that was nicely eliminated in Java, with the result being a far more consistent and friendly language--at least until generics were added. <grin> (Don't get me wrong, I love the support for generics introduced in JDK 1.5, but it doesn't do anything to make the Java language easier to read or understand.)

Although nothing will keep me from Java, it'd be nice to jump over into Objective-C once in awhile, if for no other reason than as a change of pace. But, there are a couple of hurdles. First, there isn't a good implementation of OpenStep that is sufficiently cross-platform. This is one of the things I love about Java, and although GNUstep is attempting to provide a portable OpenStep implementation, they're falling short for at least one major platform, and in particular their AppKit implementation is (still) lame. Second, Apple has extended Cocoa nee OpenStep in ways that are very attractive, but again, unavailable anwhere but on the Mac platform (as intended I'm sure). Finally, there are no good-but-inexpensive tools for Objective-C except Xcode and Interface Builder on the Mac; as nice as these tools are, in most ways they aren't really up to the level of capability of a modern Java IDE.

Even though it doesn't seem likely that I'll be writing Objective-C code any time soon (particularly since Cocoa supports the Java language nearly as well), learning about it and its history has been an eye-opening journey, and superb insight into the history of the programming language I use daily. Hats off to Apple for keeping Objective-C not just alive, but vibrant, and to the innovations in language and tools from which we unwittingly benefit today because of it.

Friday Dec 03, 2004

Custom Datatypes in RELAX NG Using Java

Another RELAX NG goodie written by the XML Bible geek, Elliotte Rusty Harold: RELAX NG with custom datatype libraries: Define new types with Java technology.

To give you an idea of what this article is about, several RELAX NG schema validators (like Sun's Multischema Validator, or MSV) let you plug in custom datatype validation using simple Java constructs. This capability can be used to go (way) beyond traditional document validation, even to the extent of verifying that a piece of XML data fulfills requirements above and beyond its syntax. For example, it would be possible to verify that a document contains a valid credit card number (and maybe even post a fund reserve) with an online clearinghouse during the validation phase, rather then relying on the application layer to do this sort of validation at a later stage. Such semantic validation is extraordinarily powerful.

Dynamic Delegation in Java

This O'Reilly article, Dynamic Delegation and Its Applications, discusses dynamic interception of method invocations for concrete Java types using code from the Dunamis Project. This is a sorely lacking feature of the JDK, and quite useful, especially for use in lightweight containers or enterprise frameworks.

Wednesday Nov 03, 2004

Dispelling NIO myths

An interesting article on the JDK's NIO features and the EmberIO library.

Cross-connected neurons - biology and programming langauges

After doing some reading about genetics and taxonomic systems in biology, it struck me how similar principals were at work in programming language design. Specifically, object-oriented languages that include classical inheritance tend to take a phylogenetic approach to structuring software systems. That is, they classify software structures in terms of their inheritance, or "is-a" relationships.

Some languages, however, also include a morphogenetic component in their design. That is, they allow classification of software structures in terms of their "shape" or how they "look". A better way to say this might be to classify objects according to their interfaces, or supported operations. The actual inheritance of an object (or phylogeny) is of less importance than the way it "looks" (or morphology) to users of the object.

Java is an example of a language that supports both phylogenetic and morphogenetic classification, primarily through its use of interfaces that can be used to make an object "look" like a particular type without actually inheriting from a particular parent type. (Admittedly, this analogy isn't perfect, since there is still an "is-a" relationship even when using interfaces. However, with the exception of tag interfaces, the primary use for an interface in Java is to specify the set of operations a class defines.) ECMAScript, however, with its prototype-based design, is fully morphogenetic. The only thing that makes an ECMAScript object classifiable is its set of members. (Here is a nice article on ECMAScript's morphogenetic features.)

Do I really have to post this?

Hopefully this is preaching to the choir, but here is a nice Allen Hollub article advocating interface-based programming. It's notable for its nice summation of points.
About

toddfast

Search

Categories
Archives
« April 2014
SunMonTueWedThuFriSat
  
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
   
       
Today