Pourquoi Java interdit-il les champs statiques dans les classes internes?

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

Bien qu'il ne soit pas possible d'accéder au champ statique avec OuterClass.InnerClass.i, si je veux enregistrer quelque chose qui devrait être statique, par exemple le nombre d'objets InnerClass créés, il serait utile de rendre ce champ statique. Alors pourquoi Java interdit-il les champs / méthodes statiques dans les classes internes?

EDIT: Je sais comment rendre le compilateur heureux avec une classe imbriquée statique (ou une classe interne statique), mais ce que je veux savoir, c'est pourquoi java interdit les champs / méthodes statiques à l'intérieur des classes internes (ou classe interne ordinaire) à la fois de la conception du langage et aspects de mise en œuvre, si quelqu'un en sait plus à ce sujet.

Jichao
la source
3
Mon exemple préféré est d'avoir un enregistreur juste pour la classe interne. Il ne peut pas être statique comme tous les autres enregistreurs.
Piotr Findeisen

Réponses:

32

L'idée derrière les classes internes est d'opérer dans le contexte de l'instance englobante. D'une manière ou d'une autre, autoriser des variables et des méthodes statiques contredit cette motivation?

8.1.2 Classes internes et instances englobantes

Une classe interne est une classe imbriquée qui n'est pas explicitement ou implicitement déclarée statique. Les classes internes ne peuvent pas déclarer d'initialiseurs statiques (§8.7) ou d'interfaces membres. Les classes internes ne peuvent pas déclarer de membres statiques, à moins qu'il ne s'agisse de champs constants au moment de la compilation (§15.28).

Gregory Pakosz
la source
18
peut-être que c'est juste décidé comme ça
Gregory Pakosz
3
vous ne pouvez pas instancier le composant interne non statique sans référence parent, mais vous pouvez toujours l' initialiser .
skaffman
Si les ClassLoaders conservent un cache indiquant «La classe X a été initialisée», alors leur logique ne peut pas être utilisée pour initialiser plusieurs instances de la classe [objets représentant] X (ce qui est nécessaire lorsque les objets de classe doivent être instanciés en tant que classes internes à l'intérieur de plusieurs objets distincts).
Erwin Smout
@skaffman Cela n'a toujours pas de sens. Les propriétés statiques de la classe interne ne seront initialisées qu'une seule fois, alors quel serait le problème? Pour le moment, j'ai un hashmap statique et j'ai environ 4 méthodes qui manipulent uniquement cette carte, ce qui rend plus idéal pour tout regrouper dans une classe interne. Cependant, le hashmap statique devrait maintenant vivre à l'extérieur, et peut-être d'autres choses liées, ce qui est tout simplement stupide. Quel serait le problème avec l'initialisation des propriétés statiques?
mmm
54

ce que je veux savoir, c'est pourquoi java interdit les champs / méthodes statiques à l'intérieur des classes internes

Parce que ces classes internes sont des classes internes "d'instance". Autrement dit, ils sont comme un attribut d'instance de l'objet englobant.

Puisqu'il s'agit de classes "d'instance", cela n'a aucun sens d'autoriser les staticfonctionnalités, car cela staticest censé fonctionner sans instance en premier lieu.

C'est comme si vous essayiez de créer un attribut statique / instance en même temps.

Prenons l'exemple suivant:

class Employee {
    public String name;
}

Si vous créez deux instances d'employé:

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

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

Il est clair pourquoi chacun a sa propre valeur pour la propriété name, non?

La même chose se produit avec la classe interne; chaque instance de classe interne est indépendante de l'autre instance de classe interne.

Donc, si vous essayez de créer un counterattribut de classe, il n'y a aucun moyen de partager cette valeur entre deux instances différentes.

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

Lorsque vous créez l'instance aet bdans l'exemple ci-dessus, quelle serait une valeur correcte pour la variable statique count? Il n'est pas possible de le déterminer, car l'existence duInnerData classe dépend entièrement de chacun des objets englobants.

C'est pourquoi, lorsque la classe est déclarée comme static, elle n'a plus besoin d'une instance vivante pour se vivre. Maintenant qu'il n'y a pas de dépendance, vous pouvez déclarer librement un attribut statique.

Je pense que cela semble répétitif, mais si vous pensez aux différences entre les attributs d'instance et de classe, cela aura du sens.

OscarRyz
la source
4
J'achèterai votre explication pour les propriétés statiques de la classe interne, mais comme @skaffman le souligne dans un commentaire à ma réponse, qu'en est-il des méthodes statiques ? Il semble que les méthodes devraient être autorisées sans exiger qu'elles soient détachées de toute instance. En effet, en java, vous pouvez appeler des méthodes statiques sur des instances (bien que cela soit considéré comme un mauvais style). BTW: J'ai demandé à un collègue de tenter de compiler le code de l'OP en C # et il ne compile. Donc C # permet apparemment cela, ce qui démontre que ce que l'OP veut faire ne viole pas un principe OO fondamental.
Asaph
2
Cela se passe exactement de la même manière avec les méthodes. Le point ici n'est pas que les attributs ou les méthodes soient statis ou non, mais le fait que la classe interne elle-même soit une instance "stuff". Je veux dire, le problème ici est qu'une instance d'une telle classe interne n'existe pas tant que la classe externe n'est pas créée. D'où la méthode qui serait alors expédiée s'il n'y a rien. Vous n'atteindriez l'air que parce que vous aurez besoin d'une instance en premier lieu.
OscarRyz
1
À propos de C # ... eh bien. Ce n'est pas OO valide simplement parce que C # le permet, je ne veux pas dire que c'est faux, mais C # inclut plusieurs paradigmes pour rendre le développement plus facile même au détriment de la cohérence (vous devez apprendre de nouvelles choses avec chaque version .NET) et cela permet cela et d'autres types de choses entre autres. Je pense que c'est une bonne chose. Si la communauté pense qu'une fonctionnalité supplémentaire est assez cool, C # peut l'avoir à l'avenir.
OscarRyz
2
@OscarRyz Pourquoi avez-vous besoin d'instances de la classe interne pour utiliser ses méthodes / champs statiques ? Et un exemple de méthode statique [utile] dans la classe interne est la méthode d'assistance privée.
Leonid Semyonov
1
Avec l'utilisation de final, les champs statiques sont autorisés dans la classe interne de java. Comment expliquez-vous ce scénario?
Numéro945
34

InnerClassne peut pas avoir de staticmembres car il appartient à une instance (de OuterClass). Si vous déclarez InnerClassle staticdétacher de l'instance, votre code se compilera.

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

BTW: Vous pourrez toujours créer des instances de InnerClass. staticdans ce contexte, permet que cela se produise sans une instance englobante de OuterClass.

Asaph
la source
6
InnerClassn'appartient pas à OuterClass, les instances en font. Les deux classes elles-mêmes n'ont pas une telle relation. La question de savoir pourquoi vous ne pouvez pas avoir de méthodes statiques est InnerClasstoujours d'actualité.
skaffman
9

En fait, vous pouvez déclarer des champs statiques s'ils sont des constantes et sont écrits au moment de la compilation.

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
la source
8
  1. La séquence d'initialisation de classe est une raison critique.

Comme les classes internes dépendent de l'instance de la classe englobante / externe, la classe externe doit être initialisée avant l'initialisation de la classe interne.
C'est JLS dit à propos de l'initialisation de classe. Le point dont nous avons besoin est que la classe T sera initialisée si

  • Un champ statique déclaré par T est utilisé et le champ n'est pas une variable constante.

Donc, si la classe interne a un accès à un champ statique, cela entraînera l'initialisation de la classe interne, mais cela ne garantira pas que la classe englobante est initialisée.

  1. Cela violerait certaines règles de base . vous pouvez passer à la dernière section (à two cases) pour éviter les trucs noob

Une chose à propos , quand certains sontstatic nested classnested classstatic elle se comportera comme une classe normale à tous égards et elle est associée à la classe Outer.

Mais le concept de Inner class/ est-il sera associé à la classe externe / englobante. Veuillez noter associé à l' instance et non à la classe. L'association à une instance signifie clairement que (d' après le concept de variable d'instance ), elle existera à l'intérieur d'une instance et sera différente d'une instance à l'autre. non-static nested classinstance

Maintenant, lorsque nous créons quelque chose de statique, nous nous attendons à ce qu'il soit initialisé lorsque la classe est en cours de chargement et devrait être partagé entre toutes les instances. Mais pour être non statiques, même les classes internes elles-mêmes ( vous pouvez certainement oublier l'instance de la classe interne pour le moment ) ne sont pas partagées avec toutes les instances de la classe externe / englobante ( du moins conceptuellement ), alors comment pouvons-nous nous attendre à ce qu'une variable de la classe interne sera partagée entre toutes les instances de la classe interne.

Donc, si Java nous permet d'utiliser une variable statique dans une classe imbriquée non statique. il y aura deux cas .

  • S'il est partagé avec toute l'instance de la classe interne, il violera le concept de context of instance(variable d'instance). C'est un NON alors.
  • S'il n'est pas partagé avec toutes les instances, il violera le concept d'être statique. Encore une fois NON.
Saif
la source
5

Voici la motivation que je trouve la mieux adaptée à cette "limite": Vous pouvez implémenter le comportement d'un champ statique d'une classe interne comme un champ d'instance de l'objet externe; Vous n'avez donc pas besoin de champs / méthodes statiques . Le comportement que je veux dire est que toutes les instances de classe interne d'un objet partagent un champ (ou une méthode).

Donc, supposons que vous vouliez compter toutes les instances de classe interne, vous feriez:

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
la source
2
Mais la question est de savoir quelle est la raison pour laquelle autoriser les champs statiques lorsqu'ils sont déclarés finalalors?
Solace
si vous regardez [ stackoverflow.com/a/1954119/1532220] (réponse OscarRyzs ci-dessus): Sa motivation est qu'une valeur ne peut pas être associée à la variable. Bien sûr, si la variable est définitive, vous pouvez assez facilement savoir quelle valeur attribuer (il faut savoir).
ianos
2

En termes simples, les classes internes non statiques sont des variables d'instance pour la classe externe et elles ne sont créées que lorsqu'une classe externe est créée et qu'un objet de classe externe est créé au moment de l'exécution, tandis que les variables statiques sont créées au moment du chargement de la classe. Donc, la classe interne non statique est une chose à l'exécution, c'est pourquoi static n'est pas la partie d'une classe interne non statique.

REMARQUE: traitez toujours les classes internes comme une variable pour une classe externe, elles peuvent être statiques ou non statiques comme toutes les autres variables.

Mannu
la source
Mais la classe interne peut avoir des static finalconstantes.
pleut du
1

Parce que cela créerait une ambiguïté dans le sens de «statique».

Les classes internes ne peuvent pas déclarer de membres statiques autres que les constantes de compilation. Il y aurait une ambiguïté sur la signification de «statique». Cela signifie-t-il qu'il n'y a qu'une seule instance dans la machine virtuelle? Ou une seule instance par objet extérieur? Les concepteurs de langage ont décidé de ne pas s'attaquer à ce problème.

Tiré de "Core Java SE 9 for the Impatient" par Cay S. Horstmann. Pg 90 Chapitre 2.6.3

aj hrishikesh
la source
-1

Je suppose que c'est pour la cohérence. Bien qu'il ne semble pas y avoir de limitation technique, vous ne pourrez pas accéder aux membres statiques de la classe interne de l'extérieur, c'est OuterClass.InnerClass.i-à- dire parce que l'étape du milieu n'est pas statique.

Chochos
la source
Mais la classe interne peut avoir des static finalconstantes.
pleut du