Pregunta

El tema dice la mayor parte: ¿cuál es la razón por la que los métodos estáticos no se pueden declarar en una interfaz?

public interface ITest {
    public static String test();
}

El código anterior me da el siguiente error (al menos en Eclipse):"Modificador ilegal para el método de interfaz ITest.test();sólo se permiten públicos y abstractos".

¿Fue útil?

Solución

Hay algunas cuestiones en juego aquí.La primera es la cuestión 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() {
    ...
  }
}

Lo primero es imposible por las razones que espo menciona:no sabes qué clase de implementación es la definición correcta.

Java podría permitir esto último;y de hecho, a partir de Java 8, ¡lo hace!

Otros consejos

La razón por la que no se puede tener un método estático en una interfaz radica en la forma en que Java resuelve las referencias estáticas.Java no se molestará en buscar una instancia de una clase cuando intente ejecutar un método estático.Esto se debe a que los métodos estáticos no dependen de la instancia y, por lo tanto, pueden ejecutarse directamente desde el archivo de clase.Dado que todos los métodos en una interfaz son abstractos, la VM tendría que buscar una implementación particular de la interfaz para encontrar el código detrás del método estático para poder ejecutarlo.Esto contradice cómo funciona la resolución de métodos estáticos e introduciría una inconsistencia en el lenguaje.

Responderé a tu pregunta con un ejemplo.Supongamos que tenemos una clase de Matemáticas con un método estático de adición.Llamarías a este método así:

Math.add(2, 3);

Si Math fuera una interfaz en lugar de una clase, no podría tener ninguna función definida.Como tal, decir algo como Math.add(2, 3) no tiene sentido.

La razón radica en el principio de diseño de que Java no permite la herencia múltiple.El problema de la herencia múltiple se puede ilustrar con el siguiente ejemplo:

public class A {
   public method x() {...}
}
public class B {
   public method x() {...}
}
public class C extends A, B { ... }

Ahora, ¿qué pasa si llamas a C.x()?¿Se ejecutará A.x() o B.x()?Todo lenguaje con herencia múltiple tiene que resolver este problema.

Las interfaces permiten en Java algún tipo de herencia múltiple restringida.Para evitar el problema anterior, no se les permite tener métodos.Si miramos el mismo problema con interfaces y métodos estáticos:

public interface A {
   public static method x() {...}
}
public interface B {
   public static method x() {...}
}
public class C implements A, B { ... }

El mismo problema aquí, ¿qué pasa si llamas a C.x()?

Los métodos estáticos no son métodos de instancia.No existe un contexto de instancia, por lo que implementarlo desde la interfaz tiene poco sentido.

Ahora Java8 nos permite definir incluso métodos estáticos en la interfaz.

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 because foo() is a Static Method of X and not Y
   }
}

Nota:Los métodos en la interfaz siguen siendo abstractos públicos de forma predeterminada si no usamos explícitamente las palabras clave default/static para convertirlos en métodos predeterminados y métodos estáticos respectivamente.

Hay una respuesta muy agradable y concisa a tu pregunta. aquí.(Me pareció una forma tan sencilla de explicarlo que quiero vincularlo desde aquí).

Parece que el método estático en la interfaz podría ser compatible con Java 8, Bueno, mi solución es simplemente definirlos en la clase interna.

interface Foo {
    // ...
    class fn {
        public static void func1(...) {
            // ...
        }
    }
}

La misma técnica también se puede utilizar en anotaciones:

public @interface Foo {
    String value();

    class fn {
        public static String getValue(Object obj) {
            Foo foo = obj.getClass().getAnnotation(Foo.class);
            return foo == null ? null : foo.value();
        }
    }
}

Siempre se debe acceder a la clase interna en forma de Interface.fn... en lugar de Class.fn..., Entonces, podrás deshacerte del problema ambiguo.

Se utiliza una interfaz para el polimorfismo, que se aplica a objetos, no a tipos.Por lo tanto (como ya se señaló) no tiene sentido tener un miembro de interfaz estático.

Java 8 había cambiado el mundo; puedes tener métodos estáticos en la interfaz, pero te obliga a proporcionar una implementación para eso.

public interface StaticMethodInterface {
public static int testStaticMethod() {
    return 0;
}

/**
 * Illegal combination of modifiers for the interface method
 * testStaticMethod; only one of abstract, default, or static permitted
 * 
 * @param i
 * @return
 */
// public static abstract int testStaticMethod(float i);

default int testNonStaticMethod() {
    return 1;
}

/**
 * Without implementation.
 * 
 * @param i
 * @return
 */
int testNonStaticMethod(float i);

}

Combinación ilegal de modificadores:estático y abstracto

Si un miembro de una clase se declara como estático, se puede utilizar con su nombre de clase que se limita a esa clase, sin crear un objeto.

Si un miembro de una clase se declara como abstracto, debe declarar la clase como abstracta y debe proporcionar la implementación del miembro abstracto en su clase heredada (Subclase).

Debe proporcionar una implementación al miembro abstracto de una clase en la subclase donde va a cambiar el comportamiento del método estático, también declarado como abstracto, que se limita a la clase base, lo cual no es correcto.

Dado que los métodos estáticos no se pueden heredar.Así que no sirve de nada colocarlo en la interfaz.La interfaz es básicamente un contrato que todos sus suscriptores deben seguir.Colocar un método estático en la interfaz obligará a los suscriptores a implementarlo.lo que ahora resulta contradictorio con el hecho de que los métodos estáticos no se pueden heredar.

Con Java 8, las interfaces ahora pueden tener métodos estáticos.

Por ejemplo, Comparator tiene un método estático naturalOrder().

También se ha relajado el requisito de que las interfaces no puedan tener implementaciones.Las interfaces ahora pueden declarar implementaciones de métodos "predeterminados", que son como implementaciones normales con una excepción:Si heredas tanto una implementación predeterminada de una interfaz como una implementación normal de una superclase, la implementación de la superclase siempre tendrá prioridad.

Quizás un ejemplo de código ayudaría. Voy a usar C#, pero deberías poder seguirlo.

Supongamos que tenemos una interfaz llamada IPayable

public interface IPayable
{
    public Pay(double amount);
}

Ahora tenemos dos clases concretas que implementan esta interfaz:

public class BusinessAccount : IPayable
{
    public void Pay(double amount)
    {
        //Logic
    }
}

public class CustomerAccount : IPayable
{
    public void Pay(double amount)
    {
        //Logic
    }
}

Ahora supongamos que tenemos una colección de varias cuentas, para ello usaremos una lista genérica del tipo IPayable

List<IPayable> accountsToPay = new List<IPayable>();
accountsToPay.add(new CustomerAccount());
accountsToPay.add(new BusinessAccount());

Ahora queremos pagar $50.00 a todas esas cuentas:

foreach (IPayable account in accountsToPay)
{
    account.Pay(50.00);
}

Ahora ves cómo las interfaces son increíblemente útiles.

Se utilizan únicamente en objetos instanciados.No en clases estáticas.

Si hubiera hecho que el pago fuera estático, al recorrer IPayable en AccountsToPay no habría forma de determinar si debería llamar al pago en BusinessAcount o CustomerAccount.

Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top