¿Por qué no podemos tener un método estático en una clase interna no estática?
Si hago que la clase interna sea estática, funciona. ¿Por qué?
static
").
¿Por qué no podemos tener un método estático en una clase interna no estática?
Si hago que la clase interna sea estática, funciona. ¿Por qué?
static
").
Respuestas:
Debido a que una instancia de una clase interna está implícitamente asociada con una instancia de su clase externa, no puede definir ningún método estático en sí. Dado que una clase anidada estática no puede referirse directamente a variables de instancia o métodos definidos en su clase adjunta, solo puede usarlos a través de una referencia de objeto, es seguro declarar métodos estáticos en una clase anidada estática.
A.B.sync(X)
o incluso (desde A) B.sync(x)
?
No tiene mucho sentido permitir un método estático en una clase interna no estática; ¿Cómo accederías a él? No puede acceder (al menos inicialmente) a una instancia de clase interna no estática sin pasar por una instancia de clase externa. No existe una forma puramente estática de crear una clase interna no estática.
Para una clase externa Outer
, puede acceder a un método estático test()
como este:
Outer.test();
Para una clase interna estática Inner
, puede acceder a su método estático innerTest()
como este:
Outer.Inner.innerTest();
Sin embargo, si Inner
no es estático, ahora no hay una forma puramente estática de hacer referencia al método innertest
. Las clases internas no estáticas están vinculadas a una instancia específica de su clase externa. Una función es diferente de una constante, en el sentido de que Outer.Inner.CONSTANT
se garantiza que una referencia a sea inequívoca de una manera que una llamada de función Outer.Inner.staticFunction();
no lo es. Digamos que tiene Inner.staticFunction()
esas llamadas getState()
, que se define en Outer
. Si intenta invocar esa función estática, ahora tiene una referencia ambigua a la clase Inner. Es decir, ¿en qué instancia de la clase interna invocas la función estática? Importa. Vea, no hay una forma verdaderamente estática de hacer referencia a ese método estático, debido a la referencia implícita al objeto externo.
Paul Bellora tiene razón en que los diseñadores de idiomas podrían haber permitido esto. Luego tendrían que prohibir cuidadosamente cualquier acceso a la referencia implícita a la clase externa en métodos estáticos de la clase interna no estática. En este punto, ¿cuál es el valor de que sea una clase interna si no puede hacer referencia a la clase externa, excepto estáticamente? Y si el acceso estático está bien, ¿por qué no declarar estática toda la clase interna? Si simplemente hace que la clase interna sea estática, entonces no tiene referencia implícita a la clase externa y ya no tiene esta ambigüedad.
Si realmente necesita métodos estáticos en una clase interna no estática, entonces probablemente deba repensar su diseño.
Outer.Inner i = new Outer().new Inner();
también, clases internas están autorizados a declarar estáticas constantes de acuerdo con JLS §15.28.
Outer.Inner.staticMethod()
como si pudieras acceder Outer.Inner.CONSTANT
. "No se puede acceder ... a una instancia de clase interna no estática sin pasar por una instancia de clase externa". ¿Por qué necesitarías una instancia? No necesita una instancia de Outer
para llamar Outer.staticMethod()
. Sé que esto es quisquilloso, pero mi punto es que no tiene sentido enmarcar su respuesta de esta manera. En mi humilde opinión, los diseñadores de idiomas podrían haberlo permitido si lo hubieran deseado.
Outer.Inner.CONSTANT
y Outer.Inner.staticMethod()
es que una referencia a una constante no tiene posibilidades de hacer referencia implícita a la instancia Outer
en la que Inner
se instancia. Todas las referencias para Outer.staticMethod()
compartir el mismo estado exacto. Todas las referencias para Outer.Inner.CONSTANT
compartir el mismo estado exacto. Sin embargo, las referencias a Outer.Inner.staticMethod()
son ambiguas: el estado "estático" no es realmente estático, debido a la referencia implícita a la clase externa en cada instancia de Inner
. No hay una forma realmente inequívoca y estática de acceder a ella.
Outer.this
. Estoy de acuerdo con los diseñadores del lenguaje Java en que no hay razón para permitir métodos estáticos o campos estáticos no finales en las clases internas, porque todo en una clase interna debe estar dentro del contexto de la clase adjunta.
Tengo una teoría, que puede o no ser correcta.
Primero, debe saber algunas cosas sobre cómo se implementan las clases internas en Java. Supongamos que tienes esta clase:
class Outer {
private int foo = 0;
class Inner implements Runnable {
public void run(){ foo++; }
}
public Runnable newFooIncrementer(){ return new Inner(); }
}
Cuando lo compila, el bytecode generado se verá como si hubiera escrito algo como esto:
class Outer {
private int foo = 0;
static class Inner implements Runnable {
private final Outer this$0;
public Inner(Outer outer){
this$0 = outer;
}
public void run(){ this$0.foo++; }
}
public Runnable newFooIncrementer(){ return new Inner(this); }
}
Ahora, si permitimos métodos estáticos en clases internas no estáticas, es posible que desee hacer algo como esto.
class Outer {
private int foo = 0;
class Inner {
public static void incrFoo(){ foo++; }
}
}
... que parece bastante razonable, ya que el Inner
clase parece tener una encarnación por Outer
instancia. Pero como vimos anteriormente, las clases internas no estáticas realmente son simplemente azúcar sintáctica para las clases "internas" estáticas, por lo que el último ejemplo sería aproximadamente equivalente a:
class Outer {
private int foo = 0;
static class Inner {
private final Outer this$0;
public Inner(Outer outer){
this$0 = outer;
}
public static void incrFoo(){ this$0.foo++; }
}
}
... que claramente no funcionará, ya this$0
que no es estático. Este tipo de explica por qué los métodos estáticos no están permitidos (aunque podría argumentar que podría permitir los métodos estáticos siempre que no hicieran referencia al objeto que los encierra) y por qué no puede tener campos estáticos no finales ( sería contra-intuitivo si las instancias de clases internas no estáticas de diferentes objetos compartieran "estado estático"). También explica por qué los campos finales están permitidos (siempre que no hagan referencia al objeto que lo encierra).
public static double sinDeg(double theta) { ... }
una clase de utilidades matemáticas internas?
La única razón es "no es un deber", entonces, ¿por qué molestarse en apoyarlo?
Sintácticamente, no hay razón para prohibir que una clase interna tenga miembros estáticos. Aunque una instancia de Inner
está asociada con una instancia de Outer
, todavía es posible usar Outer.Inner.myStatic
para referir a un miembro estático de Inner
si Java decide hacerlo.
Si necesita compartir algo entre todas las instancias de Inner
, simplemente puede ponerlas Outer
como miembros estáticos. Esto no es peor de lo que usas miembros estáticos Inner
, donde Outer
aún puedes acceder a cualquier miembro privado deInner
todos modos (no mejora la encapsulación).
Si necesita compartir algo entre todas las instancias Inner
creadas por un outer
objeto, tiene más sentido ponerlas enOuter
clase como miembros ordinarios.
No estoy de acuerdo con la opinión de que "una clase anidada estática es prácticamente una clase de nivel superior". Creo que es mejor considerar realmente una clase anidada estática / clase interna como parte de la clase externa, porque pueden acceder a los miembros privados de la clase externa. Y los miembros de la clase externa también son "miembros de la clase interna". Por lo tanto, no es necesario admitir miembros estáticos en la clase interna. Un miembro ordinario / estático en clase externa será suficiente.
De: https://docs.oracle.com/javase/tutorial/java/javaOO/nested.html
Al igual que con los métodos y las variables de instancia, una clase interna está asociada con una instancia de su clase adjunta y tiene acceso directo a los métodos y campos de ese objeto. Además, debido a que una clase interna está asociada con una instancia, no puede definir ningún miembro estático en sí.
La explicación de Oracle es superficial y ondulada. Dado que no hay una razón técnica o sintáctica para evitar miembros estáticos dentro de una clase interna (está permitido en otros lenguajes como C #), la motivación de los diseñadores de Java probablemente fue el gusto conceptual y / o una cuestión de conveniencia técnica.
Aquí está mi especulación:
A diferencia de las clases de nivel superior, las clases internas dependen de la instancia: una instancia de clase interna está asociada con una instancia de cada una de sus clases externas y tiene acceso directo a sus miembros. Esta es la principal motivación para tenerlos en Java. Expresado de otra manera: una clase interna está destinada a la creación de instancias en el contexto de una instancia de clase externa. Sin una instancia de clase externa, una clase interna no debería ser más utilizable que los otros miembros de instancia de la clase externa. Vamos a referirnos a esto como el espíritu dependiente de la instancia. de las clases internas.
La naturaleza misma de los miembros estáticos (que NO están orientados a objetos) choca con el espíritu dependiente de la instancia de las clases internas (que ES orientado a objetos) porque puede hacer referencia / llamar a un miembro estático de una clase interna sin una instancia de clase externa utilizando el nombre calificado de la clase interna.
Las variables estáticas en particular pueden ofender de otra manera: dos instancias de una clase interna que están asociadas con diferentes instancias de la clase externa compartirían variables estáticas. Dado que las variables son un componente del estado, las dos instancias de clase interna compartirían, de hecho, el estado independientemente de las instancias de clase externa con las que están asociadas. No es que sea inaceptable que las variables estáticas funcionen de esta manera (las aceptamos en Java como un compromiso sensato para la pureza de OOP), pero podría decirse que hay una ofensa más profunda al permitirlas en clases internas cuyas instancias ya están acopladas con instancias de clases externas por diseño. Prohibir a los miembros estáticos dentro de las clases internas a favor del espíritu dependiente de la instancia cosecha la ventaja adicional de evitar esta ofensa más profunda de OOP.
Por otro lado, las constantes estáticas no implican tal ofensa, que no constituyen un estado significativo y, por lo tanto, son permisibles. ¿Por qué no prohibir las constantes estáticas para obtener la máxima coherencia con el espíritu dependiente de la instancia ? Quizás porque las constantes no necesitan ocupar más memoria de la necesaria (si se las obliga a no ser estáticas, entonces se copian en cada instancia de clase interna que es potencialmente un desperdicio). De lo contrario, no puedo imaginar el motivo de la excepción.
Puede que no sea un razonamiento sólido como una roca, pero en mi opinión, tiene más sentido el comentario superficial de Oracle sobre el asunto.
Respuesta corta: El modelo mental que tienen la mayoría de los programadores sobre cómo funciona el alcance no es el modelo utilizado por javac. Hacer coincidir el modelo más intuitivo habría requerido un gran cambio en la forma en que funciona javac.
La razón principal por la que los miembros estáticos en las clases internas son deseables es por la limpieza del código: un miembro estático utilizado solo por una clase interna debería vivir dentro de él, en lugar de tener que ser colocado en la clase externa. Considerar:
class Outer {
int outID;
class Inner {
static int nextID;
int id = nextID++;
String getID() {
return outID + ":" + id;
}
}
}
Considere lo que sucede en getID () cuando uso el identificador no calificado "outID". El alcance en el que aparece este identificador se parece a:
Outer -> Inner -> getID()
Aquí, nuevamente porque así es como funciona javac, el nivel "Outer" del alcance incluye miembros estáticos y de instancia de Outer. Esto es confuso porque generalmente se nos dice que pensemos en la parte estática de una clase como otro nivel del alcance:
Outer static -> Outer instance -> instanceMethod()
\----> staticMethod()
En esta forma de pensarlo, por supuesto staticMethod () solo puede ver miembros estáticos de Outer. Pero si así fuera como funciona javac, hacer referencia a una variable de instancia en un método estático daría como resultado un error "no se puede resolver el nombre". Lo que realmente sucede es que el nombre se encuentra en el alcance, pero luego se activa un nivel adicional de verificación y se da cuenta de que el nombre se declaró en un contexto de instancia y se hace referencia a él desde un contexto estático.
OK, ¿cómo se relaciona esto con las clases internas? Ingenuamente, creemos que no hay ninguna razón por la que las clases internas no puedan tener un alcance estático, porque nos estamos imaginando que el alcance funciona así:
Outer static -> Outer instance -> Inner instance -> getID()
\------ Inner static ------^
En otras palabras, las declaraciones estáticas en la clase interna y las declaraciones de instancia en la clase externa están ambas dentro del contexto de la instancia de la clase interna, pero ninguna de estas está anidada en la otra; ambos están anidados en el ámbito estático de Outer.
Simplemente no es así como funciona javac: hay un único nivel de alcance para los miembros estáticos y de instancia, y el alcance siempre anida estrictamente. Incluso la herencia se implementa copiando declaraciones en la subclase en lugar de ramificando y buscando el alcance de la superclase.
Para admitir miembros estáticos de clases internas, javac tendría que dividir los ámbitos estáticos y de instancia y admitir las jerarquías de alcance de ramificación y reagrupación, o tendría que extender su idea booleana de "contexto estático" para cambiar para rastrear el tipo de contexto en todos los niveles de clase anidada en el ámbito actual.
¿Por qué no podemos tener un método estático en una clase interna no estática?
Nota: Una clase anidada no estática se conoce como clase interna, por lo que no tiene non-static inner class
como tal.
Una instancia de clase interna no tiene existencia sin una instancia correspondiente de clase externa. Una clase interna no puede declarar miembros estáticos que no sean constantes de tiempo de compilación. Si se permitiera, habría habido ambigüedad sobre el significado de static
. En ese caso habría habido ciertas confusiones:
Es por eso que los diseñadores probablemente tomaron la decisión de no manejar este problema en absoluto.
Si hago que la clase interna sea estática, funciona. Por qué ?
Una vez más, no puede hacer que una clase interna sea estática, sino que puede declarar una clase estática como anidada. En ese caso, esta clase anidada es en realidad parte de la clase externa y puede tener miembros estáticos sin ningún problema.
Este tema ha atraído la atención de muchos, pero intentaré explicarlo en los términos más simples.
En primer lugar, con referencia a http://docs.oracle.com/javase/specs/jls/se7/html/jls-12.html#jls-12.4.1 , una clase o interfaz se inicializa inmediatamente antes de la primera ocurrencia / invocación de cualquier miembro precedido por la palabra clave estática.
Entonces, si soportamos un miembro estático dentro de una clase interna, conducirá a la inicialización de la clase interna, no necesariamente la clase externa / envolvente. Entonces, obstaculizamos la secuencia de inicialización de la clase.
Considere también el hecho de que una clase interna no estática está asociada con la instancia de una clase externa / cerrada. Por lo tanto, asociarse con una instancia significará que la clase interna existirá dentro de una instancia de clase externa y será diferente entre las instancias.
Simplificando el punto, para acceder al miembro estático necesitamos una instancia de una clase externa, desde la cual nuevamente tendremos que crear una instancia de clase interna no estática. No se supone que los miembros estáticos estén vinculados a instancias y, por lo tanto, recibirá un error de compilación.
Una clase interna es algo completamente diferente de una clase anidada estática, aunque ambas son similares en sintaxis. Las clases anidadas estáticas son solo un medio para agrupar, mientras que las clases internas tienen una fuerte asociación y acceso a todos los valores de su clase externa. Debes estar seguro de por qué quieres usar una clase interna y luego debería ser bastante natural cuál tienes que usar. Si necesita declarar un método estático, probablemente sea una clase anidada estática que desee de todos modos.
supongamos que hay dos instancias de clase externa y ambas tienen una clase interna instanciada. Ahora, si la clase interna tiene un miembro estático, solo mantendrá una copia de ese miembro en el área de almacenamiento dinámico. En este caso, ambos objetos de la clase externa se referirán a esto copia única y pueden alterarlo juntos. Esto puede causar una situación de "Lectura sucia", por lo tanto, para evitar que Java haya aplicado esta restricción. Otro punto fuerte para apoyar este argumento es que Java permite miembros estáticos finales aquí, aquellos cuyos valores no pueden ser cambiado de cualquiera de los objetos de clase externa. Por favor, déjame si estoy equivocado.
En primer lugar, ¿por qué alguien quiere definir el miembro estático en una clase interna no estática? la respuesta es, para que el miembro de la clase externa pueda usar esos miembros estáticos solo con el nombre de la clase interna, ¿verdad?
Pero para este caso podemos definir directamente el miembro en la clase externa. que se asociará con todos los objetos de clase interna, dentro de la instancia de clase externa.
como el siguiente código,
public class Outer {
class Inner {
public static void method() {
}
}
}
se puede escribir así
public class Outer {
void method() {
}
class Inner {
}
}
Entonces, en mi opinión, para no complicar el código, el diseñador Java no permite esta funcionalidad o podemos ver esta funcionalidad en futuras versiones con algunas características más.
Intenta tratar la clase como un campo normal, entonces lo entenderás.
//something must be static. Suppose something is an inner class, then it has static keyword which means it's a static class
Outer.something
Es inútil tener miembros de clase internos como estáticos porque no podrá acceder a ellos en primer lugar.
Piense en esto, para acceder a un miembro estático, use className.memberName ,, en nuestro caso, debería ser algo así como externalclassName.innerclassName.memberName ,,, ahora ve por qué innerclass debe ser estático ...