Home Design Patterns Marker Interface
Marker Interface
Modern PracticeTag a type with an empty interface so other code can ask what it is allowed to do.
Purpose
A Marker Interface is an interface with nothing in it. No methods, no
constants, no properties. A class implements it and gains no members, no
obligations and no behavior. The only thing that changes is the answer to a
question: is this type a Serializable? The type system is being used
to carry a single fact about a class, and the fact is carried by presence alone.
That sounds like a trick, and it is, but it is a useful one. Some facts about a class genuinely cannot be expressed as a method. "This object is safe to write to disk" is not an operation the object performs; it is a permission the author of the class is granting. A marker is where you put a permission when the language gives you nowhere else to put it.
Design
There are two ways to consume a marker, and choosing between them is the whole
design decision. The first is a runtime test: if (item is IArchivable).
That is what most frameworks do, because a framework is usually handed
object and has to work out what it has. The second is a parameter
type: void write(IArchivable item). That version is checked by the
compiler, and code that hands it the wrong class does not build.
The implementing class does exactly one thing: it names the interface in its declaration. There is no method to write, which is why a marker costs a class almost nothing to adopt and why it is so easy to adopt carelessly.
Example
Autobot Robotics keeps a job archive on the office side: every completed job
is written out so a customer can reproduce a run months later. The archive
serializer started life taking object and walking whatever it was
given, which worked until somebody archived an ArmLink. An
ArmLink holds a live socket to a controller and a handle to a memory
mapped I/O region. It serialized without complaint, produced a file full of
integers that used to be pointers, and failed on reload with an error that
pointed at the archive reader rather than at the code that had written rubbish
into it three weeks earlier.
The fix was an empty IArchivable interface and a change to one
method signature. Domain records that were safe to write out declared it. Nothing
that owned a live resource did. The archive writer stopped taking
object and started taking IArchivable, and the call that
had caused the problem became a compile error instead of a corrupt file.
What made it worth doing was not the tagging. It was that the error moved from "discovered by a customer, three weeks late, in the wrong component" to "discovered by the build". An attribute would have tagged the classes just as well and caught nothing.
interface IArchivable { } // no members at all
class JobRecord : IArchivable
{
// plain data the archive is allowed to see
}
class ArmLink
{
// holds a live socket. Must never be archived.
}
class JobArchive
{
// Compile-time form. The marker IS the parameter type, so the
// check happens in the build and costs nothing at runtime.
public void write(IArchivable item)
{
// some code
}
// Runtime form. Same question, but the answer arrives as a
// thrown exception rather than as a build failure.
public void writeAny(object item)
{
if (item is IArchivable)
{
// some code
}
else
{
throw new ArgumentException("not archivable");
}
}
}
class Client
{
public void run()
{
JobArchive a = new JobArchive();
a.write(new JobRecord());
// a.write(new ArmLink()); // does not compile
}
}
interface IArchivable { }, and the signature
write(IArchivable item). Only the second one buys you anything an
attribute could not do.Seen in the Wild
Java is where the pattern lives. java.io.Serializable and
java.lang.Cloneable are both empty interfaces, and so are
java.rmi.Remote and java.util.EventListener.
java.util.RandomAccess is the most instructive of them: it declares
nothing, and Collections.binarySearch and
Collections.shuffle test for it at runtime to decide whether to
index into the list or walk it with an iterator, which changes an algorithm from
quadratic to linear on a LinkedList. .NET went the other way and
used an attribute, [Serializable], for the same job, which is the
clearest side-by-side comparison of the two approaches that exists.
Comparisons
- Interfaces normally exist so a caller can invoke something through them. A marker is the degenerate case where the interface is never called at all, only tested for.
- Extension Object asks a very similar question, but about an object rather than a type, and answers it at runtime with an object rather than a boolean. A marker says "this kind of thing is allowed to X". An extension object says "this particular thing can do X, here is how".
- Prototype is what
Cloneableis attached to in Java, and the marker is the reason that implementation is so awkward. See the criticisms. - Null Object is the other pattern built out of emptiness. A null object implements an interface and does nothing; a marker interface has nothing to implement.
Why It Exists
My take
Marker Interface exists because Java 1.0 had no way to attach metadata to a
class. Serializable shipped in 1997; annotations did not arrive
until 2004. If you needed to record a fact about a class and have the runtime
read it back, the type hierarchy was the only writable surface available. So the
fact got encoded as a supertype, because there was nowhere else. That is not a
design insight, it is a workaround for a missing feature, and most of the
pattern's reputation comes from people mistaking the second thing for the
first.
Annotations and attributes won, and they deserved to. They carry parameters, they can target a method or a field rather than only a whole class, they can be retained or discarded per build, and they do not pollute the type hierarchy. A marker can express exactly one bit and can only attach it to a class. If your answer to "why is this an interface rather than an attribute" is anything other than the next paragraph, it should be an attribute.
The one thing a marker still buys you is compile-time checking, and it is not
a small thing. void write(IArchivable item) is enforced by the
compiler. [Archivable] is enforced by a reflection call that runs
when somebody exercises that path, which in a large system may be in production
on a Friday. In the environments I have spent most of my time in the argument is
stronger still: an ahead-of-time compiled embedded target may have reflection
stripped out or restricted entirely, and a marker check that the compiler
resolves statically costs zero bytes and zero cycles at runtime. A pattern that
is obsolete on the server can still be the only option on the device.
Criticisms
The killing objection is inheritance. A marker is inherited by every subclass,
and there is no way to un-implement an interface. Declare a base class
Serializable and every class anyone ever derives from it is
serializable too, including the one somebody adds next year that holds a file
handle. The tag was a statement about one class and the language turned it into a
statement about an open-ended family of classes that did not exist when the claim
was made. Attributes are not inherited by default, and that default is correct.
Cloneable is worth singling out because it is the pattern's worst
outing and Java's own authors say so. Josh Bloch, who wrote much of the
collections framework, calls it a highly atypical use of interfaces and not one to
be emulated. The problem is that Cloneable does not declare
clone(). It modifies the behavior of a protected method on
Object: implementing the interface is what stops
Object.clone() throwing. So the marker changes what an inherited
method does rather than adding one, which means a class can implement
Cloneable and still not be clonable through its own public API, and
callers cannot call clone() through the interface because the
interface does not have it. Almost every criticism of a marker interface is
visible in that one example.
Beyond those, markers carry no data, so the moment you need "serializable, but
only version 2 and above" you are stuck adding a second marker, then a third.
They also produce types that typecheck and mean nothing: List<Serializable>
is a perfectly legal declaration that says nothing useful about its contents. And
because implementing one costs no work at all, they get added by reflex. A marker
with hundreds of implementers is not carrying information any more; it is
describing the codebase rather than distinguishing anything within it.