java - son - que es una interfaz interna
¿Es verdad que cada clase interna requiere una instancia adjunta? (3)
El término clase interna se interpreta convencionalmente como "una clase anidada que requiere una instancia adjunta". Sin embargo, el JLS establece lo siguiente:
8.1.3. Clases internas e instancias adjuntas
[...]
Las clases internas incluyen clases de miembros locales (§14.3), anónimas (§15.9.5) y no estáticas (§8.5).
[...]
Una instancia de una clase interna cuya declaración ocurre en un contexto estático no tiene instancias que encierran léxicamente.
También,
15.9.5. Declaraciones de Clase Anónimas
[...]
Una clase anónima es siempre una clase interna (§8.1.3); nunca es
static
(§8.1.1, §8.5.1).
Y es bien sabido que una clase anónima puede ser declarada en un contexto estático:
class A {
int t() { return 1; }
static A a = new A() { int t() { return 2; } };
}
Para describirlo de manera conmovedora,
new A() {}
es una clase anidada sin una instancia adjunta, definida en un contexto estático, pero no es una clase anidada estática, es una clase interna.
¿Estamos todos asignando significados inapropiados a estos términos en el uso diario?
Como un punto de interés relacionado, este documento de especificación histórica define el término de nivel superior como el opuesto de interno :
Las clases que son miembros de clases
static
y las clases que son miembros de paquetes se denominan clases de nivel superior. Se diferencian de las clases internas en que una clase de nivel superior puede hacer uso directo solo de sus propias variables de instancia.
Mientras que en el uso común , se considera que el nivel superior es el opuesto de anidado .
Bueno, ¿no tiene la clase anónima una instancia adjunta en su caso también? Es la referencia que es estática, no la instancia de la clase anónima. Considerar:
class A {
int t() { return 1; }
static A a = new A() { { System.out.println(t()); } };
}
Las distinciones establecidas en la pregunta tienen perfecto sentido desde el punto de vista de la especificación:
una clase interna tiene restricciones aplicadas, que no tienen nada que ver con la cuestión de encerrar instancias (puede que no tenga miembros estáticos, por ejemplo);
el concepto de una clase anidada estática es básicamente sobre espacios de nombres; estas clases pueden denominarse legítimamente de nivel superior , junto con lo que generalmente asumimos como clases de nivel superior.
Da la casualidad de que eliminar la static
de una declaración de clase anidada hace dos cosas separadas a la vez:
- hace que la clase requiera una instancia adjunta;
- hace que la clase interior .
Rara vez pensamos en lo interno como implicando restricciones; solo nos centramos en la cuestión de la instancia adjunta, que es mucho más visible. Sin embargo, desde el punto de vista de la especificación, las restricciones son una preocupación vital.
Lo que nos falta es un término para una clase que requiere una instancia adjunta . No hay tal término definido por el JLS, por lo que hemos (sin saberlo, parece) haber secuestrado un término relacionado, pero en realidad esencialmente diferente, para significar eso.
No hay diferencia entre la clase interna estática y no estática. No entiendo por qué deben considerarse por separado. Echa un vistazo al siguiente código:
public class Outer {
public static class StaticInner{
final Outer parent;
public StaticInner(Outer parent) {
this.parent = parent;
}
};
public class Inner{}
public static void main(String[] args) {
new StaticInner(new Outer());
new Outer().new Inner();
}
}
Y luego en StaticInner
e Inner
classes bytecode:
public class so.Outer$Inner extends java.lang.Object{
final so.Outer this$0;
public so.Outer$Inner(so.Outer);
Code:
0: aload_0
1: aload_1
2: putfield #1; //Field this$0:Lso/Outer;
5: aload_0
6: invokespecial #2; //Method java/lang/Object."<init>":()V
9: return
}
public class so.Outer$StaticInner extends java.lang.Object{
final so.Outer parent;
public so.Outer$StaticInner(so.Outer);
Code:
0: aload_0
1: invokespecial #1; //Method java/lang/Object."<init>":()V
4: aload_0
5: aload_1
6: putfield #2; //Field parent:Lso/Outer;
9: return
}
En realidad no hay diferencia entre ellos en absoluto. Yo diría que la clase interna no estática es solo un azúcar sintáctico . Una forma más corta de escribir una cosa común, no más. La única pequeña diferencia es que en la clase interna no estática, la referencia a la clase adjunta se asigna antes de llamar al constructor principal, esto podría afectar a alguna lógica, pero no creo que sea tan crítico, considerarlos por separado.
PS Una pregunta más sobre un tema relacionado, que podría ser interesante.