Pourquoi ne pouvons-nous pas avoir de méthode statique dans une classe interne non statique?
Si je rends la classe interne statique, cela fonctionne. Pourquoi?
static
.")
Pourquoi ne pouvons-nous pas avoir de méthode statique dans une classe interne non statique?
Si je rends la classe interne statique, cela fonctionne. Pourquoi?
static
.")
Réponses:
Étant donné qu'une instance d'une classe interne est implicitement associée à une instance de sa classe externe, elle ne peut définir aucune méthode statique elle-même. Puisqu'une classe imbriquée statique ne peut pas faire référence directement aux variables d'instance ou aux méthodes définies dans sa classe englobante, elle ne peut les utiliser que via une référence d'objet, il est sûr de déclarer des méthodes statiques dans une classe imbriquée statique.
A.B.sync(X)
ou même (depuis A) B.sync(x)
?
Il n'y a pas grand chose à autoriser une méthode statique dans une classe interne non statique; comment y accéderiez-vous? Vous ne pouvez pas accéder (au moins initialement) à une instance de classe interne non statique sans passer par une instance de classe externe. Il n'existe pas de moyen purement statique de créer une classe interne non statique.
Pour une classe externe Outer
, vous pouvez accéder à une méthode statique test()
comme celle-ci:
Outer.test();
Pour une classe interne statique Inner
, vous pouvez accéder à sa méthode statique innerTest()
comme ceci:
Outer.Inner.innerTest();
Cependant, si ce Inner
n'est pas statique, il n'existe plus de moyen purement statique de référencer la méthode innertest
. Les classes internes non statiques sont liées à une instance spécifique de leur classe externe. Une fonction est différente d'une constante, en ce qu'une référence à Outer.Inner.CONSTANT
est garantie sans ambiguïté, contrairement à un appel de fonction Outer.Inner.staticFunction();
. Disons que vous avezInner.staticFunction()
ces appels getState()
, qui sont définis dans Outer
. Si vous essayez d'appeler cette fonction statique, vous disposez désormais d'une référence ambiguë à la classe Inner. Autrement dit, sur quelle instance de la classe interne invoquez-vous la fonction statique? Cela compte. Voir, il n'y a pas de moyen vraiment statique de référencer cette méthode statique, en raison de la référence implicite à l'objet externe.
Paul Bellora a raison de dire que les concepteurs de langage auraient pu permettre cela. Ils devraient alors interdire soigneusement tout accès à la référence implicite à la classe externe dans les méthodes statiques de la classe interne non statique. À ce stade, quelle est la valeur de cette classe interne si vous ne pouvez pas référencer la classe externe, sauf statiquement? Et si l'accès statique est correct, alors pourquoi ne pas déclarer toute la classe interne statique? Si vous rendez simplement la classe interne elle-même statique, vous n'avez aucune référence implicite à la classe externe et vous n'avez plus cette ambiguïté.
Si vous avez réellement besoin de méthodes statiques sur une classe interne non statique, vous devez probablement repenser votre conception.
Outer.Inner i = new Outer().new Inner();
De plus, les classes internes sont autorisées à déclarer des constantes statiques selon JLS §15.28.
Outer.Inner.staticMethod()
comme vous pouvez y accéder Outer.Inner.CONSTANT
. "Vous ne pouvez pas accéder ... à une instance de classe interne non statique sans passer par une instance de classe externe." Pourquoi auriez-vous besoin d'une instance? Vous n'avez pas besoin d'une instance de Outer
pour appeler Outer.staticMethod()
. Je sais que c'est pinaillant, mais ce que je veux dire, c'est qu'il n'a aucun sens de formuler votre réponse de cette façon. À mon humble avis, les concepteurs de langage auraient pu le permettre s'ils le souhaitaient.
Outer.Inner.CONSTANT
et Outer.Inner.staticMethod()
est qu'une référence à une constante n'a aucune chance de référencer implicitement l'instance de Outer
dans laquelle a Inner
été instanciée. Toutes les références Outer.staticMethod()
partagent le même état exact. Toutes les références Outer.Inner.CONSTANT
partagent le même état exact. Cependant, les références à Outer.Inner.staticMethod()
sont ambiguës: l'état "statique" n'est pas vraiment statique, en raison de la référence implicite à la classe externe dans chaque instance de Inner
. Il n'existe pas de moyen statique et sans ambiguïté d'y accéder.
Outer.this
. Je suis d'accord avec les concepteurs de langage Java pour dire qu'il n'y a aucune raison d'autoriser les méthodes statiques ou les champs statiques non finaux dans les classes internes, car tout dans une classe interne doit être dans le contexte de la classe englobante.
J'ai une théorie, qui peut être correcte ou non.
Tout d'abord, vous devez savoir comment les classes internes sont implémentées en Java. Supposons que vous ayez cette classe:
class Outer {
private int foo = 0;
class Inner implements Runnable {
public void run(){ foo++; }
}
public Runnable newFooIncrementer(){ return new Inner(); }
}
Lorsque vous le compilez, le bytecode généré ressemblera à si vous avez écrit quelque chose comme ceci:
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); }
}
Maintenant, si nous autorisons les méthodes statiques dans les classes internes non statiques, vous voudrez peut-être faire quelque chose comme ça.
class Outer {
private int foo = 0;
class Inner {
public static void incrFoo(){ foo++; }
}
}
... ce qui semble assez raisonnable, car la Inner
classe semble avoir une incarnation par Outer
instance. Mais comme nous l'avons vu ci-dessus, les classes internes non statiques ne sont en réalité que du sucre syntaxique pour les classes «internes» statiques, donc le dernier exemple serait approximativement équivalent à:
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++; }
}
}
... ce qui ne fonctionnera clairement pas, car il this$0
n'est pas statique. Cela explique en quelque sorte pourquoi les méthodes statiques ne sont pas autorisées (bien que vous puissiez faire valoir que vous pouvez autoriser les méthodes statiques tant qu'elles ne font pas référence à l'objet englobant), et pourquoi vous ne pouvez pas avoir de champs statiques non finaux ( ce serait contre-intuitif si des instances de classes internes non statiques provenant de différents objets partageaient un «état statique»). Il explique également pourquoi les champs finaux sont autorisés (tant qu'ils ne font pas référence à l'objet englobant).
public static double sinDeg(double theta) { ... }
une classe d'utilitaires mathématiques internes?
La seule raison est "pas un must", alors pourquoi se donner la peine de le soutenir?
Du point de vue syntaxique, il n'y a aucune raison d'interdire à une classe interne d'avoir des membres statiques. Bien qu'une instance de Inner
soit associée à une instance de Outer
, il est toujours possible de l'utiliser Outer.Inner.myStatic
pour faire référence à un membre statique de Inner
si java décide de le faire.
Si vous avez besoin de partager quelque chose entre toutes les instances de Inner
, vous pouvez simplement les placer en Outer
tant que membres statiques. Ce n'est pas pire que d'utiliser des membres statiques dans Inner
, où Outer
peut toujours accéder à n'importe quel membre privé de Inner
toute façon (n'améliore pas l'encapsulation).
Si vous avez besoin de partager quelque chose parmi toutes les instances de Inner
créé par un outer
objet, il est plus logique de les mettre en Outer
classe en tant que membres ordinaires.
Je ne suis pas d'accord avec l'opinion qu'une "classe imbriquée statique est à peu près juste une classe de premier niveau". Je pense qu'il est préférable de vraiment considérer une classe imbriquée statique / classe interne comme une partie de la classe externe, car ils peuvent accéder aux membres privés de la classe externe. Et les membres de la classe externe sont également des «membres de la classe interne». Il n'est donc pas nécessaire de prendre en charge le membre statique dans la classe interne. Un membre ordinaire / statique dans la classe externe suffira.
De: https://docs.oracle.com/javase/tutorial/java/javaOO/nested.html
Comme avec les méthodes et les variables d'instance, une classe interne est associée à une instance de sa classe englobante et a un accès direct aux méthodes et champs de cet objet. De plus, comme une classe interne est associée à une instance, elle ne peut définir elle-même aucun membre statique.
L'explication d'Oracle est superficielle et vague. Puisqu'il n'y a aucune raison technique ou syntaxique de préempter les membres statiques au sein d'une classe interne (c'est autorisé dans d'autres langages tels que C #), la motivation des concepteurs Java était probablement un goût conceptuel et / ou une question de commodité technique.
Voici ma spéculation:
Contrairement aux classes de niveau supérieur, les classes internes dépendent de l'instance: une instance de classe interne est associée à une instance de chacune de ses classes externes et a un accès direct à leurs membres. C'est la principale motivation pour les avoir en Java. Exprimé d'une autre manière: une classe interne est destinée à l' instanciation dans le contexte d'une instance de classe externe. Sans une instance de classe externe, une classe interne ne devrait pas être plus utilisable que les autres membres d'instance de la classe externe. Faisons référence à cela comme l' esprit dépendant de l' instance des classes internes.
La nature même des membres statiques (qui ne sont PAS orientés objet) entre en conflit avec les éléments dépendants de l' instance esprit des classes internes (qui EST orienté objet) car vous pouvez référencer / appeler un membre statique d'une classe interne sans instance de classe externe en en utilisant le nom de classe interne qualifié.
Les variables statiques en particulier peuvent offenser d'une autre manière encore: deux instances d'une classe interne qui sont associées à différentes instances de la classe externe partageraient des variables statiques. Étant donné que les variables sont un composant d'état, les deux instances de classe interne partageraient en fait l'état indépendamment des instances de classe externes auxquelles elles sont associées. Ce n'est pas qu'il soit inacceptable que les variables statiques fonctionnent de cette manière (nous les acceptons en Java comme un compromis raisonnable à la pureté de la POO), mais il y a sans doute une infraction plus profonde à avoir en les autorisant dans des classes internes dont les instances sont déjà couplées avec des instances de classe externes intentionnellement. Interdire les membres statiques au sein des classes internes en faveur de l' esprit dépendant de l' instance récolte l'avantage supplémentaire de prévenir cette attaque plus profonde de la POO.
D'un autre côté, aucune infraction de ce type n'est impliquée par des constantes statiques, qui ne constituent pas véritablement un état et sont donc autorisées. Pourquoi ne pas interdire les constantes statiques pour une cohérence maximale avec l' esprit dépendant de l' instance ? Peut-être parce que les constantes n'ont pas besoin de prendre plus de mémoire que nécessaire (si elles sont forcées d'être non statiques, elles sont copiées dans chaque instance de classe interne, ce qui est potentiellement inutile). Sinon, je ne peux pas imaginer la raison de l'exception.
Ce n'est peut-être pas un raisonnement solide comme le roc, mais l'OMI, c'est ce qui a le plus de sens de la remarque superficielle d'Oracle sur la question.
Réponse courte: Le modèle mental que la plupart des programmeurs ont du fonctionnement de la portée n'est pas le modèle utilisé par javac. Faire correspondre le modèle le plus intuitif aurait nécessité un grand changement dans le fonctionnement de javac.
La principale raison pour laquelle les membres statiques dans les classes internes sont souhaitables est pour la propreté du code - un membre statique utilisé uniquement par une classe interne devrait y vivre, plutôt que d'avoir à être placé dans la classe externe. Considérer:
class Outer {
int outID;
class Inner {
static int nextID;
int id = nextID++;
String getID() {
return outID + ":" + id;
}
}
}
Considérez ce qui se passe dans getID () lorsque j'utilise l'identifiant non qualifié "outID". La portée dans laquelle cet identifiant apparaît ressemble à quelque chose comme:
Outer -> Inner -> getID()
Ici encore, parce que c'est ainsi que fonctionne javac, le niveau "Outer" de la portée inclut à la fois les membres statiques et d'instance d'Outer. Ceci est déroutant car on nous dit généralement de penser à la partie statique d'une classe comme un autre niveau de la portée:
Outer static -> Outer instance -> instanceMethod()
\----> staticMethod()
Dans cette façon de penser, bien sûr, staticMethod () ne peut voir que les membres statiques d'Outer. Mais si c'était ainsi que fonctionne javac, alors référencer une variable d'instance dans une méthode statique entraînerait une erreur "le nom ne peut pas être résolu". Ce qui se passe vraiment, c'est que le nom se trouve dans la portée, mais ensuite un niveau supplémentaire de vérification entre en jeu et comprend que le nom a été déclaré dans un contexte d'instance et qu'il est référencé à partir d'un contexte statique.
OK, comment cela se rapporte-t-il aux classes internes? Naïvement, nous pensons qu'il n'y a aucune raison pour laquelle les classes internes ne peuvent pas avoir une portée statique, car nous imaginons que la portée fonctionne comme ceci:
Outer static -> Outer instance -> Inner instance -> getID()
\------ Inner static ------^
En d'autres termes, les déclarations statiques dans la classe interne et les déclarations d'instance dans la classe externe sont toutes deux dans la portée du contexte d'instance de la classe interne, mais aucune d'elles n'est en fait imbriquée dans l'autre; les deux sont plutôt imbriqués dans la portée statique d'Outer.
Ce n'est tout simplement pas comme cela que javac fonctionne - il existe un seul niveau de portée pour les membres statiques et d'instance, et la portée est toujours strictement imbriquée. Même l'héritage est implémenté en copiant les déclarations dans la sous-classe plutôt qu'en créant des branches et en recherchant la portée de la superclasse.
Pour prendre en charge les membres statiques des classes internes, javac devrait soit diviser les étendues statiques et d'instance et prendre en charge les hiérarchies d' étendues de branchement et de rejoindre, ou il devrait étendre son idée de «contexte statique» booléen simple pour changer pour suivre le type de contexte à tous les niveaux de classe imbriquée dans la portée actuelle.
Pourquoi ne pouvons-nous pas avoir de méthode statique dans une classe interne non statique?
Remarque: Une classe imbriquée non statique est connue sous le nom de classe interne, vous ne l'avez donc pas en non-static inner class
tant que telle.
Une instance de classe interne n'a pas d'existence sans une instance correspondante de classe externe. Une classe interne ne peut pas déclarer de membres statiques autres que des constantes de temps de compilation. Si cela avait été autorisé, il y aurait eu une ambiguïté sur la signification de static
. Dans ce cas, il y aurait eu certaines confusions:
C'est pourquoi les concepteurs ont probablement pris la décision de ne pas traiter du tout ce problème.
Si je rends la classe interne statique, cela fonctionne. Pourquoi ?
Encore une fois, vous ne pouvez pas rendre statique une classe interne, mais plutôt déclarer une classe statique comme imbriquée. Dans ce cas, cette classe imbriquée fait en fait partie de la classe externe et peut avoir des membres statiques sans aucun problème.
Ce sujet a attiré l'attention de beaucoup, je vais encore essayer de l'expliquer dans les termes les plus simples.
Premièrement, en référence à http://docs.oracle.com/javase/specs/jls/se7/html/jls-12.html#jls-12.4.1 , une classe ou une interface est initialisée immédiatement avant la première occurrence / invocation de tout membre précédé du mot-clé static.
Donc, si nous acceptons un membre statique dans une classe interne, cela conduira à l'initialisation de la classe interne, pas nécessairement de la classe externe / englobante. Donc, nous gênons la séquence d'initialisation de classe.
Considérez également le fait qu'une classe interne non statique est associée à l'instance d'une classe englobante / externe. Ainsi, l'association à une instance signifie que la classe interne existera dans une instance de classe externe et sera différente d'une instance à l'autre.
Pour simplifier le point, pour accéder au membre statique, nous avons besoin d'une instance d'une classe Outer, à partir de laquelle nous devrons à nouveau créer une instance de classe interne non statique. Les membres statiques ne sont pas censés être liés à des instances et par conséquent, vous recevez une erreur de compilation.
Une classe interne est quelque chose de complètement différent d'une classe imbriquée statique bien que les deux soient similaires dans la syntaxe. Les classes imbriquées statiques ne sont qu'un moyen de regrouper tandis que les classes internes ont une forte association - et ont accès à toutes les valeurs de - leur classe externe. Vous devez être sûr de la raison pour laquelle vous souhaitez utiliser une classe interne, puis il devrait être assez naturel de savoir laquelle vous devez utiliser. Si vous avez besoin de déclarer une méthode statique, c'est probablement une classe imbriquée statique que vous voulez de toute façon.
Supposons qu'il y ait deux instances de classe externe et qu'elles aient toutes les deux une classe interne instanciée.Maintenant, si la classe interne a un membre statique, elle ne conservera qu'une seule copie de ce membre dans la zone de tas.Dans ce cas, les deux objets de la classe externe feront référence à cela copie unique et ils peuvent le modifier ensemble.Cela peut provoquer une situation de «lecture sale» et donc empêcher que Java ait appliqué cette restriction.Un autre point fort pour soutenir cet argument est que java autorise les membres statiques finaux ici, ceux dont les valeurs ne peuvent pas être changé de l'un ou l'autre des objets de classe externe. S'il vous plaît laissez-moi si je me trompe.
Tout d'abord, pourquoi quelqu'un veut définir le membre statique dans une classe interne non statique? La réponse est, de sorte que le membre de classe externe puisse utiliser ces membres statiques avec le nom de classe interne uniquement, n'est-ce pas?
Mais pour ce cas, nous pouvons définir directement le membre dans la classe externe. qui sera associé à tous les objets de la classe interne, dans l'instance de la classe externe.
comme ci-dessous le code,
public class Outer {
class Inner {
public static void method() {
}
}
}
peut être écrit comme ça
public class Outer {
void method() {
}
class Inner {
}
}
Donc, à mon avis, ne pas compliquer le code Java Designer ne permet pas cette fonctionnalité ou nous pourrions voir cette fonctionnalité dans les versions futures avec quelques fonctionnalités supplémentaires.
Essayez de traiter la classe comme un champ normal, alors vous comprendrez.
//something must be static. Suppose something is an inner class, then it has static keyword which means it's a static class
Outer.something
Il est inutile d'avoir des membres de classe internes comme statiques car vous ne pourrez pas y accéder en premier lieu.
Pensez à cela, pour accéder à un membre statique, vous utilisez className.memberName ,, dans notre cas, cela devrait être quelque chose comme externalclassName.innerclassName.memberName ,,, maintenant voyez-vous pourquoi innerclass doit être statique ...