Java 8 permite métodos de interfaz estática
Con Java 8, las interfaces pueden tener métodos estáticos. También pueden tener métodos de instancia concretos, pero no campos de instancia.
Realmente hay dos preguntas aquí:
- ¿Por qué, en los viejos tiempos, las interfaces no podían contener métodos estáticos?
- ¿Por qué no se pueden anular los métodos estáticos?
Métodos estáticos en interfaces.
No había una razón técnica sólida por la cual las interfaces no pudieran haber tenido métodos estáticos en versiones anteriores. Esto se resume muy bien en el póster de una pregunta duplicada. Los métodos de interfaz estática se consideraron inicialmente como un pequeño cambio de idioma, y luego hubo una propuesta oficial para agregarlos en Java 7, pero luego se descartó debido a complicaciones imprevistas.
Finalmente, Java 8 introdujo métodos de interfaz estática, así como métodos de instancia que se pueden anular con una implementación predeterminada. Sin embargo, todavía no pueden tener campos de instancia. Estas características son parte del soporte de expresiones lambda, y puede leer más sobre ellas en la Parte H de JSR 335.
Anulación de métodos estáticos
La respuesta a la segunda pregunta es un poco más complicada.
Los métodos estáticos se pueden resolver en tiempo de compilación. El despacho dinámico tiene sentido para los métodos de ejemplo, donde el compilador no puede determinar el tipo concreto del objeto y, por lo tanto, no puede resolver el método a invocar. Pero invocar un método estático requiere una clase, y dado que esa clase se conoce estáticamente , en tiempo de compilación, el despacho dinámico es innecesario.
Es necesario un poco de información sobre cómo funcionan los métodos de instancia para comprender lo que está sucediendo aquí. Estoy seguro de que la implementación real es bastante diferente, pero permítanme explicar mi noción de envío de métodos, que modela el comportamiento observado con precisión.
Imagine que cada clase tiene una tabla hash que asigna las firmas de métodos (nombre y tipos de parámetros) a un fragmento de código real para implementar el método. Cuando la máquina virtual intenta invocar un método en una instancia, consulta el objeto para su clase y busca la firma solicitada en la tabla de la clase. Si se encuentra un cuerpo de método, se invoca. De lo contrario, se obtiene la clase principal de la clase y la búsqueda se repite allí. Esto continúa hasta que se encuentra el método, o no hay más clases primarias, lo que resulta en a NoSuchMethodError
.
Si una superclase y una subclase tienen una entrada en sus tablas para la misma firma de método, la versión de la subclase se encuentra primero y la versión de la superclase nunca se usa; esto es una "anulación".
Ahora, supongamos que omitimos la instancia del objeto y simplemente comenzamos con una subclase. La resolución podría proceder como anteriormente, dándole una especie de método estático "reemplazable". Sin embargo, la resolución puede suceder en tiempo de compilación, ya que el compilador está comenzando desde una clase conocida, en lugar de esperar hasta el tiempo de ejecución para consultar un objeto de un tipo no especificado para su clase. No tiene sentido "anular" un método estático, ya que siempre se puede especificar la clase que contiene la versión deseada.
Constructor "interfaces"
Aquí hay un poco más de material para abordar la edición reciente de la pregunta.
Parece que desea exigir efectivamente un método similar al constructor para cada implementación de IXMLizable
. Olvídate de intentar hacer cumplir esto con una interfaz durante un minuto y finge que tienes algunas clases que cumplen con este requisito. Cómo lo usarías?
class Foo implements IXMLizable<Foo> {
public static Foo newInstanceFromXML(Element e) { ... }
}
Foo obj = Foo.newInstanceFromXML(e);
Como debe nombrar explícitamente el tipo concreto Foo
cuando "construye" el nuevo objeto, el compilador puede verificar que sí tiene el método de fábrica necesario. Y si no es así, ¿y qué? Si puedo implementar una IXMLizable
que carece del "constructor", y creo una instancia y la paso a su código, es una IXMLizable
con toda la interfaz necesaria.
La construcción es parte de la implementación, no la interfaz. Cualquier código que funcione correctamente con la interfaz no se preocupa por el constructor. Cualquier código que se preocupe por el constructor necesita conocer el tipo concreto de todos modos, y la interfaz puede ignorarse.