java - method - ¿Por qué se heredan los métodos estáticos de clase pero no los métodos estáticos de interfaz?
java interface multiple inheritance (3)
Antes de Java 8 , no se podían definir métodos static
en una interface
. Esto se discute mucho en esta pregunta . Voy a referirme a esta answer (por el usuario @ JamesA.Rosen) sobre por qué los diseñadores de Java probablemente no querían métodos static
en una interface
inicialmente:
Hay algunos problemas en juego aquí. El primero es el problema de declarar un método estático sin definirlo. Esta es la diferencia entre
public interface Foo {
public static int bar();
}
y
public interface Foo {
public static int bar() {
...
}
}
Java tampoco permite, pero podría permitir el segundo. El primero es imposible por las razones que menciona Espo: no se sabe qué clase de implementación es la correcta.
Java podría permitir lo último, siempre que trate las interfaces como objetos de primera clase. Los módulos de Ruby, que son aproximadamente equivalentes a las interfaces de Java, permiten exactamente eso:
module Foo
def self.bar
...
end
end
Sin embargo, desde el lanzamiento de Java 8 , puede agregar métodos default
y static
dentro de una interface
.
Voy a citar mucho esta source aquí. Este es el problema inicial:
La función de lenguaje de interfaz de Java le permite declarar interfaces con métodos abstractos y proporcionar implementaciones de esos métodos en las clases que implementan las interfaces. Se le exige que implemente cada método, lo cual es engorroso cuando hay muchos métodos para implementar. Además, después de publicar la interfaz, no puede agregar nuevos métodos abstractos sin romper la compatibilidad de origen y binario.
Esta fue la solución que Java 8 proporcionó por default
:
Java 8 soluciona estos problemas desarrollando la interfaz para admitir métodos estáticos y predeterminados. Un método predeterminado es un método de instancia definido en una interfaz cuyo encabezado de método comienza con la palabra clave predeterminada; también proporciona un cuerpo de código. Cada clase que implementa la interfaz hereda los métodos predeterminados de la interfaz y puede anularlos
Y para static
:
Un método estático es un método que está asociado con la clase en la que está definido, en lugar de con cualquier objeto creado a partir de esa clase. Cada instancia de la clase comparte los métodos estáticos de la clase. Java 8 también permite que los métodos estáticos se definan en interfaces donde pueden ayudar a los métodos predeterminados.
Cuando implementa una interfaz que contiene un método estático, el método estático sigue siendo parte de la interfaz y no forma parte de la clase de implementación. Por esta razón, no puede prefijar el método con el nombre de la clase. En su lugar, debe prefijar el método con el nombre de la interfaz
Ejemplo:
interface X
{
static void foo()
{
System.out.println("foo");
}
}
class Y implements X
{
}
public class Z
{
public static void main(String[] args)
{
X.foo();
// Y.foo(); // won''t compile
}
}
La expresión
Y.foo()
no se compilará porquefoo()
es un miembro estático de la interfazX
y no un miembro estático de la claseY
Entiendo que en Java los métodos estáticos se heredan al igual que los métodos de instancia, con la diferencia de que cuando se vuelven a declarar, las implementaciones principales se ocultan en lugar de anularse. Bien, esto tiene sentido. Sin embargo, el tutorial de Java señala que
Los métodos estáticos en las interfaces nunca se heredan.
¿Por qué? ¿Cuál es la diferencia entre los métodos estáticos regulares y de interfaz?
Permítanme aclarar a qué me refiero cuando digo que los métodos estáticos se pueden heredar:
class Animal {
public static void identify() {
System.out.println("This is an animal");
}
}
class Cat extends Animal {}
public static void main(String[] args) {
Animal.identify();
Cat.identify(); // This compiles, even though it is not redefined in Cat.
}
Sin embargo,
interface Animal {
public static void identify() {
System.out.println("This is an animal");
}
}
class Cat implements Animal {}
public static void main(String[] args) {
Animal.identify();
Cat.identify(); // This does not compile, because interface static methods do not inherit. (Why?)
}
Aquí está mi suposición.
Como Cat
solo puede extender una clase si Cat
extiende Animal
entonces Cat.identify
tiene solo un significado. Cat
puede implementar múltiples interfaces, cada una de las cuales puede tener una implementación estática. Por lo tanto, el compilador no sabría cuál elegir?
Sin embargo, como lo señaló el autor,
Java ya tiene este problema, con métodos predeterminados. Si dos interfaces declaran default void identify (), ¿cuál se usa? Es un error de compilación, y debe implementar un método de anulación (que podría ser Animal.super.identify ()). Entonces, Java ya resuelve este problema para los métodos predeterminados, ¿por qué no para los métodos estáticos?
Si tuviera que adivinar de nuevo, diría que, por default
la implementación forma parte de la vtable de Cat
. Con static
no puede ser. La función principal debe unirse a algo. En tiempo de compilación Cat.identify
podría ser reemplazado por Animal.identify
por el compilador pero el código no coincidiría con la realidad si Cat
se recompilara pero no la clase que contiene main.
Los métodos estáticos en las interfaces podrían crear un diamante de la muerte si fueran heredados. Por lo tanto, llamar a un método estático desde la interfaz adecuada es lo suficientemente bueno en comparación con el riesgo de llamarlo desde una clase concreta que puede implementar múltiples interfaces que contienen métodos estáticos del mismo nombre.
¿Por qué los métodos estáticos son diferentes?
Los métodos estáticos son solo funciones no relacionadas con los objetos. En lugar de colocarlos en clases abstractas de utilidad (como llamar a Collections.sort ()) movemos esas funciones (métodos estáticos) a sus interfaces apropiadas. Podrían estar vinculados a los objetos heredados como lo hacen los métodos predeterminados, pero ese no es su trabajo. Los métodos estáticos proporcionan una funcionalidad que no está relacionada con las instancias de la clase.
Ejemplo:
interface Floatable {
default void float() {
// implementation
}
static boolean checkIfItCanFloat(Object fl) {
// some physics here
}
}
class Duck implements Floatable { }
Entonces, el punto es que un pato puede flotar, pero la función que verifica si un objeto realmente flota no es algo que un pato pueda hacer. Es una función irrelevante que podríamos pasar a nuestra interfaz Floatable en lugar de tenerlo dentro de alguna clase de utilidad.