DEV Community

Kyle Carter
Kyle Carter

Posted on

Effective Java! Use Marker Interfaces to Define Types

In this chapter marker interfaces and marker annotations are discussed. A marker interface is an interface that has no method declarations but simply is used to mark an implementing class as having a certain attribute. An example of a marker interface is the Serializable interface built into the core of Java. Another similar thing you may hear about is of marker annotations. These serve a similar purpose of marking a specific item as having an attribute but, as the name suggests, uses an annotation instead of an interface to accomplish its task. So should we use one versus the other?

Marker interfaces, as all interfaces do, define a type. This is really where the core of when they should be used comes from. In defining a type we allow ourselves to use that power to help us at compile time. Let's consider the above example of Serializable. The main consumer of this interface, ObjectOutputStream.write, could use this ability of knowing it requires an object of the Serializable type to define it's write method as taking a Serializable object. If this was done we would know at compile time (or more likely coding time because of our IDE's help) when we were trying to pass an object that isn't going to work to this method. Unfortunately the designer of this class didn't take the opportunity to make the interface this way and it instead takes an Object but it still is instructive of a good potential use for a marker interface. Other times when we should use a marker interface is when we see that marker interface as only applicable to subtypes of a certain parent interface. In this case we simply extend the parent interface and then we know that all classes implementing this interface are also of the parent interface. As stated above, if deinfing a type makes sense for the marker you are creating, marker interfaces are the way to go.

So when do marker annotations make sense? Some of the things can lead us to using one of these instead of interfaces is when we are marking something other than a type. This is obviously impossible with an interface so naturally fits in here. We can also gain value of cohesiveness when working within an annotation heavy framework to follow the norm and use marker annotations rather than marker interfaces. This is nice in that the framework all feels put together and you aren't using interfaces sometimes and annotations at other times.

As often is the case when making decisions between two techniques in development, whether to use a marker interface or marker annotation largely comes down to use case. Both of these methods have valid use cases and by understanding those use cases we can make the best decision when writing our code now.

Discussion (5)

Collapse
nfrankel profile image
Nicolas Frankel

Marker interfaces were the only way to go before Java 5's annotations. Since annotations, there's no valid reason to come up with an interface with no methods.

Interfaces are meant to design a contract that all implementors need to follow. Serializable and other marker interfaces are legacy, and should never ever be used as examples of what to do.

Collapse
kylec32 profile image
Kyle Carter Author

I definitely agree that interfaces most often are used to define a contract and it does muddie the waters to use them as marker interfaces. I think the author is simply pointing out that marker interfaces do have interesting attributes that annotations don't give you, namely compile time checking vs runtime checking. While I personally have never used an interfaces this way it is an interesting attribute of marker interfaces, and one that can be weighed against the shortcomings of the pattern as well.

Collapse
nfrankel profile image
Nicolas Frankel

Can you please provide a concrete example of this advantage?

Thread Thread
kylec32 profile image
Kyle Carter Author

Sure. For example if my method takes a Set. Set adds nothing new to the Collection interface other than some JavaDoc changes in my reading of the two interfaces. If I wanted to force the implementation of Set that would mean I don't require anything above a Collection interface but I could enforce that implementation detail at compile time.

As stated above the ObjectOutputstream.writeObject method is a missed opportunity for forcing at at compile time compliance in that only objects that implement Serializable can successfully be processed by this method. Of course these classes were added to Java before annotations so they can't use annotations. However, they act the same way that annotations would act today (checked at runtime). The author is pitching that if the original designer would have made the method take Serializable rather than Object it would be slightly easier to work with the method and more self documenting.

Thread Thread
nfrankel profile image
Nicolas Frankel

I'll focus on Serializable, because Set cannot be replaced by annotation. Worse, its "contract" is enforced by nothing.

Regarding Serializable, it should be the class that offers the writeObject() method. It should also be a default method, just as List.sort() is. For the whole rationale behind default methods, please check this post.