¿Por qué no podemos tener un método estático en una clase interna (no estática)?

142

¿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é?

Rahul Garg
fuente
44
Porque ahora Java es ese viejo COBOL :)
Ses
La conclusión es: porque aún no lo han implementado.
intrepidis
1
'Interior no estático' es una tautología.
Marqués de Lorne el
Si no desea exponer su clase interna a los demás y espera que contenga métodos estáticos, puede colocar los modificadores "privados" y "estáticos" en la clase interna.
Willie Z
Una clase interna es, por definición, no estática. Puede tener una clase anidada / miembro estática y una clase anidada / miembro no estática. Este último también se conoce como una clase interna. (Referencia: la sección 8.1.3 del JLS establece "Una clase interna es una clase anidada que no se declara explícita o implícitamente static").
Erwin Bolwidt

Respuestas:

111

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.

Bill el lagarto
fuente
77
Sé que una clase interna está asociada con una instancia de su clase externa y sé que es inútil que podamos declarar miembros estáticos dentro de una clase interna, pero todavía me pregunto por qué una clase interna no puede declarar miembros estáticos.
Kareem
15
En C ++ puede tener, por lo que este es un error en el lenguaje Java.
Antidepresivo industrial
39
La palabra error ... No creo que esa palabra signifique lo que tú piensas que significa.
Seth Nelson el
24
Una frase más apropiada sería "molesto como un mothertrucker". No entiendo por qué Java no permite esto. A veces, quiero que una clase interna use las propiedades de la clase principal, pero mantenga métodos estáticos para un mejor espacio de nombres. ¿Hay algo inherentemente mal con esto? :(
Angad
66
Exactamente. Quiero escribir una clase interna de utilidad. Algunos de sus métodos se beneficiarían del acceso a la clase externa, por lo que no puedo hacerlo estático, pero algunos de sus métodos son solo funciones de utilidad. ¿Por qué no puedo llamar A.B.sync(X)o incluso (desde A) B.sync(x)?
Edward Falk
44

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 Innerno 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.CONSTANTse 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.

Eddie
fuente
66
-1 Tengo que estar en desacuerdo con el ángulo que tomaste aquí. Ciertamente, podemos hacer referencia un tipo de clase interna, por ejemplo 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.
Paul Bellora
2
Sí, las clases internas pueden declarar constantes estáticas . ¡Eso no tiene nada que ver con los métodos estáticos! Si bien puede hacer referencia a un método estático de forma no estática, esto no se recomienda. Todas las herramientas de calidad de código se quejan de ese tipo de referencia y por una buena razón. Y te perdiste mi punto. Nunca dije que no hay forma de hacer referencia a una clase interna estática. Dije que no hay una forma ESTÁTICA de hacer referencia al método estático de una clase interna de una clase externa no estática. Por lo tanto, no hay una forma CORRECTA de referenciarlo.
Eddie
25
"No tiene mucho sentido permitir un método estático en una clase interna no estática; ¿cómo accedería a él?" Llamarías 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 Outerpara 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.
Paul Bellora
1
La diferencia entre Outer.Inner.CONSTANTy Outer.Inner.staticMethod()es que una referencia a una constante no tiene posibilidades de hacer referencia implícita a la instancia Outeren la que Innerse instancia. Todas las referencias para Outer.staticMethod()compartir el mismo estado exacto. Todas las referencias para Outer.Inner.CONSTANTcompartir 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.
Eddie
3
@Eddie No puede hacer referencia a campos de instancia en un método estático, por lo que no hay conflicto relacionado con la imposibilidad de referirse al campo de instancia implícito 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.
Theodore Murdock
20

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 Outerinstancia. 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$0que 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).

gustafc
fuente
77
Pero eso es solo un error normal de tipo "intento de acceder a una variable no estática desde un contexto estático", no es diferente de si un método estático de nivel superior intenta acceder a la variable de instancia de su propia clase.
Lawrence Dol
2
Me gusta esta respuesta porque en realidad explica por qué no es técnicamente posible, aunque parezca posible sintácticamente.
LoPoBo
@gustafc, creo que fue una excelente explicación. Pero como señala Lawrence, es solo un fracaso debido a la referencia a foo, que no es estática. Pero, ¿y si quisiera escribir public static double sinDeg(double theta) { ... }una clase de utilidades matemáticas internas?
Edward Falk
6

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 Innerestá asociada con una instancia de Outer, todavía es posible usar Outer.Inner.myStaticpara referir a un miembro estático de Innersi Java decide hacerlo.

Si necesita compartir algo entre todas las instancias de Inner, simplemente puede ponerlas Outercomo miembros estáticos. Esto no es peor de lo que usas miembros estáticos Inner, donde Outeraún puedes acceder a cualquier miembro privado deInner todos modos (no mejora la encapsulación).

Si necesita compartir algo entre todas las instancias Innercreadas por un outerobjeto, 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.

Don Li
fuente
Las clases internas tampoco son un "must". Sin embargo, como el lenguaje hace dar clases internas, se debe proporcionar una implementación completa y significativa de ellos.
intrepidis
4

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.

Benjamin Curtis Drake
fuente
3

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.

Russell Zahniser
fuente
Creo que una dificultad más fundamental para permitir que las clases internas no estáticas tengan miembros estáticos no constantes es que los programadores que declaran tales miembros podrían tener la intención de vincularlos a instancias de la clase externa, o hacer que sean realmente estáticos. En los casos en que un constructo, si es legal, podría especificarse sensiblemente como que significa cualquiera de dos cosas diferentes, y donde ambas cosas pueden expresarse de otras maneras inequívocas, especificar que el constructo es ilegal a menudo es mejor que especificarlo como que tiene cualquier significado
supercat
3

¿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 classcomo 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:

  1. ¿Significa que solo hay una instancia en VM?
  2. ¿O solo una instancia por objeto externo?

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.

akhil_mittal
fuente
3

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.

  1. 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.

  2. 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.

Condenado93
fuente
2

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.

SER
fuente
Benedikt, ¿qué quieres decir cuando dices "las clases anidadas estáticas son solo un medio para agrupar"?
Ankur
0

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.

malayo
fuente
0

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.

Vishnu Saini
fuente
0

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 
usuario1888955
fuente
-1

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 ...

Creative_Cimmons
fuente
-2

Se le permiten métodos estáticos en clases anidadas estáticas. Por ejemplo

public class Outer {

  public static class Inner {

    public static void method() {

    }
  }
}
mR_fr0g
fuente