¿Por qué no hay métodos estáticos en las interfaces, pero los campos estáticos y las clases internas están bien? [pre-Java 8] [duplicado]

StackOverflow https://stackoverflow.com/questions/129267

  •  02-07-2019
  •  | 
  •  

Pregunta

    

Esta pregunta ya tiene una respuesta aquí:

         

Se han formulado algunas preguntas aquí sobre por qué no puede definir métodos estáticos dentro de las interfaces, pero ninguno de ellos aborda una inconsistencia básica: por qué puede definir campos estáticos y tipos internos estáticos dentro de una interfaz, pero no métodos estáticos ?

Los tipos internos estáticos quizás no sean una comparación justa, ya que eso es solo azúcar sintáctico que genera una nueva clase, pero ¿por qué los campos pero no los métodos?

Un argumento en contra de los métodos estáticos dentro de las interfaces es que rompe la estrategia de resolución de la tabla virtual utilizada por la JVM, pero ¿no debería aplicarse igualmente a los campos estáticos, es decir, el compilador puede simplemente alinearlo?

La consistencia es lo que deseo, y Java no debería haber soportado ninguna estadística de ninguna forma dentro de una interfaz, o debería ser coherente y permitirlos.

¿Fue útil?

Solución

Se ha realizado una propuesta oficial para permitir métodos estáticos en las interfaces en Java 7. Esta propuesta se realiza en Project Coin .

Mi opinión personal es que es una gran idea. No hay dificultad técnica en la implementación, y es algo muy lógico y razonable de hacer. Hay varias propuestas en Project Coin que espero que nunca formen parte del lenguaje Java, pero esta es una que podría limpiar muchas API. Por ejemplo, el La clase Collections tiene métodos estáticos para manipular cualquier implementación de List ; estos podrían incluirse en la interfaz List .


Actualización: En el Java Posse Podcast # 234, Joe D'arcy mencionó brevemente la propuesta, diciendo que era "compleja". y probablemente no entraría en Project Coin.


Actualización: Si bien no llegaron a Project Coin para Java 7, Java 8 admite funciones estáticas en las interfaces.

Otros consejos

Voy a seguir con mi teoría favorita con esta, que es que la falta de consistencia en este caso es una cuestión de conveniencia más que de diseño o necesidad, ya que no he escuchado ningún argumento convincente de que sea de esos dos.

Los campos estáticos están allí (a) porque estaban allí en JDK 1.0, y muchas decisiones poco fiables se tomaron en JDK 1.0, y (b) los campos finales estáticos en las interfaces son lo más cercano que java tenía a las constantes en ese momento.

Se permitieron clases internas estáticas en las interfaces porque eso es azúcar sintáctica pura: la clase interna no tiene nada que ver con la clase principal.

Por lo tanto, los métodos estáticos no están permitidos simplemente porque no hay una razón convincente para hacerlo; la consistencia no es lo suficientemente convincente como para cambiar el status quo.

Por supuesto, esto podría permitirse en futuras versiones de JLS sin romper nada.

Nunca tiene sentido declarar un método estático en una interfaz. No pueden ejecutarse mediante la llamada normal MyInterface.staticMethod (). (EDITAR: dado que esa última oración confundió a algunas personas, llamar a MyClass.staticMethod () ejecuta con precisión la implementación de staticMethod en MyClass, ¡que si MyClass es una interfaz no puede existir!) 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 declarado de var debe ejecutarse. Como se trata de una interfaz, esto es imposible.

Por supuesto, siempre puede eliminar la palabra clave estática del método. Todo funcionará bien. Es posible que deba suprimir algunas advertencias si se llama desde un método de instancia.

Para responder algunos de los comentarios a continuación, 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.

El propósito de las interfaces es definir un contrato sin proporcionar una implementación. Por lo tanto, no puede tener métodos estáticos, ya que tendrían que tener una implementación ya en la interfaz ya que no puede anular los métodos estáticos. En cuanto a los campos, solo se permiten los campos finales estáticos, que son, esencialmente, constantes (en 1.5+ también puede tener enumeraciones en las interfaces). Las constantes están ahí para ayudar a definir la interfaz sin números mágicos.

Por cierto, no es necesario especificar explícitamente los modificadores static final para los campos en las interfaces, porque solo se permiten los campos estáticos finales.

Este es un hilo viejo, pero esta es una pregunta muy importante para todos. Como me di cuenta de esto hoy solo, estoy tratando de explicarlo de una manera más limpia:

El objetivo principal de la interfaz es proporcionar algo que no se puede implementar, por lo que si proporcionan

  

métodos estáticos permitidos

entonces puede llamar a ese método usando interfaceName.staticMethodName () , pero este es un método no implementado y no contiene nada. Por lo tanto, es inútil permitir métodos estáticos. Por lo tanto, no proporcionan esto en absoluto.

  

los campos estáticos están permitidos

porque los campos no son implementables, por implementable quiero decir que no puede realizar ninguna operación lógica en el campo, puede hacer la operación en el campo. Por lo tanto, no está cambiando el comportamiento del campo, por eso están permitidos.

  

Se permiten clases internas

Las clases internas están permitidas porque después de la compilación se crea un archivo de clase diferente de la clase interna, digamos InterfaceName $ InnerClassName.class , por lo que básicamente está proporcionando implementación en una entidad diferente, pero no en la interfaz. Por lo tanto, se proporciona la implementación en clases internas.

Espero que esto ayude.

En realidad, a veces hay razones por las que alguien puede beneficiarse de los métodos estáticos. Se pueden usar como métodos de fábrica para las clases que implementan la interfaz. Por ejemplo, esa es la razón por la que tenemos la interfaz Colección y la clase Colecciones en openjdk ahora. Así que hay soluciones alternativas como siempre: proporcione a otra clase un constructor privado que sirva como un "espacio de nombres". para los métodos estáticos.

Antes de Java 5, un uso común para los campos estáticos era:

interface HtmlConstants {
    static String OPEN = "<";
    static String SLASH_OPEN = "</";
    static String CLOSE = ">";
    static String SLASH_CLOSE = " />";
    static String HTML = "html";
    static String BODY = "body";
    ...
}

public class HtmlBuilder implements HtmlConstants { // implements ?!?
    public String buildHtml() {
       StringBuffer sb = new StringBuffer();
       sb.append(OPEN).append(HTML).append(CLOSE);
       sb.append(OPEN).append(BODY).append(CLOSE);
       ...
       sb.append(SLASH_OPEN).append(BODY).append(CLOSE);
       sb.append(SLASH_OPEN).append(HTML).append(CLOSE);
       return sb.toString();
    }
}

Esto significaba que HtmlBuilder no tendría que calificar cada constante, por lo que podría usar OPEN en lugar de HtmlConstants.OPEN

Usar implementos de esta manera es en última instancia confuso.

Ahora con Java 5, tenemos la sintaxis importar estática para lograr el mismo efecto:

private final class HtmlConstants {
    ...
    private HtmlConstants() { /* empty */ }
}

import static HtmlConstants.*;
public class HtmlBuilder { // no longer uses implements
    ...
}

No hay una razón real para no tener métodos estáticos en las interfaces, excepto: los diseñadores del lenguaje Java no lo querían así. Desde un punto de vista técnico, tendría sentido permitirlos. Después de todo, una clase abstracta también puede tenerlos. Supongo, pero no lo probé, que puedes "hacer manualidades". código de bytes donde la interfaz tiene un método estático y debería funcionar sin problemas para llamar al método y / o usar la interfaz como de costumbre.

A menudo me pregunto ¿por qué los métodos estáticos? Tienen sus usos, pero los métodos de nivel de paquete / espacio de nombres probablemente cubrirían 80 de los métodos estáticos para los que se usan.

Se te ocurren dos razones principales:

  1. Los métodos estáticos en Java no pueden ser anulados por subclases, y esto es mucho más importante para los métodos que los campos estáticos. En la práctica, nunca he querido anular un campo en una subclase, pero anulo los métodos todo el tiempo. Por lo tanto, tener métodos estáticos evita que una clase que implementa la interfaz proporcione su propia implementación de ese método, lo que en gran medida anula el propósito de usar una interfaz.

  2. Las interfaces no deben tener código; para eso están las clases abstractas. El objetivo de una interfaz es permitirte hablar sobre objetos posiblemente no relacionados que tienen un cierto conjunto de métodos. En realidad, proporcionar una implementación de esos métodos está fuera de los límites de las interfaces que se pretende que sean.

Los métodos estáticos están vinculados a una clase. En Java, una interfaz no es técnicamente una clase, es un tipo, pero no una clase (por lo tanto, la palabra clave se implementa y las interfaces no extienden Object). Debido a que las interfaces no son clases, no pueden tener métodos estáticos, porque no hay una clase real a la que adjuntar.

Puede llamar a InterfaceName.class para obtener el objeto de clase correspondiente a la interfaz, pero la clase de clase establece específicamente que representa clases e interfaces en una aplicación Java. Sin embargo, la interfaz en sí no se trata como una clase y, por lo tanto, no puede adjuntar un método estático.

Solo los campos finales estáticos se pueden declarar en una interfaz (al igual que los métodos, que son públicos incluso si no incluye la palabra clave "público", los campos estáticos son "finales" con o sin la palabra clave).

Estos son solo valores, y se copiarán literalmente donde sea que se usen en el momento de la compilación, por lo que en realidad nunca "llama". campos estáticos en tiempo de ejecución. Tener un método estático no tendría la misma semántica, ya que implicaría llamar a una interfaz sin una implementación, lo que Java no permite.

La razón es que todos los métodos definidos en una interfaz son abstractos independientemente de si declara explícitamente o no ese modificador. Un método estático abstracto no es una combinación permitida de modificadores, ya que los métodos estáticos no pueden anularse.

En cuanto a por qué las interfaces permiten campos estáticos. Tengo la sensación de que debería considerarse una "característica". La única posibilidad que se me ocurre sería agrupar constantes en las que las implementaciones de la interfaz estarían interesadas.

Estoy de acuerdo en que la coherencia habría sido un mejor enfoque. No se deben permitir miembros estáticos en una interfaz.

Creo que se puede acceder a los métodos estáticos sin crear un objeto y la interfaz no permite crear un objeto para restringir a los programadores el uso de los métodos de la interfaz directamente en lugar de su clase implementada. Pero si define un método estático en una interfaz, puede acceder a él directamente sin su implementación. Por lo tanto, los métodos estáticos no están permitidos en las interfaces. No creo que la consistencia deba ser una preocupación.

El método estático de la interfaz Java 1.8 es visible solo para los métodos de interfaz, si eliminamos el método methodSta1 () de la clase InterfaceExample, no podremos usarlo para el objeto InterfaceExample. Sin embargo, como otros métodos estáticos, podemos usar métodos estáticos de interfaz usando el nombre de clase. Por ejemplo, una declaración válida será: exp1.methodSta1 ();

Entonces, después de mirar el siguiente ejemplo, podemos decir: 1) El método estático de la interfaz Java es parte de la interfaz, no podemos usarlo para implementar objetos de clase.

2) Los métodos estáticos de la interfaz Java son buenos para proporcionar métodos de utilidad, por ejemplo, verificación nula, clasificación de colecciones, registro, etc.

3) El método estático de la interfaz Java nos ayuda a proporcionar seguridad al no permitir que las clases de implementación (InterfaceExample) las anulen.

4) No podemos definir el método estático de interfaz para los métodos de clase Object, obtendremos un error del compilador como "Este método estático no puede ocultar el método de instancia de Object". Esto se debe a que no está permitido en Java, ya que Object es la clase base para todas las clases y no podemos tener un método estático de nivel de clase y otro método de instancia con la misma firma.

5) Podemos usar métodos estáticos de la interfaz de Java para eliminar clases de utilidad como Colecciones y mover todos sus métodos estáticos a la interfaz correspondiente,    eso sería fácil de encontrar y usar.

public class InterfaceExample implements exp1 {

    @Override
    public void method() {
        System.out.println("From method()");
    }

    public static void main(String[] args) {
        new InterfaceExample().method2();
        InterfaceExample.methodSta2();      //  <---------------------------    would not compile
        // methodSta1();                        //  <---------------------------    would not compile
        exp1.methodSta1();
    }

    static void methodSta2() {          //          <-- it compile successfully but it can't be overridden in child classes
        System.out.println("========= InterfaceExample :: from methodSta2() ======");
    }
}


interface exp1 {

    void method();
    //protected void method1();         //          <--      error
    //private void method2();           //          <--      error
    //static void methodSta1();         //          <--      error it require body in java 1.8

    static void methodSta1() {          //          <-- it compile successfully but it can't be overridden in child classes
        System.out.println("========= exp1:: from methodSta1() ======");
    }

    static void methodSta2() {          //          <-- it compile successfully but it can't be overridden in child classes
        System.out.println("========= exp1:: from methodSta2() ======");
    }

    default void method2() { System.out.println("---  exp1:: from method2() ---");}
    //synchronized default void method3() { System.out.println("---");}             // <-- Illegal modifier for the interface method method3; only public, abstract, default, static 
                                                                                // and strictfp are permitted
    //final default void method3() { System.out.println("---");} //             <--      error
}
Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top