Маркерные интерфейсы в Java?

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

Но в последнее время я узнал, что это фактически не имеет ничего общего с компилятором или JVM. Например, в случае интерфейса Serializable метод writeObject(Object) of ObjectOutputStream выполняет нечто вроде instanceOf Serializable, чтобы определить, реализует ли класс Serializable и бросает NotSerializableException соответственно. Все обрабатывается в коде, и это похоже на шаблон дизайна, поэтому я думаю, что мы можем определить наши собственные интерфейсы маркеров.

Теперь мои сомнения:

  • Является ли определение интерфейса маркера, упомянутого выше в 1-й точке, неправильным? Как мы можем определить интерфейс Marker?

  • И вместо того, чтобы использовать оператор instanceOf, почему метод не может быть чем-то вроде writeObject(Serializable), так что есть проверка типа времени компиляции, а не время выполнения?

  • Как аннотации лучше, чем интерфейсы маркера?

Ответ 1

  • Не указано ли определение интерфейса маркера, упомянутого выше в 1-й точке?. Это правильно в тех частях, которые: (1) интерфейс маркера должен быть пустым и (2) реализация означает что подразумевает какое-то особое отношение к классу-исполнителю. Неправильная часть состоит в том, что это означает, что JVM или компилятор будет относиться к объектам этого класса по-разному: вы правильно заметили, что это код библиотеки классов Java, который рассматривает эти объекты как клонируемые, сериализуемые и т.д. Он имеет не имеет ничего общего с компилятором или JVM.
  • вместо использования оператора instanceOf, почему метод не может быть чем-то вроде writeObject(Serializable), поэтому существует проверка типа компиляции. Это позволяет избежать загрязнения вашего кода именем интерфейс маркера, когда требуется "plain Object". Например, если вы создаете класс, который должен быть сериализуемым, и у него есть члены-члены, вам придется либо делать кастинг, либо делать ваши объекты Serializable во время компиляции. Это неудобно, потому что интерфейс лишен какой-либо функциональности.
  • Как аннотации лучше, чем интерфейсы Marker?. Они позволяют достичь той же цели, что и метаданные о классе для своих потребителей, не создавая для этого отдельный тип. Аннотации также более мощные, позволяя программистам передавать более сложную информацию классам, которые "потребляют" его.

Ответ 2

Невозможно принудительно выполнить Serializable на writeObject, поскольку дети класса, не связанные с сериализацией, могут быть сериализуемыми, но экземпляры могут быть возвращены обратно в родительский класс. В результате ссылка на что-то несериализуемое (например, Object) не означает, что упомянутый экземпляр действительно не может быть сериализован. Например, в

   Object x = "abc";
   if (x instanceof Serializable) {
   }

родительский класс (Object) не сериализуем и будет инициализирован с помощью конструктора без параметров. Значение, на которое ссылаются x, String, является сериализуемым и выполняется условное утверждение.

Ответ 3

Маркерный интерфейс в Java - это интерфейс без полей или методов. Проще говоря, пустой интерфейс в Java называется интерфейсом маркера. Примерами интерфейсов маркеров являются интерфейсы Serializable, Cloneable и Remote. Они используются для указания некоторой информации для компиляторов или JVM. Поэтому, если JVM видит, что класс является Serializable, он может выполнить с ним некоторую специальную операцию. Аналогично, если JVM видит, что какой-то класс реализует Cloneable, он может выполнять некоторые операции для поддержки клонирования. То же самое верно для RMI и интерфейса Remote. Короче говоря, интерфейс маркера указывает сигнал или команду компилятору или JVM.

Вышесказанное началось как копия поста в блоге, но было слегка отредактировано для грамматики.

Ответ 4

Интерфейс маркера a/A, поскольку его название предполагает существование только для того, чтобы уведомить все, что знает об этом, что класс объявляет что-то. Все может быть классами JDK для интерфейса Serializable или любым классом, который вы пишете yoursel для пользовательского.

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

c/Существует небольшая разница между пустым интерфейсом и аннотацией, которая не использует значение или параметр. Но есть разница: аннотация может объявить список ключей/значений, которые будут доступны во время выполнения.

Ответ 5

  • Ему нечего делать (обязательно) с JVM и компиляторами, он имеет какое-то отношение к любому интересующему тестирование для данного интерфейса маркера.

  • Это дизайнерское решение, и оно сделано по уважительной причине. См. ответ от Audrius Meškauskas.

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

Ответ 6

а. Я всегда рассматривал их как шаблон дизайна и ничего JVM-Special. Я использовал этот шаблон в нескольких ситуациях.

с. Я верю, что использование аннотаций для обозначения чего-то является лучшим решением, а затем использованием интерфейсов маркеров. Просто потому, что интерфейсы в первую очередь направлены на определение общих интерфейсов типов/классов. Они являются частью класса-иерархии.

Аннотации направлены на предоставление метаинформации на код, и я считаю, что маркер является метаинформацией. Поэтому они предназначены именно для этого случая.

Ответ 7

Основной целью интерфейсов маркеров является создание специальных типов, в которых сами типы не имеют собственного поведения.

public interface MarkerEntity {

}

public boolean save(Object object) throws InvalidEntityFoundException {
   if(!(object instanceof MarkerEntity)) {
       throw new InvalidEntityFoundException("Invalid Entity Found, can't be  saved);
   } 
   return db.save(object);
}

Здесь метод сохранения гарантирует, что сохраняются только объекты классов, реализующие интерфейс MarkerEntity, для других типов InvalidEntityFoundException. Таким образом, интерфейс MarkerEntity-маркера определяет тип, который добавляет особое поведение к классам, реализующим его.

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

Но аннотации маркеров не могут полностью заменить интерфейсы маркеров, потому что; Маркерные интерфейсы используются для определения типа (как уже объяснялось выше), где в качестве аннотаций маркеров нет.

Источник для комментария интерфейса маркера

Ответ 8

Я бы сказал, в первую очередь, что Serializable и Cloneable являются плохими примерами интерфейсов маркеров. Конечно, это интерфейсы с методами, но они подразумевают методы, такие как writeObject(ObjectOutputStream). (Компилятор создаст для вас метод writeObject(ObjectOutputStream), если вы его не переопределите, и все объекты уже имеют clone(), но компилятор снова создаст для вас реальный clone() метод, но с оговорками. являются странными крайними случаями, которые действительно не являются хорошими примерами дизайна.)

Маркерные интерфейсы обычно используются для одной из двух целей:

1) Как ярлык, чтобы избежать чрезмерно длинного типа, что может произойти с большим количеством генериков. Например, скажем, у вас есть эта подпись метода:

public void doSomething(Foobar<String, Map<String, SomethingElse<Integer, Long>>>) { ... }

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

public interface Widget extends Foobar<String, Map<String, SomethingElse<Integer, Long>>> { }

Затем ваш метод выглядит следующим образом:

public void doSomething(Widget widget) { ... }

Он не только более ясен, но теперь вы можете использовать интерфейс Javadoc the Widget, а также легче искать все вхождения в вашем коде виджета.

2) Маркерные интерфейсы также могут использоваться как путь к отсутствию типов пересечений Java. С интерфейсом маркера вы можете потребовать, чтобы что-то было двух разных типов, например, в сигнатуре метода. Скажем, у вас есть виджет интерфейса в вашем приложении, как описано выше. Если у вас есть метод, для которого требуется виджет, который также позволяет вам перебирать его (он изобретает, но работает со мной здесь), единственным хорошим решением является создание интерфейса маркера, который расширяет оба интерфейса:

public interface IterableWidget extends Iterable<String>, Widget { }

И в вашем коде:

public void doSomething(IterableWidget widget) {
    for (String s : widget) { ... }
}

Ответ 9

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

Ответ 10

Я сделал простую демонстрацию, чтобы разрешить сомнения № 1 и 2:

У нас будет интерфейс Movable, который будет реализован классом MobilePhone.java, и еще один класс LandlinePhone.java, который НЕ реализует интерфейс Movable.

Наш маркер интерфейс:

package com;

public interface Movable {

}

LandLinePhone.java и MobilePhone.java

 package com;

 class LandLinePhone {
    // more code here
 }
 class MobilePhone implements Movable {
    // more code here
 }

Наш класс исключений: пакет com;

открытый класс NotMovableException расширяет Exception {

private static final long serialVersionUID = 1L;

@Override
public String getMessage() {
    return "this object is not movable";
}
// more code here
}

Наш тестовый класс: TestMArkerInterface.java

package com;

 public class TestMarkerInterface {

public static void main(String[] args) throws NotMovableException {
    MobilePhone mobilePhone = new MobilePhone();
    LandLinePhone landLinePhone = new LandLinePhone();

    TestMarkerInterface.goTravel(mobilePhone);
    TestMarkerInterface.goTravel(landLinePhone);

}

public static void goTravel(Object o) throws NotMovableException {
    if (!(o instanceof Movable)) {
        System.out.println("you cannot use :" + o.getClass().getName() + "   while travelling");
        throw new NotMovableException();
    }

    System.out.println("you can use :" + o.getClass().getName() + "   while travelling");
}}

Теперь, когда мы выполняем основной класс:

you can use :com.MobilePhone while travelling
you cannot use :com.LandLinePhone while travelling
Exception in thread "main" com.NotMovableException: this object is not movable
    at com.TestMarkerInterface.goTravel(TestMarkerInterface.java:22)
    at com.TestMarkerInterface.main(TestMarkerInterface.java:14)

Так какой класс реализует маркерный интерфейс, Movable пройдет тест, иначе появится сообщение об ошибке.

Это способ проверки оператора instanceOf для Serializable, Cloneable и т.д.