¿Por qué Java prohíbe los campos estáticos en las clases internas?

85
class OuterClass {
 class InnerClass {
  static int i = 100; // compile error
  static void f() { } // compile error
 }
} 

Aunque no es posible acceder al campo estático con OuterClass.InnerClass.i, si quiero grabar algo que debería ser estático, por ejemplo, el número de objetos InnerClass creados, sería útil hacer que ese campo sea estático. Entonces, ¿ por qué Java prohíbe los campos / métodos estáticos en las clases internas?

EDITAR: Sé cómo hacer feliz al compilador con la clase anidada estática (o la clase interna estática), pero lo que quiero saber es por qué Java prohíbe los campos / métodos estáticos dentro de las clases internas (o la clase interna ordinaria) tanto del diseño del lenguaje como aspectos de implementación, si alguien sabe más al respecto.

Jichao
fuente
3
Mi ejemplo favorito es tener un registrador solo para la clase interna. No puede ser estático como lo son todos los demás registradores.
Piotr Findeisen

Respuestas:

32

La idea detrás de las clases internas es operar en el contexto de la instancia adjunta. De alguna manera, ¿permitir variables y métodos estáticos contradice esta motivación?

8.1.2 Clases internas e instancias adjuntas

Una clase interna es una clase anidada que no se declara estática explícita o implícitamente. Las clases internas no pueden declarar inicializadores estáticos (§8.7) o interfaces miembro. Las clases internas no pueden declarar miembros estáticos, a menos que sean campos constantes en tiempo de compilación (§15.28).

Gregory Pakosz
fuente
18
tal vez se haya decidido así
Gregory Pakosz
3
no puede crear una instancia del interior no estático sin una referencia principal, pero aún puede inicializarlo .
skaffman
Si los ClassLoaders mantienen un caché que dice "La clase X ha sido inicializada", entonces su lógica no puede usarse para inicializar múltiples instancias de Class [objetos que representan] X (que es lo que se necesita cuando los objetos Class deben instanciarse como clases internas dentro de varios objetos distintos).
Erwin Smout
@skaffman Todavía no tiene sentido. Las propiedades estáticas de la clase interna solo se inicializarán una vez, entonces, ¿cuál sería el problema? En este momento, tengo un mapa hash estático y tengo alrededor de 4 métodos que manipulan solo este mapa, lo que lo hace más ideal para agrupar todo en una clase interna. Sin embargo, el mapa hash estático ahora tendría que vivir afuera y posiblemente otras cosas relacionadas, lo cual es simplemente estúpido. ¿Cuál sería el problema de tener propiedades estáticas inicializadas?
mmm
54

lo que quiero saber es por qué Java prohíbe los campos / métodos estáticos dentro de las clases internas

Porque esas clases internas son clases internas de "instancia". Es decir, son como un atributo de instancia del objeto adjunto.

Dado que son clases de "instancia", no tiene sentido permitir staticcaracterísticas, ya que staticestá destinado a funcionar sin una instancia en primer lugar.

Es como si intentaras crear un atributo estático / instancia al mismo tiempo.

Tome el siguiente ejemplo:

class Employee {
    public String name;
}

Si crea dos instancias de empleado:

Employee a = new Employee(); 
a.name = "Oscar";

Employee b = new Employee();
b.name = "jcyang";

Está claro por qué cada uno tiene su propio valor para la propiedad name, ¿verdad?

Lo mismo ocurre con la clase interior; cada instancia de clase interna es independiente de la otra instancia de clase interna.

Entonces, si intenta crear un counteratributo de clase, no hay forma de compartir ese valor entre dos instancias diferentes.

class Employee {
    public String name;
    class InnerData {
        static count; // ??? count of which ? a or b? 
     }
}

Cuando crea la instancia ay ben el ejemplo anterior, ¿cuál sería un valor correcto para la variable estática count? No es posible determinarlo, porque la existencia de la InnerDataclase depende completamente de cada uno de los objetos que la encierran.

Por eso, cuando la clase se declara como static, ya no necesita una instancia viva para vivir ella misma. Ahora que no hay dependencia, puede declarar libremente un atributo estático.

Creo que esto suena reiterativo, pero si piensa en las diferencias entre los atributos de instancia y de clase, tendrá sentido.

OscarRyz
fuente
4
Compraré su explicación de las propiedades estáticas de la clase interna, pero como @skaffman señala en un comentario a mi respuesta, ¿qué pasa con los métodos estáticos ? Parece que los métodos deberían permitirse sin exigir que se separen de ninguna instancia. De hecho, en Java, puede llamar a métodos estáticos en instancias (aunque se considera de mal estilo). Por cierto: le pregunté a un colega para tratar de compilar el código de la OP como C # y que lo hace de compilación. Entonces, C # aparentemente permite esto, lo que demuestra que lo que quiere hacer el OP no viola algún principio fundamental de OO.
Asaph
2
Ocurre exactamente lo mismo con los métodos. El punto aquí no es que los atributos o los métodos sean statis o no, sino el hecho de que la clase interna en sí misma es "materia" de instancia . Quiero decir, el problema aquí es que una instancia de dicha clase interna no existe hasta que se crea la clase externa. Por lo tanto, ¿qué método se enviaría entonces si no hay nada? Golpearía al aire solo porque necesitará una instancia en primer lugar.
OscarRyz
1
Acerca de C # ... bueno. No es válido para OO solo porque C # lo permite, no quiero decir que esté mal, pero C # incluye varios paradigmas para facilitar el desarrollo incluso a costa de la coherencia (hay que aprender cosas nuevas con cada versión de .NET) y permite este y otro tipo de cosas entre otras. Creo que eso es bueno. Si la comunidad cree que una característica adicional es lo suficientemente interesante, C # puede tenerla en el futuro.
OscarRyz
2
@OscarRyz ¿Por qué necesita instancias de la clase interna para usar sus métodos / campos estáticos ? Y un ejemplo de método estático [útil] en la clase interna es el método auxiliar privado.
Leonid Semyonov
1
Con el uso de final, se permiten campos estáticos en la clase interna en java. ¿Cómo explica este escenario?
Number945
34

InnerClassno puede tener staticmiembros porque pertenece a una instancia (de OuterClass). Si se declara InnerClasscomo staticpara separarla de la instancia, el código se compila.

class OuterClass {
    static class InnerClass {
        static int i = 100; // no compile error
        static void f() { } // no compile error
    }
}

Por cierto: aún podrás crear instancias de InnerClass. staticen este contexto permite que eso suceda sin una instancia adjunta de OuterClass.

Asaph
fuente
6
InnerClassno no pertenecen a OuterClass, los casos de que lo hacen. Las dos clases en sí mismas no tienen tal relación. La pregunta de por qué no puede tener métodos estáticos InnerClasssigue en pie.
skaffman
9

En realidad, puede declarar campos estáticos si son constantes y están escritos en tiempo de compilación.

class OuterClass {
    void foo() {
        class Inner{
            static final int a = 5; // fine
            static final String s = "hello"; // fine
            static final Object o = new Object(); // compile error, because cannot be written during compilation
        }
    }
}
vmolchanov
fuente
8
  1. class La secuencia de inicialización es una razón fundamental.

Como las clases internas dependen de la instancia de la clase envolvente / externa, la clase externa debe inicializarse antes de la inicialización de la clase interna.
Esto es lo que dice JLS sobre la inicialización de clases. El punto que necesitamos es que la clase T se inicializará si

  • Se utiliza un campo estático declarado por T y el campo no es una variable constante.

Entonces, si la clase interna tiene acceso a un campo estático, eso provocará la inicialización de la clase interna, pero eso no garantizará que la clase circundante se inicialice.

  1. Violaría algunas reglas básicas . puedes saltar a la última sección (a two cases) para evitar cosas de novatos

Una cosa es que cuando alguna se comportará como una clase normal en todos los sentidos y está asociada con la clase Outer.static nested classnested classstatic

Pero el concepto de Inner class/ es se asociará con el de la clase exterior / envolvente. Tenga en cuenta asociado con la instancia, no la clase. Ahora asociar con instancia significa claramente que ( desde el concepto de variable de instancia ) existirá dentro de una instancia y será diferente entre instancias. non-static nested classinstance

Ahora, cuando hacemos algo estático, esperamos que se inicialice cuando la clase se esté cargando y debería compartirse entre todas las instancias. Pero para ser no estáticos, incluso las clases internas en sí mismas ( definitivamente puede olvidarse de la instancia de la clase interna por ahora ) no se comparten con todas las instancias de la clase externa / envolvente ( al menos conceptualmente ), entonces, ¿cómo podemos esperar que alguna variable de la clase interna se compartirá entre todas las instancias de la clase interna.

Entonces, si Java nos permite usar una variable estática dentro de una clase anidada no estática. habrá dos casos .

  • Si se comparte con todas las instancias de la clase interna, violará el concepto de context of instance(variable de instancia). Entonces es un NO.
  • Si no se comparte con todas las instancias, violará el concepto de ser estático. De nuevo NO.
Saif
fuente
5

Aquí está la motivación que encuentro más adecuada para este "límite": puede implementar el comportamiento de un campo estático de una clase interna como un campo de instancia del objeto externo; Por lo tanto, no necesita campos / métodos estáticos . El comportamiento al que me refiero es que todas las instancias de clase interna de algún objeto comparten un campo (o método).

Entonces, suponga que quisiera contar todas las instancias de clases internas, haría:

public class Outer{
    int nofInner; //this will count the inner class 
                  //instances of this (Outer)object
                  //(you know, they "belong" to an object)
    static int totalNofInner; //this will count all 
                              //inner class instances of all Outer objects
    class Inner {
        public Inner(){
            nofInner++;
            totalNofInner++;
        }
    }
}
ianos
fuente
2
Pero la pregunta es cuál es la razón para permitir campos estáticos cuando se declaran finalentonces.
Solace
si miras [ stackoverflow.com/a/1954119/1532220] (respuesta de OscarRyzs arriba): Su motivación es que un valor no se puede asociar a la variable. Por supuesto, si la variable es final, puede saber fácilmente qué valor asignar (debe saberlo).
ianos
2

En palabras simples, las clases internas no estáticas son variables de instancia para la clase externa, y se crean solo cuando se crea una clase externa y se crea un objeto de clase externa en tiempo de ejecución, mientras que las variables estáticas se crean en el momento de carga de la clase. Entonces, la clase interna no estática es algo en tiempo de ejecución, por eso la estática no es parte de una clase interna no estática.

NOTA: trate las clases internas siempre como una variable para una clase externa, pueden ser estáticas o no estáticas como cualquier otra variable.

Mannu
fuente
Pero la clase interna puede tener static finalconstantes.
Lloviendo
1

Porque causaría ambigüedad en el significado de "estático".

Las clases internas no pueden declarar miembros estáticos que no sean constantes en tiempo de compilación. Habría una ambigüedad sobre el significado de "estático". ¿Significa que solo hay una instancia en la máquina virtual? ¿O solo una instancia por objeto externo? Los diseñadores de lenguajes decidieron no abordar este problema.

Tomado de "Core Java SE 9 for the Impatient" de Cay S. Horstmann. Pg 90 Capítulo 2.6.3

aj hrishikesh
fuente
-1

Supongo que es por coherencia. Si bien no parece haber ninguna limitación técnica, no podrá acceder a los miembros estáticos de la clase interna desde el exterior, es decir, OuterClass.InnerClass.iporque el paso intermedio no es estático.

Chochos
fuente
Pero la clase interna puede tener static finalconstantes.
Lloviendo