00:00

Marker Interface in Java – Tagging Classes with Empty Interfaces

A marker interface (or tagging interface) is an interface with no methods and no constants. Implementing it does not add behaviour; it simply marks a class as having a certain property. Other code, such as the JVM, a library or your own framework, checks for the marker with instanceof and treats marked objects differently.

This tutorial covers the JDK's marker interfaces (Serializable, Cloneable, RandomAccess), how to create your own, and how marker interfaces compare with marker annotations.

1. What Is a Marker Interface?

Marker interface
public interface Serializable { }      // the entire declaration in java.io

class Customer implements Serializable { ... }   // "Customer objects may be serialized"

The marker carries meaning through the type system: an object is either an instance of the marker type or not. Code that cares checks obj instanceof Marker or declares parameters of the marker type.

2. Marker Interfaces in the JDK

MarkerPackageMeaningChecked by
Serializablejava.ioObjects may be converted to a byte streamObjectOutputStream throws NotSerializableException otherwise
Cloneablejava.langObject.clone() may make a field-by-field copyObject.clone() throws CloneNotSupportedException otherwise
RandomAccessjava.utilThe list supports fast indexed accessAlgorithms such as Collections.binarySearch choose a strategy
Remotejava.rmiMethods may be invoked remotely via RMIThe RMI runtime
EventListenerjava.utilTags all event listener interfacesTools and frameworks
JdkMarkers.java
import java.io.*;
import java.util.*;

public class JdkMarkers {
    static class Plain { int x = 1; }
    static class Saveable implements Serializable { int x = 1; }

    static class Sheep implements Cloneable {
        String name = "Dolly";
        @Override public Sheep clone() throws CloneNotSupportedException { return (Sheep) super.clone(); }
    }
    static class Goat {
        String name = "Billy";
        public Goat copy() throws CloneNotSupportedException { return (Goat) super.clone(); }
    }

    static void serialize(Object o) {
        try (ObjectOutputStream out = new ObjectOutputStream(new ByteArrayOutputStream())) {
            out.writeObject(o);
            System.out.println("Serializable: " + o.getClass().getSimpleName() + " OK");
        } catch (IOException e) {
            System.out.println("Serializable: " + e);
        }
    }

    public static void main(String[] args) {
        serialize(new Saveable());
        serialize(new Plain());

        try { System.out.println("Cloneable: cloned " + new Sheep().clone().name); }
        catch (CloneNotSupportedException e) { System.out.println("Cloneable: " + e); }
        try { new Goat().copy(); }
        catch (CloneNotSupportedException e) { System.out.println("Cloneable: " + e); }

        // RandomAccess tells algorithms that get(i) is fast
        System.out.println("ArrayList is RandomAccess:  " + (new ArrayList<>() instanceof RandomAccess));
        System.out.println("LinkedList is RandomAccess: " + (new LinkedList<>() instanceof RandomAccess));
    }
}
Output
Serializable: Saveable OK
Serializable: java.io.NotSerializableException: JdkMarkers$Plain
Cloneable: cloned Dolly
Cloneable: java.lang.CloneNotSupportedException: JdkMarkers$Goat
ArrayList is RandomAccess:  true
LinkedList is RandomAccess: false

The classes are identical except for the marker, yet serialization and cloning succeed only for marked classes. See Serialization and Object Cloning.

3. Creating a Custom Marker Interface

You can define your own marker to tag classes that need special handling, such as events that must be audited:

CustomMarker.java
import java.util.List;

public class CustomMarker {
    // Custom marker interface: no methods, just a type tag
    interface Auditable { }

    record Payment(String id, double amount) implements Auditable { }
    record Login(String user) implements Auditable { }
    record PageView(String url) { }

    static void process(Object event) {
        if (event instanceof Auditable) {                 // check the marker
            System.out.println("AUDIT  -> " + event);
        }
        System.out.println("handled  " + event.getClass().getSimpleName());
    }

    // Unlike an annotation, the marker is a TYPE: the compiler can enforce it
    static void archive(Auditable record) {
        System.out.println("ARCHIVE -> " + record);
    }

    public static void main(String[] args) {
        List<Object> events = List.of(new Payment("P-1", 999.0), new PageView("/java"), new Login("asha"));
        events.forEach(CustomMarker::process);
        archive(new Login("ravi"));
        // archive(new PageView("/x"));   // compile error: PageView is not Auditable
    }
}
Output
AUDIT  -> Payment[id=P-1, amount=999.0]
handled  Payment
handled  PageView
AUDIT  -> Login[user=asha]
handled  Login
ARCHIVE -> Login[user=ravi]

The key strength of a marker interface is visible in archive(Auditable record): the compiler rejects unmarked objects, so mistakes are caught before the program runs.

4. Marker Interfaces vs Marker Annotations

Since Java 5, annotations without elements (marker annotations such as @Override, @FunctionalInterface and @Deprecated) can also tag code:

MarkerAnnotation.java
import java.lang.annotation.*;
import java.util.List;

public class MarkerAnnotation {
    // Annotation-based alternative: a marker annotation
    @Retention(RetentionPolicy.RUNTIME)
    @Target(ElementType.TYPE)
    @interface Cacheable { }

    @Cacheable static class ProductService { }
    static class OrderService { }

    public static void main(String[] args) {
        for (Class<?> c : List.of(ProductService.class, OrderService.class)) {
            boolean cache = c.isAnnotationPresent(Cacheable.class);
            System.out.println(c.getSimpleName() + " cacheable? " + cache);
        }
    }
}
Output
ProductService cacheable? true
OrderService cacheable? false
AspectMarker interfaceMarker annotation
Defines a typeYes: usable for parameters and variablesNo
Compile-time checkingYes (method can require the marker type)Only with annotation processors
Inherited by subclassesAlwaysOnly if @Inherited (and only for classes)
Can markClasses and interfaces onlyClasses, methods, fields, parameters, packages, modules…
Can carry dataNoYes, by adding elements later
Checked withinstanceofReflection: isAnnotationPresent

A useful rule (from Effective Java): use a marker interface when you want to define a type that methods can accept; use a marker annotation to mark program elements other than classes, or in frameworks that already rely heavily on annotations (such as Spring and JPA).

5. Criticism and Pitfalls

  • Inheritance cannot be undone: if a parent class is Serializable, every subclass is too, even if it should not be.
  • Cloneable is a poor design: it does not declare clone(), so the marker alone does not make an object cloneable through the interface. Copy constructors or factory methods are usually better.
  • Meaning is implicit: nothing in the interface explains what it promises; document it clearly.

6. Summary

  • A marker interface has no methods and tags a class with a property.
  • JDK markers include Serializable, Cloneable and RandomAccess.
  • Markers are checked with instanceof or enforced by parameter types at compile time.
  • Marker annotations are more flexible but do not define a type.

Related Tutorials