¿Por qué no puedo definir un método estático en una interfaz Java?

499

EDITAR: a partir de Java 8, los métodos estáticos ahora están permitidos en las interfaces.

Aquí está el ejemplo:

public interface IXMLizable<T>
{
  static T newInstanceFromXML(Element e);
  Element toXMLElement();
}

Por supuesto que esto no funcionará. ¿Pero por qué no?

Uno de los posibles problemas sería qué sucede cuando llama:

IXMLizable.newInstanceFromXML(e);

En este caso, creo que debería llamar a un método vacío (es decir, {}). Todas las subclases se verían obligadas a implementar el método estático, por lo que estarían bien al llamar al método estático. Entonces, ¿por qué no es esto posible?

EDITAR: Supongo que estoy buscando una respuesta que sea más profunda que "porque así es Java".

¿Existe una razón tecnológica particular por la cual los métodos estáticos no se pueden sobrescribir? Es decir, ¿por qué los diseñadores de Java decidieron hacer que los métodos de instancia sean reemplazables pero no métodos estáticos?

EDITAR: El problema con mi diseño es que estoy tratando de usar interfaces para aplicar una convención de codificación.

Es decir, el objetivo de la interfaz es doble:

  1. Quiero que la interfaz IXMLizable me permita convertir clases que lo implementen en elementos XML (usando polimorfismo, funciona bien).

  2. Si alguien quiere crear una nueva instancia de una clase que implemente la interfaz IXMLizable, siempre sabrá que habrá un nuevo constructor estáticoInstanceFromXML (Elemento e).

¿Hay alguna otra manera de garantizar esto, aparte de simplemente hacer un comentario en la interfaz?

cdmckay
fuente
44
No necesita saturar las definiciones de métodos (y campos) con public en interfaces, por cierto.
Tom Hawtin - tackline
Hmm, parece ser un duplicado de stackoverflow.com/questions/21817/… . No había visto eso antes.
Michael Myers
1
¿Podría proporcionar algún código sobre cómo le gustaría utilizar los métodos de interfaz estática?
Pavel Feldman
43
Esto será posible en Java 8: docs.oracle.com/javase/tutorial/java/IandI/…
dakshang
1
@dakshang Sí, pero no hace lo que quiere el OP.
user253751

Respuestas:

518

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í:

  1. ¿Por qué, en los viejos tiempos, las interfaces no podían contener métodos estáticos?
  2. ¿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 Foocuando "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 IXMLizableque carece del "constructor", y creo una instancia y la paso a su código, es una IXMLizablecon 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.

erickson
fuente
Buena llamada en Project Coin. mail.openjdk.java.net/pipermail/coin-dev/2009-March/000117.html
Michael Myers
12
¿Podría la razón para el n. ° 1 ser herencia múltiple? Dado que podemos heredar de múltiples interfaces, si dos interfaces contienen la misma firma de método estático y luego una clase los implementó a ambos y llamó a ese método, entonces las cosas podrían complicarse de una manera que los creadores del lenguaje Java querían evitar al no permitir la herencia de múltiples clases en El primer lugar. Obviamente, podría hacerse el mismo argumento para la interfaz que no permite ninguna definición de método en absoluto.
shrini1000
1
@ shrini1000: no, los métodos estáticos se resuelven en tiempo de compilación. La ambigüedad se puede manejar de la misma manera que se maneja con constantes: con un error del compilador. Sin embargo, la propuesta del Proyecto Moneda fue rechazada, citando algunas dificultades imprevistas. No estoy seguro de lo que eran, pero no creo que fuera así.
erickson
1
@ tgm1024 Sí, la sección "Interfaces del constructor" explica por qué no tiene sentido intentar invocar el comportamiento polimórfico a través de un tipo conocido en tiempo de compilación. ¿Cómo invocarías RESET()en una clase determinada? Tu escribirias SomeClass.RESET(). Por lo tanto, no necesita una interfaz para describir esa API; Es estático. Las interfaces se usan cuando no se conoce el tipo concreto en tiempo de compilación. Ese nunca es el caso con un método estático.
erickson
2
"La construcción es parte de la implementación, no de la interfaz. Cualquier código que funcione correctamente con la interfaz no se preocupa por el constructor". - Eso claramente no es cierto. En otros idiomas (por ejemplo, Swift), puedo crear nuevas instancias Tsin saberlo Testáticamente, porque prometo en una interfaz que existirá cierto constructor (o método estático) en tiempo de ejecución. El hecho de que la generación sea imposible de especificar en Java no significa que no sea algo significativo.
Raphael
48

Esto ya fue preguntado y respondido, aquí

Para duplicar mi respuesta:

Nunca tiene sentido declarar un método estático en una interfaz. No pueden ejecutarse mediante la llamada normal MyInterface.staticMethod (). Si los llama especificando la clase de implementación MyImplementor.staticMethod (), entonces debe conocer la clase real, por lo que es irrelevante si la interfaz la contiene o no.

Más importante aún, los métodos estáticos nunca se anulan, y si intenta hacerlo:

MyInterface var = new MyImplementingClass();
var.staticMethod();

Las reglas para static dicen que el método definido en el tipo de var declarado debe ejecutarse. Como se trata de una interfaz, esto es imposible.

La razón por la que no puede ejecutar "result = MyInterface.staticMethod ()" es que tendría que ejecutar la versión del método definido en MyInterface. Pero no puede haber una versión definida en MyInterface, porque es una interfaz. No tiene código por definición.

Si bien puede decir que esto equivale a "porque Java lo hace así", en realidad la decisión es una consecuencia lógica de otras decisiones de diseño, también tomadas por muy buenas razones.

DJClayworth
fuente
14
Si usa <T extend MyInterface> como un parámetro de tipo genérico, sería bueno garantizar a través de la interfaz que T puede .doSomething ().
Chris Betti
44
Si bien entiendo los argumentos, estoy de acuerdo con @Chris_Betti (incluso para los tipos no genéricos): sería bueno que la estructura del código garantice que algunas clases implementen una API estática específica. Tal vez sea posible usando un concepto diferente ...
Juh_
@Juh_, ..... o una nueva palabra clave si es absolutamente necesario. Creo que statices un término malo en los idiomas de todos modos, y se ha extendido demasiado como está. Por lo tanto, tenerlo solo ya es incompleto. Vea mi ejemplo anterior stackoverflow.com/questions/512877/… {shrug}.
3
Esto parece falso: "Nunca tiene sentido declarar un método estático en una interfaz". Si tengo una colección de clases que, sin ser instanciadas, podrían ofrecerme información, pero necesitaría una interfaz común para poner esta información estática de nivel de clase (es decir, una interfaz con un método estático reemplazable), entonces ese es un uso válido . Piense en la reflexión ++, donde podría capturar metainformación sobre las propiedades de la clase sin piratear los atributos, la reflexión, etc.
Jason
1
"Nunca tiene sentido declarar un método estático en una interfaz". Esto no es cierto: imagine que su sistema tiene una resolución de clase predeterminada. Si detecta que implementa el método ContainerInjectionInterce :: create (Container $ container), creará el objeto con esta función, por ejemplo.
user3790897
37

Normalmente esto se hace usando un patrón Factory

public interface IXMLizableFactory<T extends IXMLizable> {
  public T newInstanceFromXML(Element e);
}

public interface IXMLizable {
  public Element toXMLElement();
}
Peter Lawrey
fuente
77
+1 un patrón de fábrica suena como la solución al problema. (aunque no a la pregunta)
pvgoddijn
¿Alguien puede decirme cuál es el significado de poner <T extend IXMLizable> aquí. Soy nuevo en Java. ¿Qué hace?
Nuwan Harshakumara Piyarathna
1
@NuwanHarshakumaraPiyarathna T debe ser una clase que se extienda IXMLizable. Examine los genéricos de Java para una mejor comprensión de lo que esto significa
adrian
37

Con la llegada de Java 8 ahora es posible escribir métodos predeterminados y estáticos en la interfaz. docs.oracle/staticMethod

Por ejemplo:

public interface Arithmetic {

    public int add(int a, int b);

    public static int multiply(int a, int b) {
        return a * b;
    }
}
public class ArithmaticImplementation implements Arithmetic {

    @Override
    public int add(int a, int b) {
        return a + b;
    }

    public static void main(String[] args) {
        int result = Arithmetic.multiply(2, 3);
        System.out.println(result);
    }
}

Resultado : 6

CONSEJO: Llamar a un método de interfaz estática no requiere ser implementado por ninguna clase. Seguramente, esto sucede porque las mismas reglas para los métodos estáticos en las superclases se aplican a los métodos estáticos en las interfaces.

muchacho rebelde
fuente
Este es un ejemplo perfecto de esta pregunta.
21

Porque los métodos estáticos no pueden anularse en subclases y, por lo tanto, no pueden ser abstractos. Y todos los métodos en una interfaz son, de facto , abstractos.

Michael Myers
fuente
2
Siempre puede forzar a cada tipo a implementar cualquier método de interfaz estática. Clases de tipo, alguien?
MichaelGG
16
Salga de sí mismo y responda la pregunta: ¿Por qué no se pueden anular los métodos estáticos? Si los métodos estáticos pudieran ser anulados, ¿cómo se vería? ¿Qué podrías hacer con ellos? Esta respuesta es básicamente "No puedes porque no puedes".
erickson
10

¿Por qué no puedo definir un método estático en una interfaz Java?

En realidad puedes en Java 8.

Según el documento de Java :

Un método estático es un método asociado con la clase en la que se define en lugar de con cualquier objeto. Cada instancia de la clase comparte sus métodos estáticos.

En Java 8, una interfaz puede tener métodos predeterminados y métodos estáticos . Esto nos facilita la organización de métodos auxiliares en nuestras bibliotecas. Podemos mantener métodos estáticos específicos para una interfaz en la misma interfaz en lugar de en una clase separada.

Ejemplo de método predeterminado:

list.sort(ordering);

en vez de

Collections.sort(list, ordering);

Ejemplo de método estático (del propio documento ):

public interface TimeClient {
    // ...
    static public ZoneId getZoneId (String zoneString) {
        try {
            return ZoneId.of(zoneString);
        } catch (DateTimeException e) {
            System.err.println("Invalid time zone: " + zoneString +
                "; using default time zone instead.");
            return ZoneId.systemDefault();
        }
    }

    default public ZonedDateTime getZonedDateTime(String zoneString) {
        return ZonedDateTime.of(getLocalDateTime(), getZoneId(zoneString));
    }    
}
akhil_mittal
fuente
6

Las interfaces están relacionadas con el polimorfismo que está inherentemente vinculado a instancias de objetos, no a clases. Por lo tanto, la estática no tiene sentido en el contexto de una interfaz.

acantilado.
fuente
Lógica clara y concisa. Así poner.
Oke Uwechue
6

Primero, todas las decisiones de lenguaje son decisiones tomadas por los creadores del lenguaje. No hay nada en el mundo de la ingeniería de software o la definición del lenguaje o la escritura del compilador / intérprete que diga que un método estático no puede ser parte de una interfaz. Creé un par de idiomas y escribí compiladores para ellos; todo es solo sentarse y definir una semántica significativa. Yo diría que la semántica de un método estático en una interfaz es notablemente clara, incluso si el compilador tiene que diferir la resolución del método al tiempo de ejecución.

En segundo lugar, que usemos métodos estáticos significa que hay una razón válida para tener un patrón de interfaz que incluya métodos estáticos: no puedo hablar por ninguno de ustedes, pero uso métodos estáticos de manera regular.

La respuesta correcta más probable es que no se percibía la necesidad, en el momento en que se definió el lenguaje, de métodos estáticos en las interfaces. Java ha crecido mucho a lo largo de los años y este es un elemento que aparentemente ha ganado algo de interés. El hecho de que se haya analizado para Java 7 indica que ha aumentado a un nivel de interés que podría provocar un cambio de idioma. Yo, por mi parte, estaré feliz cuando ya no tenga que instanciar un objeto solo para poder llamar a mi método getter no estático para acceder a una variable estática en una instancia de subclase ...

chica alta
fuente
5

Los métodos estáticos no son virtuales como los métodos de instancia, así que supongo que los diseñadores de Java decidieron que no los querían en las interfaces.

Pero puede poner clases que contengan métodos estáticos dentro de las interfaces. ¡Podrías probar eso!

public interface Test {
    static class Inner {
        public static Object get() {
            return 0;
        }
    }
}
Adrian Pronk
fuente
5
  • "Hay una razón particular por la cual los métodos estáticos no pueden ser anulados".

Permítanme reformular esa pregunta para ustedes completando las definiciones.

  • "¿Hay alguna razón particular por la que los métodos resueltos en tiempo de compilación no se puedan resolver en tiempo de ejecución"?

O, para decirlo más completamente, si quiero llamar a un método sin una instancia, pero conociendo la clase, ¿cómo puedo resolverlo en función de la instancia que no tengo?

Darron
fuente
3

Varias respuestas han discutido los problemas con el concepto de métodos estáticos reemplazables. Sin embargo, a veces te encuentras con un patrón en el que parece que eso es justo lo que quieres usar.

Por ejemplo, trabajo con una capa relacional de objetos que tiene objetos de valor, pero también tiene comandos para manipular los objetos de valor. Por varias razones, cada clase de objeto de valor tiene que definir algunos métodos estáticos que permiten que el marco encuentre la instancia de comando. Por ejemplo, para crear una Persona que haría:

cmd = createCmd(Person.getCreateCmdId());
Person p = cmd.execute();

y para cargar una Persona por ID que harías

cmd = createCmd(Person.getGetCmdId());
cmd.set(ID, id);
Person p = cmd.execute();

Esto es bastante conveniente, sin embargo tiene sus problemas; notablemente la existencia de los métodos estáticos no se puede imponer en la interfaz. Un método estático reemplazable en la interfaz sería exactamente lo que necesitaríamos, si tan solo pudiera funcionar de alguna manera.

Los EJB resuelven este problema teniendo una interfaz de inicio; cada objeto sabe cómo encontrar su Hogar y el Hogar contiene los métodos "estáticos". De esta manera, los métodos "estáticos" pueden anularse según sea necesario, y no abarrotan la interfaz normal (se llama "Remota") con métodos que no se aplican a una instancia de su bean. Simplemente haga que la interfaz normal especifique un método "getHome ()". Devuelve una instancia del objeto Home (que podría ser un singleton, supongo) y la persona que llama puede realizar operaciones que afectan a todos los objetos Person.

Sr. Brillante y Nuevo 安 宇
fuente
3

Comentando EDIT: As of Java 8, static methods are now allowed in interfaces.

Es correcto, los métodos estáticos ya que Java 8 están permitidos en las interfaces, pero su ejemplo aún no funcionará. No puede simplemente definir un método estático: debe implementarlo o obtendrá un error de compilación.

flavio
fuente
2

Bueno, sin genéricos, las interfaces estáticas son inútiles porque todas las llamadas a métodos estáticos se resuelven en tiempo de compilación. Entonces, no hay un uso real para ellos.

Con los genéricos, tienen uso, con o sin una implementación predeterminada. Obviamente, tendría que haber una anulación, etc. Sin embargo, supongo que dicho uso no fue muy OO (como las otras respuestas señalan obtusamente) y, por lo tanto, no se consideró que valiera la pena el esfuerzo que requerirían implementar de manera útil.

MichaelGG
fuente
1
¿Qué tienen que ver los genéricos con esto? Un método estático en una interfaz aún no sería ejecutable.
DJClayworth
Primero, esa sería una decisión de implementación. Pero supongo que no quiere llamar a métodos estáticos en una interfaz (podría usar una clase). Pero, en cambio, quiere tener algo así como una clase de tipos o algo más sobre los parámetros de tipo. De hecho, su última edición muestra esto aún más claramente.
MichaelGG
2
Why can't I define a static method in a Java interface?

Todos los métodos en una interfaz son explícitamente abstractos y, por lo tanto, no puede definirlos como estáticos porque los métodos estáticos no pueden ser abstractos.

Aniket Thakur
fuente
1

Una interfaz nunca puede ser desreferenciada estáticamente, por ejemplo ISomething.member. Una interfaz siempre se desreferencia a través de una variable que se refiere a una instancia de una subclase de la interfaz. Por lo tanto, una referencia de interfaz nunca puede saber a qué subclase se refiere sin una instancia de su subclase.

Por lo tanto, la aproximación más cercana a un método estático en una interfaz sería un método no estático que ignora "esto", es decir, no accede a ningún miembro no estático de la instancia. En la abstracción de bajo nivel, cada método no estático (después de la búsqueda en cualquier vtable) es realmente una función con alcance de clase que toma "esto" como un parámetro formal implícito. Vea el objeto singleton de Scala y la interoperabilidad con Java como evidencia de ese concepto. Y así, cada método estático es una función con alcance de clase que no toma un parámetro "este". Por lo tanto, normalmente un método estático puede llamarse estáticamente, pero como se indicó anteriormente, una interfaz no tiene implementación (es abstracta).

Por lo tanto, obtener la aproximación más cercana a un método estático en una interfaz es utilizar un método no estático, luego no acceder a ninguno de los miembros de la instancia no estática. No habría beneficio de rendimiento posible de otra manera, porque no hay forma de vincular estáticamente (en tiempo de compilación) a ISomething.member(). El único beneficio que veo de un método estático en una interfaz es que no ingresaría (es decir, ignoraría) un "esto" implícito y, por lo tanto, no permitiría el acceso a ninguno de los miembros de la instancia no estática. Esto declararía implícitamente que la función que no accede a "esto", es inmutada y ni siquiera es de solo lectura con respecto a su clase que contiene. Pero una declaración de "estática" en una interfaz ISomethingtambién confundiría a las personas que intentaron acceder a ella conISomething.member()lo que causaría un error del compilador. Supongo que si el error del compilador fue suficientemente explicativo, sería mejor que tratar de educar a las personas sobre el uso de un método no estático para lograr lo que quieren (aparentemente, principalmente métodos de fábrica), como lo estamos haciendo aquí (y se ha repetido durante 3 Preguntas y respuestas en este sitio), por lo que obviamente es un problema que no es intuitivo para muchas personas. Tuve que pensarlo por un tiempo para entenderlo correctamente.

La forma de obtener un campo estático mutable en una interfaz es usar métodos getter y setter no estáticos en una interfaz, para acceder a ese campo estático que en la subclase. Nota aparte, las estadísticas aparentemente inmutables se pueden declarar en una interfaz Java con static final.

Shelby Moore III
fuente
0

Las interfaces solo proporcionan una lista de cosas que proporcionará una clase, no una implementación real de esas cosas, que es lo que es su elemento estático.

Si desea estática, use una clase abstracta y heredela; de lo contrario, elimine la estática.

¡Espero que ayude!

Samoz
fuente
2
Bueno, en teoría, podría definir una interfaz para incluir un comportamiento estático, es decir, "las implementaciones de esta interfaz tendrán un método estático foo () con esta firma", y dejar la implementación a la clase específica. Me he encontrado con situaciones en las que este comportamiento sería útil.
Rob
0

No puede definir métodos estáticos en una interfaz porque los métodos estáticos pertenecen a una clase, no a una instancia de clase, y las interfaces no son clases. Leer más aquí.

Sin embargo, si quieres puedes hacer esto:

public class A {
  public static void methodX() {
  }
}

public class B extends A {
  public static void methodX() {
  }
}

En este caso, lo que tiene son dos clases con 2 métodos estáticos distintos llamados methodX ().

Handerson
fuente
0

Supongamos que puedes hacerlo; considera este ejemplo:

interface Iface {
  public static void thisIsTheMethod();
}

class A implements Iface {

  public static void thisIsTheMethod(){
    system.out.print("I'm class A");
  }

}

class B extends Class A {

  public static void thisIsTheMethod(){
    System.out.print("I'm class B");
  } 
}

SomeClass {

  void doStuff(Iface face) {
    IFace.thisIsTheMethod();
    // now what would/could/should happen here.
  }

}
pvgoddijn
fuente
1
Imprimiría "Soy clase A". Sin embargo, si escribiste A.thisIsTheMethod(), imprimiría "Soy de clase B".
cdmckay
pero oyr llamando a los métodos en la interfaz ¿cómo podría usted (o el compilador) saber qué método debería llamarse? (recuerde que puede haber más clases implementando dricetly Iface
pvgoddijn
lo siento, quería decir: Sin embargo, si escribes B.thisIsTheMethod(), se imprimirá "Soy de clase B".
cdmckay
Dije IFace.thisIsTHeMethod a propósito porque ahí radica el problema. no sería posible llamarlo en la interfaz sin un comportamiento indefinido (a pesar de que está declarado)
pvgoddijn
0

Algo que podría implementarse es la interfaz estática (en lugar del método estático en una interfaz). Todas las clases que implementan una interfaz estática dada deben implementar los métodos estáticos correspondientes. Puede obtener una interfaz SI estática desde cualquier clase clazz usando

SI si = clazz.getStatic(SI.class); // null if clazz doesn't implement SI
// alternatively if the class is known at compile time
SI si = Someclass.static.SI; // either compiler errror or not null

entonces puedes llamar si.method(params). ¡Esto sería útil (por ejemplo, para el patrón de diseño de fábrica) porque puede obtener (o verificar la implementación de) la implementación de métodos estáticos SI desde una clase desconocida en tiempo de compilación! Es necesario un despacho dinámico y puede anular los métodos estáticos (si no son finales) de una clase extendiéndolo (cuando se llama a través de la interfaz estática). Obviamente, estos métodos solo pueden acceder a variables estáticas de su clase.

Christophe Bouchon
fuente
0

Si bien me doy cuenta de que Java 8 resuelve este problema, pensé en intervenir en un escenario en el que estoy trabajando actualmente (bloqueado con el uso de Java 7) donde sería útil poder especificar métodos estáticos en una interfaz.

Tengo varias definiciones de enumeración donde he definido los campos "id" y "displayName" junto con métodos auxiliares que evalúan los valores por varias razones. La implementación de una interfaz me permite garantizar que los métodos getter estén en su lugar, pero no los métodos auxiliares estáticos. Al ser una enumeración, realmente no hay una manera limpia de descargar los métodos auxiliares en una clase abstracta heredada o algo similar, por lo que los métodos deben definirse en la enumeración misma. Además, debido a que es una enumeración, nunca podría pasarlo realmente como un objeto instanciado y tratarlo como el tipo de interfaz, pero poder exigir la existencia de los métodos auxiliares estáticos a través de una interfaz es lo que me gusta de está soportado en Java 8.

Aquí hay un código que ilustra mi punto.

Definición de interfaz:

public interface IGenericEnum <T extends Enum<T>> {
    String getId();
    String getDisplayName();
    //If I was using Java 8 static helper methods would go here
}

Ejemplo de una definición de enumeración:

public enum ExecutionModeType implements IGenericEnum<ExecutionModeType> {
    STANDARD ("Standard", "Standard Mode"),
    DEBUG ("Debug", "Debug Mode");

    String id;
    String displayName;

    //Getter methods
    public String getId() {
        return id;
    }

    public String getDisplayName() {
        return displayName;
    }

    //Constructor
    private ExecutionModeType(String id, String displayName) {
        this.id = id;
        this.displayName = displayName;
    }

    //Helper methods - not enforced by Interface
    public static boolean isValidId(String id) {
        return GenericEnumUtility.isValidId(ExecutionModeType.class, id);
    }

    public static String printIdOptions(String delimiter){
        return GenericEnumUtility.printIdOptions(ExecutionModeType.class, delimiter);
    }

    public static String[] getIdArray(){
        return GenericEnumUtility.getIdArray(ExecutionModeType.class);
    }

    public static ExecutionModeType getById(String id) throws NoSuchObjectException {
        return GenericEnumUtility.getById(ExecutionModeType.class, id);
    }
}

Definición de utilidad genérica enum:

public class GenericEnumUtility {
    public static <T extends Enum<T> & IGenericEnum<T>> boolean isValidId(Class<T> enumType, String id) {       
        for(IGenericEnum<T> enumOption : enumType.getEnumConstants()) {
            if(enumOption.getId().equals(id)) {
                return true;
            }
        }

        return false;
    }

    public static <T extends Enum<T> & IGenericEnum<T>> String printIdOptions(Class<T> enumType, String delimiter){
        String ret = "";
        delimiter = delimiter == null ? " " : delimiter;

        int i = 0;
        for(IGenericEnum<T> enumOption : enumType.getEnumConstants()) {
            if(i == 0) {
                ret = enumOption.getId();
            } else {
                ret += delimiter + enumOption.getId();
            }           
            i++;
        }

        return ret;
    }

    public static <T extends Enum<T> & IGenericEnum<T>> String[] getIdArray(Class<T> enumType){
        List<String> idValues = new ArrayList<String>();

        for(IGenericEnum<T> enumOption : enumType.getEnumConstants()) {
            idValues.add(enumOption.getId());
        }

        return idValues.toArray(new String[idValues.size()]);
    }

    @SuppressWarnings("unchecked")
    public static <T extends Enum<T> & IGenericEnum<T>> T getById(Class<T> enumType, String id) throws NoSuchObjectException {
        id = id == null ? "" : id;
        for(IGenericEnum<T> enumOption : enumType.getEnumConstants()) {
            if(id.equals(enumOption.getId())) {
                return (T)enumOption;
            }
        }

        throw new NoSuchObjectException(String.format("ERROR: \"%s\" is not a valid ID. Valid IDs are: %s.", id, printIdOptions(enumType, " , ")));
    }
}
Ryan
fuente
0

Supongamos que se permitieran métodos estáticos en las interfaces: * Forzarían a todas las clases implementadoras a declarar ese método. * Las interfaces generalmente se usarían a través de objetos, por lo que los únicos métodos efectivos en ellas serían los no estáticos. * Cualquier clase que conozca una interfaz particular podría invocar sus métodos estáticos. Por lo tanto, el método estático de una clase implementadora se llamaría debajo, pero la clase invocador no sabe cuál. ¿Cómo saberlo? ¡No tiene instanciación adivinar eso!

Se pensaba que las interfaces se usaban al trabajar con objetos. De esta manera, un objeto se instancia de una clase particular, por lo que este último asunto se resuelve. La clase que invoca no necesita saber qué clase en particular es porque la instanciación puede ser realizada por una tercera clase. Entonces la clase que invoca solo conoce la interfaz.

Si queremos que esto se extienda a los métodos estáticos, deberíamos tener la posibilidad de especificar una clase de implementación antes, y luego pasar una referencia a la clase que invoca. Esto podría usar la clase a través de los métodos estáticos en la interfaz. Pero, ¿cuál es la diferencia entre esta referencia y un objeto? Solo necesitamos un objeto que represente lo que era la clase. Ahora, el objeto representa la clase anterior y podría implementar una nueva interfaz que incluye los métodos estáticos antiguos, que ahora no son estáticos.

Las metaclases sirven para este propósito. Puedes probar la clase Class of Java. Pero el problema es que Java no es lo suficientemente flexible para esto. No puede declarar un método en el objeto de clase de una interfaz.

Este es un problema meta: cuando necesitas hacer el culo

..blah blah

de todos modos, tiene una solución fácil: hacer que el método no sea estático con la misma lógica. Pero luego tendría que crear primero un objeto para llamar al método.

Beibichunai
fuente
0

Para resolver esto: error: falta el cuerpo del método o declara el vacío estático abstracto main (String [] args);

interface I
{
    int x=20;
    void getValue();
    static void main(String[] args){};//Put curly braces 
}
class InterDemo implements I
{
    public void getValue()
    {
    System.out.println(x);
    }
    public static void main(String[] args)
    {
    InterDemo i=new InterDemo();
    i.getValue();   
    }

}

salida: 20

Ahora podemos usar el método estático en la interfaz

Aashish Pawar
fuente
1
Sin embargo, eso es inútil. La definición de un método estático en una interfaz no impone definiciones adicionales en las clases que implementan dicha interfaz. Si solo elimina el método estático por completo de la interfaz I, su código se compilará y se ejecutará de todos modos sin problemas. En otras palabras, NO está anulando el método principal de la interfaz I en la clase InterDemo, solo está creando un nuevo método con la misma firma.
Fran Marzoa
-2

Creo que Java no tiene métodos de interfaz estática porque no los necesita. Puede pensar que lo hace, pero ... ¿Cómo los usaría? Si quieres llamarlos como

MyImplClass.myMethod()

entonces no necesita declararlo en la interfaz. Si quieres llamarlos como

myInstance.myMethod()

entonces no debería ser estático. Si realmente va a usar la primera manera, pero solo quiere imponer que cada implementación tenga un método estático, entonces es realmente una convención de codificación, no un contrato entre instancias que implementa una interfaz y un código de llamada.

Las interfaces le permiten definir el contrato entre la instancia de clase que implementa la interfaz y el código de llamada. Y Java le ayuda a asegurarse de que este contrato no se viole, por lo que puede confiar en él y no preocuparse de qué clase implementa este contrato, solo "alguien que firmó un contrato" es suficiente. En caso de interfaces estáticas su código

MyImplClass.myMethod()

no se basa en el hecho de que cada implementación de interfaz tiene este método, por lo que no necesita Java para ayudarlo a estar seguro.

Pavel Feldman
fuente
-5

¿Cuál es la necesidad de un método estático en la interfaz? Los métodos estáticos se utilizan básicamente cuando no es necesario crear una instancia de objeto. La idea general de la interfaz es incorporar los conceptos OOP con la introducción del método estático que está desviando del concepto.

VIckyb
fuente