Зачем нужны маркерные интерфейсы в Java?
Что проверяют
Знаете ли вы стандартные маркеры JDK и что они реально делают, а главное, можете ли сравнить их с аннотациями. Следом почти всегда звучит: «Почему бы не заменить Serializable аннотацией?» и «Что не так с Cloneable?».
Как отвечать
Маркерный интерфейс не объявляет методов, он только добавляет классу тип. Код, которому важен этот признак, проверяет его через instanceof или принимает параметр этого типа. Примеры из JDK:
Serializable:ObjectOutputStreamсериализует объект, только если класс его реализует, иначе бросаетNotSerializableException;Cloneable: без негоObject.clone()бросаетCloneNotSupportedException. При этом самclone()в интерфейсе не объявлен, а вObjectонprotected. Это известная ошибка проектирования, и Джошуа Блох в Effective Java советует конструктор копирования или фабрику;RandomAccess: говорит, что у списка быстрый доступ по индексу.Collections.binarySearchи другие алгоритмы по нему решают, идти по индексу или итератором;java.util.EventListenerиjava.rmi.Remote: тоже маркеры, помечают роль класса.
Главное сравнение с аннотациями. Интерфейс создаёт тип, поэтому ошибку ловит компилятор: метод void save(Serializable s) не примет чужой объект. Аннотация типа не создаёт и проверяется рефлексией или процессором аннотаций, зато гибче: вешается на методы и поля, несёт параметры, по умолчанию не наследуется. Правило Блоха: если признак нужен как тип, берите интерфейс, для методов, полей и фреймворков на аннотациях берите аннотацию. Поэтому Spring, JPA и JUnit построены на аннотациях.
Где спотыкаются
Говорят, что проверка маркера через instanceof медленная. Проверка типа после JIT стоит копейки, это не аргумент против маркеров.
Думают, что Cloneable добавляет классу метод clone(). Его нужно переопределить самому и сделать public, иначе снаружи он недоступен.
Забывают, что маркер наследуется: подкласс сериализуемого класса тоже сериализуем, и отказаться от этого можно только обходным путём, бросив NotSerializableException из writeObject.