The Artima Developer Community
Sponsored Link

Dr. Dichotomy's Development Diary
Negatable Marker Annotations
by Eamonn McManus
March 9, 2005
Summary
The Java language has always had the notion of marker interfaces. A problem with these is that if a parent class has the marker, all its children inevitably do too. On the other hand, marker annotations don't necessarily have this limitation. Here's how to define a marker that can be cancelled in a subclass.

Advertisement

A marker interface is an interface with no methods, which a class can implement to show that it has certain properties. Well-known examples are Cloneable and Serializable. A class can implement Serializable to indicate that it has been designed to be serialized, for example.

One problem with marker interfaces is that when a class implements an interface, all of its subclasses inherit that implementation. Since Number implements Serializable, any subclass such as Integer or AtomicInteger does too.

What can you do if you want to define a subclass of Number that is not Serializable? You are forced to resort to a hack: define a writeObject method that throws NotSerializableException. Instances of your class will still appear to be serializable (instanceof Serializable is still true) but will not actually be. Yuck.

Annotations, introduced with Tiger (J2SE 5), provide an alternative to marker interfaces. If serialization were being defined now, then instead of specifying serializability like this...

public class Number implements Serializable {...}

...you might specify it like this...

@Serializable
public class Number {...}

Annotations can be defined such that they either are or are not inherited by subclasses. We could decide that the hypothetical @Serializable annotation is inheritable when we define it:

@Inherited
public @interface Serializable {}

In this case, subclasses of our @Serializable Number class like Integer are automatically @Serializable themselves. This is usually what you want, but just as with the marker interface there is no way to cancel serializability in a subclass. Once an inheritable marker annotation is in a superclass, a subclass can't turn it off.

Alternatively, we could omit the @Inherited annotation from the definition of @Serializable. Then Integer is not automatically @Serializable even if Number is. We can explicitly choose whether or not to make it so. But we no longer get the behaviour that we usually want, which is that a class is automatically @Serializable if its parent is.

We could define a @NotSerializable marker annotation, just as we could define a NotSerializable marker interface, so a subclass could override the inherited @Serializable by saying it was @NotSerializable after all. But how ugly.

Fortunately, there is a nicer solution using what I'll call negatable marker annotations. The idea is simple. We change the definition of @Serializable to this:

@Inherited
public @interface Serializable {
    boolean value() default true;
}

With this definition, we can still define Number as before:

@Serializable
public class Number {...}

Subclasses of Number are now automatically @Serializable too. But if we want to define a subclass that is not, we simply write:

@Serializable(false)
public class UnserializableInteger extends Number {...}

It could hardly be simpler!

When we write @Serializable with this definition, it's equivalent to writing @Serializable(true). We can omit true since it's the default value, and then there's nothing left but @Serializable(). The annotation syntax allows us to omit the empty parentheses too, and that leaves us with plain @Serializable.

The code that uses reflection to know whether a class is @Serializable or not is different with this definition. With a plain marker annotation, we would use:

Class c = whatever;
Serializable ann = c.getAnnotation(Serializable.class);
boolean isSerializable = (ann != null);

With a negatable marker annotation, we must use:

Class c = whatever;
Serializable ann = c.getAnnotation(Serializable.class);
boolean isSerializable = (ann != null && ann.value());

The class is serializable if the @Serializable annotation is present (ann != null) and the value of the annotation is true (ann.value(), which we could also write as ann.value() == true).

Negatable marker annotations can have other applications than cancelling inherited annotations. For example, suppose you have a convention that an interface called SomethingMBean is an "MBean interface", whatever that might be. You can add a negatable @MBean annotation to supplement this convention. You can say that an interface is an MBean interface even if isn't called SomethingMBean, by using @MBean. And, you can say that an interface is not an MBean interface even if it is called SomethingMBean, by using @MBean(false).

Negatable marker annotations also allow you to be more explicit. With plain marker annotations, if a class is marked @Serializable then you know that its designers intended it to be serializable. But if it is not marked @Serializable, you only know that either its designers intended it not to be serializable or they didn't think about the question. A negatable marker annotation allows you to write @Serializable(false) to be explicit about it.


I haven't seen this idea elsewhere, but I would amazed if it has not been independently invented several times. Let me know!

Talk Back!

Have an opinion? Readers have already posted 7 comments about this weblog entry. Why not add yours?

RSS Feed

If you'd like to be notified whenever Eamonn McManus adds a new entry to his weblog, subscribe to his RSS feed.

About the Blogger

Eamonn McManus is the technical lead of the JMX team at Sun Microsystems. As such he heads the technical work on JSR 3 (JMX API) and JSR 160 (JMX Remote API). In a previous life, he worked at the Open Software Foundation's Research Institute on the Mach microkernel and countless other things, including a TCP/IP stack written in Java. In an even previouser life, he worked on modem firmware in Z80 assembler. He is Irish, but lives and works in France and in French. His first name is pronounced Aymun (more or less) and is correctly written with an acute accent on the first letter, which however he long ago despaired of getting intact through computer systems.

This weblog entry is Copyright © 2005 Eamonn McManus. All rights reserved.

Sponsored Links



Google
  Web Artima.com   

Copyright © 1996-2019 Artima, Inc. All Rights Reserved. - Privacy Policy - Terms of Use