Interfaces de marcador en Java?

134

Me enseñaron que la interfaz Marker en Java es una interfaz vacía y se utiliza para indicar al compilador o JVM que los objetos de la clase que implementan esta interfaz deben tratarse de una manera especial, como la serialización, la clonación, etc.

Pero últimamente he aprendido que en realidad no tiene nada que ver con el compilador o la JVM. Por ejemplo, en el caso de Serializablela interfaz del método writeObject(Object)de ObjectOutputStreamhace algo como instanceOf Serializablepara detectar si las implementa la clase Serializabley lanza NotSerializableExceptionen consecuencia. Todo se maneja en el código y esto parece ser un patrón de diseño, por lo que creo que podemos definir nuestras propias interfaces de marcador.

Ahora mis dudas:

  1. ¿Es incorrecta la definición de una interfaz de marcador mencionada anteriormente en el primer punto? ¿Cómo podemos definir una interfaz de marcador entonces?

  2. Y en lugar de usar el instanceOfoperador, ¿por qué el método no puede ser algo writeObject(Serializable)así como para que haya una verificación de tipo en tiempo de compilación en lugar de tiempo de ejecución?

  3. ¿Cómo son las anotaciones mejores que las interfaces de marcador?

Xavier DSouza
fuente
8
Serializablecomo una anotación no tiene sentido y @NonNullcomo una interfaz no tiene sentido. Yo diría: las anotaciones son marcadores + metadatos. Por cierto: Forefunner of Annotations fue XDoclet, nacido en Javadoc, asesinado por Annotations.
Grim

Respuestas:

117
  1. ¿Es incorrecta la definición de una interfaz de marcador mencionada anteriormente en el primer punto? - Es correcto en las partes que (1) una interfaz de marcador debe estar vacía, y (2) implementarla implica un tratamiento especial de la clase implementadora. La parte incorrecta es que implica que JVM o el compilador tratarían los objetos de esa clase de manera diferente: está en lo correcto al observar que es el código de la biblioteca de clases Java que trata estos objetos como clonables, serializables, etc. nada que ver con el compilador o la JVM.
  2. en lugar de usar el operador instanceOf, ¿por qué el método no puede ser algo writeObject(Serializable)así como para que haya una verificación de tipo en tiempo de compilación ? Esto le permite evitar contaminar su código con el nombre de la interfaz del marcador cuando Objectse necesita un "plano ". Por ejemplo, si crea una clase que necesita ser serializable y tiene miembros de objeto, se vería obligado a realizar la conversión o hacer sus objetos Serializableen tiempo de compilación. Esto es inconveniente, porque la interfaz no tiene ninguna funcionalidad.
  3. ¿Cómo las anotaciones son mejores que las interfaces de marcador? - Le permiten lograr el mismo propósito de transmitir metadatos sobre la clase a sus consumidores sin crear un tipo separado para ella. Las anotaciones también son más poderosas, permitiendo que los programadores pasen información más sofisticada a las clases que la "consumen".
dasblinkenlight
fuente
14
La forma en que siempre lo entendí es que las anotaciones son una especie de 'Marker Interfaces 2.0': existe serializable desde Java 1.1, las anotaciones se agregaron en 1.5
blagae
y debido a que writeObjectes privado, eso significa que, por ejemplo, una clase no tiene que llamar a la implementación de la superclase
Ratchet Freak
15
Una cosa a tener en cuenta es que se agregaron anotaciones al lenguaje algunos años después de que se diseñó originalmente la biblioteca estándar. Si las anotaciones hubieran estado en el idioma desde el principio, es dudoso que Serializable hubiera sido una interfaz, probablemente habría sido una anotación.
Theodore Norvell
Bueno, en el caso de Cloneableque no esté claro si se trata de una biblioteca o un tratamiento JVM de la instancia que está alterada.
Holger
"[...] sin crear un tipo separado para ello". - Yo diría que esto es precisamente lo que los separa: Una interfaz de marcador hace introducir un tipo, mientras que las anotaciones (tipo de) does't.
aioobe
22

No es posible hacer cumplir Serializableel writeObjectporque los niños de la clase serializable no pueden ser serializable, pero sus casos puede ser posterior upcasted a la clase padre. Como resultado, mantener una referencia a algo no serializable (como Object) no significa que la instancia referida no se pueda serializar realmente. Por ejemplo en

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

la clase padre ( Object) no es serializable y se inicializaría utilizando su constructor sin parámetros. El valor al que hace referencia x, Stringes serializable y la sentencia condicional correría.

Audrius Meskauskas
fuente
6

Una interfaz de marcador en Java es una interfaz sin campos ni métodos. En pocas palabras, una interfaz vacía en Java se llama interfaz de marcador. Ejemplos de interfaces de marcadores son los Serializable, Cloneabley Remoteinterfaces. Estos se utilizan para indicar cierta información a los compiladores o JVM. Entonces, si la JVM ve que una clase es Serializable, puede hacer una operación especial en ella. Del mismo modo, si la JVM ve que se está implementando alguna clase Cloneable, puede realizar algunas operaciones para admitir la clonación. Lo mismo es cierto para RMI y la Remoteinterfaz. En resumen, una interfaz de marcador indica una señal o un comando al compilador o JVM.

Lo anterior comenzó como una copia de una publicación de blog, pero ha sido ligeramente editado para gramática.

vidya kn
fuente
66
Está bien copiar, pero también mencione la fuente: javarevisited.blogspot.com/2012/01/… . También será bueno si no copia y pega los errores de ortografía también. :)
Saurabh Patil
6

He hecho una demostración simple para resolver la duda no 1 y 2:

Tendremos una interfaz móvil que implementará MobilePhone.javaClass y una clase más LandlinePhone.javaque NO implementará la interfaz móvil

Nuestra interfaz de marcador:

package com;

public interface Movable {

}

LandLinePhone.java y MobilePhone.java

 package com;

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

Nuestra clase de excepción personalizada: paquete com;

public class NotMovableException extends Exception {

private static final long serialVersionUID = 1L;

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

Nuestra clase de Prueba: 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");
}}

Ahora cuando ejecutamos la clase principal:

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)

Entonces, cualquier clase implementa una interfaz de marcador Movable pasará la prueba; de lo contrario, se mostrará un mensaje de error.

Esta es la forma en instanceOfque se realiza la verificación del operador para Serializable , Cloneable , etc.

Shashank Bodkhe
fuente
5

La interfaz de marcador a / A, como su nombre indica, existe solo para notificar a cualquier cosa que sepa sobre ella que una clase declara algo. Cualquier cosa puede ser las clases JDK para la Serializableinterfaz, o cualquier clase que escriba usted mismo para una personalizada.

b / Si es una interfaz de marcador, no debe implicar la existencia de ningún método; sería mejor incluir el método implícito en la interfaz. Pero puede decidir diseñarlo como desee si sabe por qué lo necesita

c / Hay poca diferencia entre una interfaz vacía y una anotación que no utiliza ningún valor o parámetro. Pero la diferencia está ahí: una anotación puede declarar una lista de claves / valores que serán accesibles en tiempo de ejecución.

Serge Ballesta
fuente
3

a. Siempre los he visto como un patrón de diseño y nada JVM-Special. He usado ese patrón en varias situaciones.

C. Creo que usar Anotaciones para marcar algo es una mejor solución que usar interfaces de marcador. Simplemente porque las interfaces están en primer lugar destinadas a definir interfaces comunes de tipos / clases. Son parte de la jerarquía de clases.

Las anotaciones están destinadas a proporcionar metainformaciones al código, y creo que ese marcador son metainformaciones. Entonces son exactamente para ese caso de uso.

treeno
fuente
3
  1. No tiene nada que ver (necesariamente) con la JVM y los compiladores, tiene algo que ver con cualquier código que esté interesado y esté probando una interfaz de marcador determinada.

  2. Es una decisión de diseño y se hace por una buena razón. Ver la respuesta de Audrius Meškauskas.

  3. Con respecto a este tema en particular, no creo que sea una cuestión de ser mejor o peor. La interfaz del marcador está haciendo lo que se supone que debe hacer bien.

peter.petrov
fuente
¿Puedes agregar un enlace a "la respuesta de Audrius Meškauskas"? No veo nada en esta página con ese nombre.
Sarah Messer
2

El objetivo principal de las interfaces de marcadores es crear tipos especiales donde los tipos en sí mismos no tienen un comportamiento propio.

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);
}

Aquí, el método save asegura que solo se guarden los objetos de las clases que implementan la interfaz MarkerEntity, para otros tipos se lanza InvalidEntityFoundException. Entonces, la interfaz de marcador MarkerEntity está definiendo un tipo que agrega un comportamiento especial a las clases que lo implementan.

Aunque las anotaciones también se pueden usar ahora para marcar clases para algunos tratamientos especiales, las anotaciones de marcador son un reemplazo para el patrón de nomenclatura, no para las interfaces de marcador.

Pero las anotaciones de marcador no pueden reemplazar completamente las interfaces de marcador porque; Las interfaces de marcador se utilizan para definir el tipo (como ya se explicó anteriormente) donde las anotaciones de marcador no.

Fuente para el comentario de la interfaz del marcador

infoj
fuente
1
+1 por mostrar que en realidad se usa como un simple gancho de "seguridad" que un programador tiene que decir explícitamente que se puede guardar como una base de datos.
Ludvig W
1

En primer lugar, diría que Serializable y Cloneable son malos ejemplos de interfaces de marcadores. Claro, son interfaces con métodos, pero implican métodos, como writeObject(ObjectOutputStream). (El compilador creará un writeObject(ObjectOutputStream)método para usted si no lo anula, y todos los objetos ya lo tienen clone(), pero el compilador volverá a crear un clone()método real para usted, pero con advertencias. Ambos son casos extraños que realmente no son buenos ejemplos de diseño).

Las interfaces de marcador se usan generalmente para uno de dos propósitos:

1) Como atajo para evitar un tipo excesivamente largo, lo que puede suceder con muchos genéricos. Por ejemplo, supongamos que tiene la firma de este método:

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

Eso es desordenado y molesto de escribir, y lo más importante, difícil de entender. Considere esto en su lugar:

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

Entonces su método se ve así:

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

No solo es más claro, sino que ahora puede Javadoc la interfaz Widget, y también es más fácil buscar todas las ocurrencias en su código de Widget.

2) Las interfaces de marcador también se pueden utilizar para evitar la falta de tipos de intersección de Java. Con una interfaz de marcador, puede requerir que algo sea de dos tipos diferentes, como en la firma de un método. Digamos que tiene algún widget de interfaz en su aplicación, como lo describimos anteriormente. Si tiene un método que requiere un Widget que también le permite iterar sobre él (está ideado, pero trabaje conmigo aquí), su única buena solución es crear una interfaz de marcador que extienda ambas interfaces:

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

Y en tu código:

public void doSomething(IterableWidget widget) {
    for (String s : widget) { ... }
}
MikeyB
fuente
1
El compilador no crea ningún método si su clase implementa Serializableo Cloneable. Puede verificarlo inspeccionando sus archivos de clase. Además, la creación de "interfaces de acceso directo" no es de lo que se trata la interfaz de marcador. Y es una práctica de codificación realmente mala, ya que requeriría crear clases de implementación adicionales para cumplir con las interfaces adicionales. Por cierto, Java tiene tipos de intersección desde hace una década. Aprenda sobre genéricos ...
Holger
Para clonar, tienes razón. Cambia lo que hace Object.clone (). Todavía es horrible Ver enlace Josh Bloch Las interfaces de acceso directo no son necesariamente un buen patrón de diseño, ya que restringe arbitrariamente lo que puede enviar a un método. Al mismo tiempo, creo que escribir un código más claro, aunque un poco más restrictivo, suele ser una compensación razonable. En cuanto a los tipos de intersección, eso solo se aplica a los genéricos, y no son denotables, y por lo tanto inútiles en muchas ocasiones. Intente tener una variable de instancia que sea serializable y, bueno, cualquier otra cosa.
MikeyB
0

Si una interfaz no contiene ningún método y, al implementar esa interfaz, nuestro objeto tendrá alguna habilidad, este tipo de interfaces se denominan interfaces marcadoras.

Yogesh Kumar
fuente