Interfaces de marcadores en Java

1. Introducción

En este tutorial rápido, aprenderemos acerca de las interfaces de marcadores en Java.

2. Interfaces de marcadores

Una interfaz de marcador es una interfaz que no tiene métodos ni constantes en su interior . Proporciona información de tipo en tiempo de ejecución sobre los objetos , por lo que el compilador y la JVM tienen información adicional sobre el objeto .

Una interfaz de marcador también se denomina interfaz de etiquetado.

Aunque las interfaces de marcador todavía están en uso, es muy probable que indiquen un olor a código y deben usarse con cuidado. La razón principal de esto es que difuminan las líneas sobre lo que representa una interfaz, ya que los marcadores no definen ningún comportamiento. El desarrollo más reciente favorece las anotaciones para resolver algunos de los mismos problemas.

3. Interfaces de marcador JDK

Java tiene muchas interfaces de marcador integradas, como Serializable , Cloneable y Remote.

Tomemos el ejemplo de la interfaz Cloneable . Si intentamos clonar un objeto que no implementa esta interfaz, la JVM arroja una CloneNotSupportedException . Por lo tanto, la interfaz de marcador clonable es un indicador de la JVM que podemos llamar al método Object.clone () .

De la misma manera, al llamar al método ObjectOutputStream.writeObject () , la JVM verifica si el objeto implementa la interfaz de marcador serializable . Cuando no es el caso, se lanza una NotSerializableException . Por lo tanto, el objeto no se serializa en el flujo de salida.

4. Interfaz de marcador personalizado

Creemos nuestra propia interfaz de marcador.

Por ejemplo, podríamos crear un marcador que indique si un objeto se puede eliminar de la base de datos:

public interface Deletable { }

Para eliminar una entidad de la base de datos, el objeto que representa a esta entidad debe implementar nuestra interfaz de marcador eliminable :

public class Entity implements Deletable { // implementation details }

Digamos que tenemos un objeto DAO con un método para eliminar entidades de la base de datos. Podemos escribir nuestro método delete () para que solo los objetos que implementen nuestra interfaz de marcador puedan ser eliminados:

public class ShapeDao { // other dao methods public boolean delete(Object object) { if (!(object instanceof Deletable)) { return false; } // delete implementation details return true; } }

Como podemos ver, le estamos dando una indicación a la JVM, sobre el comportamiento en tiempo de ejecución de nuestros objetos. Si el objeto implementa nuestra interfaz de marcador, se puede eliminar de la base de datos.

5. Interfaces de marcadores frente a anotaciones

Al introducir anotaciones, Java nos ha proporcionado una alternativa para lograr los mismos resultados que las interfaces de marcador. Además, al igual que las interfaces de marcadores, podemos aplicar anotaciones a cualquier clase y podemos utilizarlas como indicadores para realizar determinadas acciones.

Entonces, ¿cuál es la diferencia clave?

A diferencia de las anotaciones, las interfaces nos permiten aprovechar el polimorfismo . Como resultado, podemos agregar restricciones adicionales a la interfaz del marcador.

Por ejemplo, agreguemos una restricción de que solo un tipo de forma se puede eliminar de la base de datos:

public interface Shape { double getArea(); double getCircumference(); }

En este caso, nuestra interfaz de marcador, llamémosla DeletableShape, se verá así:

public interface DeletableShape extends Shape { }

Entonces nuestra clase implementará la interfaz de marcador:

public class Rectangle implements DeletableShape { // implementation details }

Por lo tanto, todas las implementaciones de DeletableShape también son implementaciones de Shape . Obviamente, no podemos hacer eso usando anotaciones .

Sin embargo, cada decisión de diseño tiene compensaciones y el polimorfismo se puede utilizar como un contraargumento contra las interfaces de los marcadores. En nuestro ejemplo, cada clase que extiende Rectangle implementará automáticamente DeletableShape.

6. Interfaces de marcador frente a interfaces típicas

En el ejemplo anterior, podríamos obtener los mismos resultados modificando el método delete () de nuestro DAO para probar si nuestro objeto es una forma o no , en lugar de probar si es un eliminable:

public class ShapeDao { // other dao methods public boolean delete(Object object) { if (!(object instanceof Shape)) { return false; } // delete implementation details return true; } }

Entonces, ¿por qué crear una interfaz de marcador cuando podemos lograr los mismos resultados utilizando una interfaz típica?

Imaginemos que, además del tipo Shape , también queremos eliminar el tipo Person de la base de datos. En este caso, existen dos opciones para lograrlo:

La primera opción es agregar una verificación adicional a nuestro método delete () anterior para verificar si el objeto a eliminar es una instancia de Person o no.

public boolean delete(Object object) { if (!(object instanceof Shape || object instanceof Person)) { return false; } // delete implementation details return true; }

Pero, ¿qué pasa si también tenemos más tipos que queremos eliminar de la base de datos? Obviamente, esta no será una buena opción porque tenemos que cambiar nuestro método para cada nuevo tipo .

La segunda opción es hacer que el tipo Person implemente la interfaz Shape , que actúa como una interfaz de marcador. Pero, ¿un objeto Person es realmente una forma ? La respuesta es claramente no, y eso hace que la segunda opción sea peor que la primera.

Por lo tanto, aunque podemos lograr los mismos resultados utilizando una interfaz típica como marcador, terminaremos con un diseño deficiente.

7. Conclusión

En este artículo, discutimos qué son las interfaces de marcador y cómo se pueden usar. Luego, vimos algunos ejemplos de Java integrados de este tipo de interfaces y cómo las usa el JDK.

A continuación, creamos nuestra propia interfaz de marcador y la comparamos con el uso de una anotación. Finalmente, terminamos viendo por qué es una buena práctica usar una interfaz de marcador en algunos escenarios en lugar de una interfaz tradicional.

Como siempre, el código se puede encontrar en GitHub.