¿Por qué se utilizaría una interfaz anidada estática en Java?

235

Acabo de encontrar una interfaz anidada estática en nuestra base de código.

class Foo {
    public static interface Bar {
        /* snip */
    }
    /* snip */
}

Nunca he visto esto antes. El desarrollador original está fuera de alcance. Por lo tanto, tengo que preguntar SO:

¿Cuáles son las semánticas detrás de una interfaz estática? ¿Qué cambiaría si elimino el static? ¿Por qué alguien haría esto?

Mes.
fuente
2
Esta no es una interfaz interna: es una interfaz anidada . Interior tiene un significado específico en Java.
Marqués de Lorne el

Respuestas:

293

La palabra clave estática en el ejemplo anterior es redundante (una interfaz anidada es automáticamente "estática") y se puede eliminar sin ningún efecto en la semántica; Recomendaría que se elimine. Lo mismo ocurre con "público" en los métodos de interfaz y "público final" en los campos de interfaz: los modificadores son redundantes y simplemente agregan desorden al código fuente.

De cualquier manera, el desarrollador simplemente declara una interfaz llamada Foo.Bar. No hay más asociación con la clase que lo encierra, excepto que el código que no puede acceder a Foo tampoco podrá acceder a Foo.Bar. (Desde el código fuente: ¡el bytecode o la reflexión pueden acceder a Foo.Bar incluso si Foo es un paquete privado!)

Es un estilo aceptable crear una interfaz anidada de esta manera si espera que se use solo desde la clase externa, de modo que no cree un nuevo nombre de nivel superior. Por ejemplo:

public class Foo {
    public interface Bar {
        void callback();
    }
    public static void registerCallback(Bar bar) {...}
}
// ...elsewhere...
Foo.registerCallback(new Foo.Bar() {
    public void callback() {...}
});
Jesse Glick
fuente
1
En la respuesta de Jesse Glick, lo que esto significa: (desde el código fuente: el código de bytes o la reflexión pueden acceder a Foo.Bar, incluso si Foo es un paquete privado).
Vasu
Kaillash, se puede acceder a los métodos privados mediante relfection (en el paquete reflect) y mediante el acceso directo al bytecode de los archivos .class generados.
gmoore
2
Por "bytecode ... puede acceder a Foo.Bar" quiero decir que una clase compilada que hace referencia a Foo.Bar se puede cargar y ejecutar incluso si no puede hacer referencia a Foo. Esto podría suceder si la clase se compiló en un momento anterior cuando Foo era público, o si la clase se ensambló a mano o se compiló desde algún lenguaje que no sea Java, etc. Sin embargo, el compilador de Java verifica el modificador de acceso en la clase adjunta. incluso cuando el bytecode resultante no se referiría a esa clase envolvente.
Jesse Glick
@Jesse ¿Se puede acceder a una clase estática privada en una clase privada de nivel superior a través de la reflexión?
Pacerier
1
@Pacerier: no. Más precisamente, puede cargar la clase e inspeccionar sus miembros, pero no puede instanciarla o invocar métodos en ella sin usar setAccessible (true). En otras palabras, es visible, pero no accesible, a través de la reflexión. Pero si la clase estática anidada es pública, es accesible de forma predeterminada a través de la reflexión, aunque no es accesible estáticamente (durante la compilación).
Jesse Glick
72

La pregunta ha sido respondida, pero una buena razón para usar una interfaz anidada es si su función está directamente relacionada con la clase en la que se encuentra. Un buen ejemplo de esto es a Listener. Si tuviera una clase Fooy quisiera que otras clases pudieran escuchar eventos en ella, podría declarar una interfaz llamada FooListener, que está bien, pero probablemente sería más claro declarar una interfaz anidada e implementar esas otras clases Foo.Listener( una clase anidada Foo.Eventno es mala junto con esto).

ColinD
fuente
11
Un ejemplo típico es java.util.Map.Entry(que es una interfaz anidada en otra interfaz).
Paŭlo Ebermann
44
Sé que este es un tema antiguo, pero preferiría que la clase externa esté en su propio paquete y que las interfaces suplementarias (por ejemplo Map.Entry) o las clases también estén dentro de ese paquete. Digo esto porque me gusta mantener mis clases cortas y al grano. También un lector puede ver qué otras entidades están relacionadas con una clase mirando las clases en el paquete. Probablemente tendría un paquete java.collections.mappara mapas. Se trata de OO y modularidad. java.utiltiene demasiado utiles como common- un olor IMO
David Kerr
1
@ Shaggy: java.utilciertamente tiene demasiado. Dicho esto, tampoco creo que sea ideal dividirlo en paquetes tan finos como sugieres.
ColinD
1
@DavidKerr Suponga que llama a esta interfaz java.util.MapEntryfuera de su propio paquete. Primer reflejo: ¿cuáles son las interfaces relacionadas con esta clase? Miro a la clase. A menos que javadoc se vincule a esta interfaz, no tengo idea de su relación. Además, las personas no miran el paquete de importación. Todos tienen sus propias opiniones. Nadie tiene razón, nadie está equivocado
Raymond Chenon
@RaymondChenon parte de lo que dije fue poner Map y sus clases asociadas, por ejemplo, Map.Entry en un paquete separado, por ejemplo, java.collections.map. De esa manera, todo el código relacionado con el mapa está en una única ubicación (el paquete) y la clase de mapa de nivel superior no está cargada. Quizás pondría las implementaciones relacionadas (HashMap, etc.) en el mismo paquete.
David Kerr
14

Las interfaces de los miembros son implícitamente estáticas. El modificador estático en su ejemplo se puede eliminar sin cambiar la semántica del código. Consulte también la Especificación del lenguaje Java 8.5.1. Declaraciones de tipo de miembro estático

Bas Leijdekkers
fuente
Esta no es una 'interfaz interna': es una interfaz anidada. Interior tiene un significado específico en Java.
Marqués de Lorne el
@EJP, leí varios blogs usando los términos indistintamente. ¿Existe la interfaz interna? ¿Cómo son diferentes de la interfaz anidada?
Número945
@BreakingBenjamin, interfaz anidada significa estática. Existe clase interna y clase anidada, pero no interfaz interna. Incluso si es así, debería llamarse como interfaz anidada. Interior: no estático y anidado es estático.
Sundar Rajan
9

Una interfaz interna tiene que ser estática para poder acceder. La interfaz no está asociada con instancias de la clase, sino con la clase misma, por lo que se accedería de esta Foo.Barmanera:

public class Baz implements Foo.Bar {
   ...
}

En la mayoría de los casos, esto no es diferente de una clase interna estática.

Clinton N. Dreisbach
fuente
35
Una interfaz anidada es automáticamente estática, tanto si se escribe la palabra clave como si no.
Paŭlo Ebermann
3
Realmente necesitamos una forma de que la comunidad vote para aceptar una respuesta diferente: stackoverflow.com/a/74400/632951
Pacerier
@ ClintonN.Dreisbach ¿Puede explicar qué significa The interface isn't associated with instances of the class, but with the class itselfmás, no lo entendí
Kasun Siyambalapitiya
6

La respuesta de Jesse es cercana, pero creo que hay un mejor código para demostrar por qué una interfaz interna puede ser útil. Mire el código a continuación antes de seguir leyendo. ¿Puedes encontrar por qué la interfaz interna es útil? La respuesta es que la clase DoSomethingAlready se puede instanciar con cualquier clase que implemente A y C; no solo la clase concreta Zoo. Por supuesto, esto se puede lograr incluso si AC no es interno, pero imagine concatenar nombres más largos (no solo A y C), y haciendo esto para otras combinaciones (digamos, A y B, C y B, etc.) y usted fácilmente ver cómo las cosas se salen de control. Sin mencionar que las personas que revisan su árbol de origen se verán abrumadas por las interfaces que son significativas solo en una clase.Una interfaz interna permite la construcción de tipos personalizados y mejora su encapsulación .

class ConcreteA implements A {
 :
}

class ConcreteB implements B {
 :
}

class ConcreteC implements C {
 :
}

class Zoo implements A, C {
 :
}

class DoSomethingAlready {
  interface AC extends A, C { }

  private final AC ac;

  DoSomethingAlready(AC ac) {
    this.ac = ac;
  }
}
usuario1982892
fuente
2
Esto no tiene sentido. La clase Zoono no implementa la interfaz AC, por lo tanto, los casos de Zoolata no pueden pasar al constructor de DoSomethingAlreadyque espera AC. El hecho de que ACextienda ambos, Ay C, no implica que las clases se implementen Ay Cmágicamente también se implementen AC.
Holger
3

Para responder su pregunta muy directamente, mire Map.Entry.

Map.Entry

También esto puede ser útil

Entrada de blog de Inerfaces anidadas estáticas

Henry B
fuente
44
Este es un ejemplo, pero no es realmente una respuesta.
Paŭlo Ebermann
Yo uso Map.Entry para crear muchos objetos "Emparejar". Está expuesto La implementación de Pair tiene dos escuelas de pensamiento, pero ese no es el punto aquí. Map.Entry puede ser interno pero lo uso afuera.
Ravindranath Akila
0

Típicamente veo clases internas estáticas. Las clases internas estáticas no pueden hacer referencia a las clases que contienen, mientras que las clases no estáticas sí. A menos que se encuentre con algunas colisiones de paquetes (ya hay una interfaz llamada Bar en el mismo paquete que Foo), creo que lo convertiría en su propio archivo. También podría ser una decisión de diseño para imponer la conexión lógica entre Foo y Bar. Quizás el autor pretendía que Bar solo se usara con Foo (aunque una interfaz interna estática no impondrá esto, solo una conexión lógica)

basszero
fuente
'Estático interno' es una contradicción en los términos. Clases anidadas son ya sea estática o interior.
Marqués de Lorne
0

Si cambia la clase Foo a la interfaz Foo, la palabra clave "pública" en el ejemplo anterior también será redundante porque

La interfaz definida dentro de otra interfaz será implícitamente pública estática.

Danylo Volokh
fuente
No respondió la pregunta
llovió el
0

En 1998, Philip Wadler sugirió una diferencia entre interfaces estáticas e interfaces no estáticas.

Hasta donde puedo ver, la única diferencia en hacer una interfaz no estática es que ahora puede incluir clases internas no estáticas; entonces el cambio no invalidaría ningún programa Java existente.

Por ejemplo, propuso una solución al problema de expresión , que es el desajuste entre la expresión como "cuánto puede expresar su idioma" por un lado y la expresión como "los términos que está tratando de representar en su idioma" por otro lado .

Un ejemplo de la diferencia entre interfaces anidadas estáticas y no estáticas se puede ver en su código de muestra :

// This code does NOT compile
class LangF<This extends LangF<This>> {
    interface Visitor<R> {
        public R forNum(int n);
    }

    interface Exp {
        // since Exp is non-static, it can refer to the type bound to This
        public <R> R visit(This.Visitor<R> v);
    }
}

Su sugerencia nunca lo hizo en Java 1.5.0. Por lo tanto, todas las demás respuestas son correctas: no hay diferencia en las interfaces anidadas estáticas y no estáticas.

Pindatjuh
fuente
Cabe señalar que todo esto se refiere a GJ, que fue un procesador muy temprano para genéricos en Java.
Marqués de Lorne
-1

En Java, la interfaz / clase estática permite que la interfaz / clase se use como una clase de nivel superior, es decir, puede ser declarada por otras clases. Entonces, puedes hacer:

class Bob
{
  void FuncA ()
  {
    Foo.Bar foobar;
  }
}

Sin la estática, lo anterior no se podría compilar. La ventaja de esto es que no necesita un nuevo archivo fuente solo para declarar la interfaz. También asocia visualmente la barra de interfaz a la clase Foo, ya que debe escribir Foo.Bar e implica que la clase Foo hace algo con instancias de Foo.Bar.

Una descripción de los tipos de clase en Java .

Skizz
fuente
Sin la estática que hace de compilación. El 'estático' es redundante.
Marqués de Lorne
@EJP: Según el artículo al que me vinculé, sin la estática, la clase interna solo puede ser utilizada por la clase propietaria y no por nada fuera de la clase adeudada. La estática la convierte en una clase de nivel superior anidada y puede ser utilizada por objetos fuera del alcance de la clase propietaria (al menos, según el artículo). La estática no es completamente redundante y afecta el alcance de la clase interna. Esto se aplica a las clases, las interfaces siempre pueden actuar como objetos de nivel superior (es necesario leer las especificaciones del idioma para verificar). Quizás debería haber separado la interfaz y las partes de clase de mi respuesta (mi error).
Skizz
-6

Estático significa que cualquier parte de la clase del paquete (proyecto) puede acceder a él sin usar un puntero. Esto puede ser útil u obstaculizar dependiendo de la situación.

El ejemplo perfecto de las líneas útiles de los métodos "estáticos" es la clase Math. Todos los métodos en matemáticas son estáticos. Esto significa que no tiene que salir de su camino, hacer una nueva instancia, declarar variables y almacenarlas en aún más variables, solo puede ingresar sus datos y obtener un resultado.

La estática no siempre es tan útil. Si está haciendo una comparación de casos, por ejemplo, es posible que desee almacenar datos de varias maneras diferentes. No puede crear tres métodos estáticos con firmas idénticas. Necesita 3 instancias diferentes, no estáticas, y luego puede y comparar, porque si es estático, los datos no cambiarán junto con la entrada.

Los métodos estáticos son buenos para devoluciones únicas y cálculos rápidos o datos fáciles de obtener.

Vordreller
fuente
1
Sé lo que significa estática. Nunca antes lo había visto en una interfaz. Por lo tanto, su pregunta está fuera de tema - lo siento
Mo.
1
Creo que su respuesta está fuera de tema.
Koray Tugay