Интерфейс маркера против пустого абстрактного класса

Я сталкиваюсь с трудностями при выборе между интерфейсом маркера или пустым абстрактным классом.

У меня два класса BrokerResponse а также Notification, которые не имеют структурного сходства. Единственное, что их связывает, - это необходимость подписки.

void register(Receivable receivable, BrokerObserver observer)

Я почему-то не люблю использовать Marker Interfaceпотому что это нарушает основное определение Interface, С другой стороны, используя abstract super класс делает меня таким неудобным, потому что оба класса не имеют никаких отношений друг с другом.

Каков в целом предпочтительный подход в этом сценарии и почему?

Редактировать 1

Я забыл упомянуть, что BrokerResponse сам абстрактный класс, который имеет несколько подклассов для определения соответствующего типа.

3 ответа

Решение

Абстрактный класс против интерфейса маркера:

В интерфейсе маркера нет ничего плохого, и есть несколько вариантов его использования. Выбирая между этими двумя, интерфейс маркера обладает большей гибкостью.

Если вы хотите определить тип, используйте интерфейс.

Цель абстрактного класса - предоставить соответствующий суперкласс, от которого другие классы могут наследовать и, таким образом, иметь общий дизайн - ваши классы не имеют общего дизайна и нечего делить. Более того, вы будете придерживаться их обоих в некотором ограниченном дизайне и будете не такими гибкими, если в будущем вам понадобится добавить к ним совершенно других родителей.

Список вариантов использования для абстрактного класса:

  • Поделитесь кодом среди нескольких тесно связанных классов.

  • Классы, которые расширяют ваш абстрактный класс, имеют много общих методов или полей или требуют модификаторов доступа, отличных от public (например, protected и private).

  • Объявите нестатические или не финальные поля, что позволяет вам определять методы, которые могут получать доступ и изменять состояние объекта, к которому они принадлежат.

Варианты использования для интерфейса:

  • Несвязанные классы будут реализовывать ваш интерфейс.

  • Укажите поведение определенного типа данных без учета того, кто реализует его поведение.

  • Преимущество множественного наследования.

Все перечисленные аргументы для использования интерфейса. поскольку BrokerResponse сам по себе абстрактный и имеет свою собственную иерархию, что делает тот факт, что у этих классов нет ничего общего более сильным.

В качестве альтернативы вы можете использовать аннотацию маркера. Я хотел бы придерживаться одного из этих двух подходов вместо абстрактного класса.

Интерфейс маркера против аннотации маркера:

Согласно " Эффективной Яве " Джошуа Блоха:

Интерфейсы маркеров имеют два преимущества перед аннотациями маркеров. Прежде всего, маркерные интерфейсы определяют тип, который реализуется экземплярами помеченного класса; маркерных аннотаций нет. Существование этого типа позволяет вам отлавливать ошибки во время компиляции, которые вы не могли отловить до времени выполнения, если использовали аннотацию маркера. Другое преимущество интерфейсов маркеров над аннотациями маркеров заключается в том, что они могут быть более точными.

Когда следует использовать аннотацию маркера?

Вы должны использовать аннотацию, если маркер применяется к любому элементу программы, кроме класса или интерфейса, поскольку для реализации или расширения интерфейса могут быть созданы только классы и интерфейсы.

Когда вы должны использовать интерфейс маркера?

Задайте себе вопрос, могу ли я написать один или несколько методов, которые принимают только объекты, имеющие эту маркировку? Если это так, вы должны использовать интерфейс маркера, а не аннотацию. Это позволит вам использовать интерфейс в качестве типа параметра для рассматриваемых методов, что даст реальную выгоду от проверки типов во время компиляции.

Резюме:

Если вы хотите определить тип, который не имеет никаких новых методов, связанных с ним, лучше всего воспользоваться интерфейсом маркера.

Если вы хотите пометить элементы программы, отличные от классов и интерфейсов, чтобы обеспечить возможность добавления дополнительной информации к маркеру в будущем или разместить маркер в каркасе, который уже интенсивно использует типы аннотаций, тогда аннотацию маркера это правильный выбор.

Использование пустого абстрактного класса не имеет никакого смысла в этом случае, так как в множественном наследовании нет Java, Заставление вашего класса реализовать некоторый интерфейс маркера не изменит вашу иерархию классов, он просто помечает ваш класс некоторыми дополнительными метаданными.

Представьте себе случай, когда ваш класс уже помечен как Subscribable также должно быть, например, Writable, Если вы используете пустой абстрактный класс, вам нужно будет перестроить всю иерархию. С интерфейсом маркера это так же просто, как добавить Writable к списку реализаций.

Как насчет их аннотирования? Вы получили ответ, что использование интерфейса пометок - это то, что вам нужно, если вам придется выбирать, но использование аннотации в зависимости от того, что вам может понадобиться, будет намного чище.

Тот факт, что вы говорите, что нужно сделать их как-то "одинаковыми", говорит о instanceof позвони и сделай что-нибудь на этом основании То же самое может быть достигнуто с помощью isAnnotationPresent или т.п.

Но если вы добавляете интерфейс маркера, как насчет того, чтобы сделать его не интерфейсом маркера - только в том случае, если у вас есть конечное число классов, с которыми нужно тестировать? Нечто подобное MyInterface {boolean isSubscribable();}

Другие вопросы по тегам