Home Design Patterns Marker Interface

Marker Interface

Modern Practice

Tag 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.

UML class diagram: an empty Serializable interface implemented by ArmProgram and ToolPath, with a Persister class that tests for it.
The marker box is empty, and that is not an omission. Nothing flows through the interface at runtime; the only thing that crosses it is the fact that the implementing class chose to declare it.

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
    }
}
Two lines carry the pattern: the empty 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 Cloneable is 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.