jdk - java interface default method modifier
¿Puede la interfaz de marcador como serializable contener métodos predeterminados? (4)
Creo que no puede, porque el principio de la interfaz de marcador es no tener ningún método, pero como los métodos predeterminados no son abstractos, no estoy seguro.
Una interfaz "Marker" es solo una interfaz regular en lo que respecta a Java. Por lo tanto, puede tener métodos predeterminados al igual que cualquier interfaz (Java-8).
Ahora, si esto viola el principio de una interfaz de Marker, tendría que decir que sí. Una interfaz Marker debe actuar como una especie de bandera, identificando solo que una clase cumple con algunos criterios externos. Ahora, puede ser una interfaz Marker y tiene métodos abstractos / predeterminados, pero ya no cumplirá con la definición.
De Effective Java (Segunda Edición) :
Una interfaz de marcador es una interfaz que no contiene declaraciones de métodos, sino que simplemente designa (o "marca") una clase que implementa la interfaz como si tuviera alguna propiedad.
Una interfaz de marcador es un patrón de diseño, por lo que podemos comenzar a responder a su pregunta observando exactamente cuál es la definición:
En versiones anteriores de Java, las interfaces de marcador eran la única forma de declarar metadatos sobre una clase. Por ejemplo, la interfaz de marcador serializable le permite al autor de una clase decir que su clase se comportará correctamente cuando se serialice y se deserialice.
El propósito de una interfaz de Marker, en el contexto de Java, era decir algo sobre esa clase. Curiosamente, lo marcó como algo. Un ejemplo de esto es la interfaz Serializable
, que no hizo nada más que marcar que una Class
podía ser serializada en una String
. La pregunta aquí es:
¿La definición incluye funcionalidad?
No, no creo que lo haga. La funcionalidad es más que solo metadatos sobre la clase; ayuda a definir la clase en sí. Toma ese paso de los metadatos a los datos . Entonces, en términos del patrón de diseño , una interfaz de marcador no puede definir o declarar la funcionalidad; simplemente puede hacer una declaración sobre la Class
implementadora.
Una interfaz de marcador puede tener métodos predeterminados, pero tenerlos no tiene sentido.
Una interfaz de marcador difiere de una interfaz convencional en la forma en que se utiliza. Una interfaz convencional define métodos, tanto abstractos como predeterminados. Por lo tanto, es sensato para un programa declarar variables con esa interfaz como su tipo, y llamar a métodos de ambos tipos a través de una referencia de ese tipo de interfaz.
Una interfaz de marcador, por constrast, no se usa para los métodos de llamada. Es una pieza de metainformación sobre un objeto declarado a través del sistema de tipo. Normalmente se usa llamando al código a través de una instanceof
expresión, u ocasionalmente Class.isAssignableFrom()
. No tiene sentido declarar una variable cuyo tipo sea una interfaz de marcador, ya que no hay nada que pueda hacer con dicha variable.
Los ejemplos de interfaces de marcador en el JDK son Cloneable
, RandomAccess
y Serializable
.
Ahora considere la adición de un método predeterminado a alguna interfaz de marcador:
interface Marker {
default void foo() { ... }
}
¿Qué podría hacer la implementación predeterminada de foo
?
Las implementaciones de los métodos predeterminados normalmente quieren operar en this
, y lo hacen llamando a otros métodos de instancia sobre this
. Podrían llamar a otros métodos predeterminados, pero tener un grupo de métodos predeterminados llamándose entre sí no es útil. Eventualmente, se debe realizar algún tipo de operación real sobre this
. Como los métodos de interfaz no tienen acceso al estado (campos), las operaciones reales deben ser realizadas por implementaciones de métodos abstractos que residan en una clase implementadora. Sin embargo, en una interfaz de marcador no existen tales métodos.
La implementación predeterminada de foo
podría llamar a un método estático en esta interfaz o en alguna otra clase. Esto es casi inútil, ya que tal método probablemente se expresaría mejor como un método estático en primer lugar. La implementación podría pasar this
a un método estático, pero ese método no podría hacer nada útil con dicha referencia, ¡ya que no tiene métodos! Bueno, podría tener métodos predeterminados, pero ahora vamos en círculos.
Para que un método predeterminado sea útil en una interfaz, esa interfaz también debe tener métodos abstractos. Pero si tiene métodos abstractos, ya no es una interfaz de marcador. Por lo tanto, no tiene sentido tener métodos predeterminados en una interfaz de marcador.
Aunque la respuesta de @Azar es correcta, no debemos olvidar que Java efectivo se escribió antes de que se introdujeran los métodos predeterminados.
¿Qué es una interfaz de marcador?
Hay dos formas de ver las interfaces de marcadores:
- Son interfaces que no declaran ningún método.
- Son interfaces que no fuerzan la implementación de ningún método.
La definición "oficial" es la primera pero hasta Java 7 esas dos declaraciones fueron equivalentes. Es un patrón recurrente en Java efectivo que una vez que publique una interfaz, no pueda agregar ningún método porque forzará la implementación de los nuevos métodos.
Sin embargo, este es exactamente el problema que los métodos predeterminados intentan abordar: permitir la evolución de las interfaces sin la necesidad de actualizar todas las clases que las implementan. También hace que las dos declaraciones anteriores signifiquen cosas ligeramente diferentes: un método predeterminado viola claramente el enunciado 1 y, por su diseño, no viola el enunciado 2.
¿Qué significa todo esto en la práctica?
Imagine que escribe un motor de serialización XML y crea una interfaz de marcador XmlSerializable
para ir con él:
public interface XmlSerializable {}
Hasta aquí todo bien. Pero más tarde te das cuenta de que en realidad tienes algunas clases que necesitan un tratamiento especial, necesitan proporcionar sus propios convertidores personalizados. Entonces, lo que puedes hacer es algo como esto:
public interface XmlSerializable {
public static final Map<Class,Class> CONVERTERS = ...
default Class customConverter() {
return CONVERTERS.get(this.getClass());
}
}
¿ XmlSerializable
que XmlSerializable
sea una interfaz de marcador? Puede decir que todavía es una interfaz de marcador ya que realmente no agrega un comportamiento adicional directamente a su interfaz, solo metadatos adicionales que influyen en el comportamiento del motor de serializador. Por otro lado, esta solución permite implementar clases para anular customConverter()
, que es ligeramente dudoso, una interfaz de marcador no debería permitir eso. (Por otra parte, ¿ Serializable
y Cloneable
depende mejor de los métodos "mágicos" en la clase de implementación? No lo creo).
Podría decirse que el ejemplo anterior no es una muy buena manera de resolver este tipo de problema, probablemente sería mucho mejor usar anotaciones. Pero eso también es cierto para la mayoría de las interfaces de marcador "verdaderas".
tl; dr
Podemos concluir que una interfaz con solo métodos predeterminados es más o menos equivalente a una interfaz vacía. Si desea hacer una distinción teórica y no llamarla interfaz de marcador, eso está bien. Pero hay poca diferencia práctica, y dados los problemas inherentes a las interfaces de marcadores en general, probablemente deberíamos evitarlos de todos modos.