Pourquoi ne pouvons-nous pas avoir de méthode statique dans une classe interne (non statique)?


142

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?


4
Parce que maintenant Java est ce vieux COBOL :)
ses

Le résultat est: parce qu'ils ne l'ont pas encore mis en œuvre.
intrepidis

1
«Intérieur non statique» est une tautologie.
Marquis of Lorne

Si vous ne voulez pas exposer votre classe interne aux autres et espérez qu'elle contienne des méthodes statiques, vous pouvez placer les modificateurs à la fois «privé» et «statique» sur la classe interne.
Willie Z

Une classe interne n'est par définition pas statique. Vous pouvez avoir une classe imbriquée / membre statique et une classe imbriquée / membre non statique. Ce dernier est également connu comme une classe interne. (Référence: la section 8.1.3 des états JLS "Une classe interne est une classe imbriquée qui n'est pas explicitement ou implicitement déclarée static.")
Erwin Bolwidt

Réponses:


111

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


7
Je sais qu'une classe interne est associée à une instance de sa classe externe et je sais qu'il est un peu inutile que nous devenions capables de déclarer des membres statiques dans une classe interne, mais je me demande toujours pourquoi une classe interne ne peut pas déclarer des membres statiques?
Kareem le

15
En C ++, vous pouvez avoir, donc c'est un bogue dans le langage Java.
Antidépresseur industriel

39
Le mot bug ... Je ne pense pas que ce mot signifie ce que vous pensez qu'il signifie.
Seth Nelson

24
Une phrase plus appropriée serait «ennuyeux en tant que mothertrucker». Je ne comprends pas pourquoi Java ne permet pas cela. Parfois, je veux qu'une classe interne utilise les propriétés de la classe parente, mais conserve les méthodes statiques pour un meilleur espace de noms. Y a-t-il quelque chose qui ne va pas en soi? :(
Angad

6
Exactement. Je veux écrire une classe interne utilitaire. Certaines de ses méthodes bénéficieraient d'un accès à la classe externe, donc je ne peux pas la rendre statique, mais certaines de ses méthodes ne sont que des fonctions utilitaires. Pourquoi ne puis-je pas appeler A.B.sync(X)ou même (depuis A) B.sync(x)?
Edward Falk du

44

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 Innern'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.CONSTANTest 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.


6
-1 Je ne suis pas d'accord avec l'angle que vous avez pris ici. Nous pouvons certainement faire référence à un type de classe interne, par exemple. Outer.Inner i = new Outer().new Inner();De plus, les classes internes sont autorisées à déclarer des constantes statiques selon JLS §15.28.
Paul Bellora

2
Oui, les classes internes peuvent déclarer des constantes statiques . Cela n'a rien à voir avec les méthodes statiques! Bien que vous puissiez vous référer à une méthode statique de manière non statique, cela est déconseillé. Tous les outils de qualité du code se plaignent de ce type de référence et pour une bonne raison. Et vous avez manqué mon point. Je n'ai jamais dit qu'il n'y avait aucun moyen de référencer une classe interne statique. J'ai dit qu'il n'y a pas de moyen STATIQUE de référencer la méthode statique d'une classe interne d'une classe externe non statique. Ainsi, il n'y a pas de manière appropriée de le référencer.
Eddie

25
"Il n'y a pas grand chose à autoriser une méthode statique dans une classe interne non statique; comment y accéderiez-vous?" Vous appelleriez 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 Outerpour 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.
Paul Bellora

1
La différence entre Outer.Inner.CONSTANTet Outer.Inner.staticMethod()est qu'une référence à une constante n'a aucune chance de référencer implicitement l'instance de Outerdans 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.CONSTANTpartagent 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.
Eddie

3
@Eddie Vous ne pouvez pas faire référence aux champs d'instance dans une méthode statique, il n'y a donc pas de conflit lié à l'impossibilité de faire référence au champ d'instance implicite 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.
Theodore Murdock

20

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 Innerclasse semble avoir une incarnation par Outerinstance. 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$0n'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).


7
Mais c'est juste une erreur normale de type "tentative d'accès à une variable non statique à partir d'un contexte statique" - pas différent de si une méthode statique de niveau supérieur essaie d'accéder à la variable d'instance de sa propre classe.
Lawrence Dol

2
J'aime cette réponse car elle explique en fait pourquoi ce n'est pas techniquement possible, même si cela peut sembler possible syntaxiquement.
LoPoBo

@gustafc, je pense que c'était une excellente explication. Mais comme le souligne Lawrence, ce n'est qu'un échec à cause de la référence à foo, qui n'est pas statique. Mais que se passerait-il si je voulais écrire public static double sinDeg(double theta) { ... }une classe d'utilitaires mathématiques internes?
Edward Falk du

6

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 Innersoit associée à une instance de Outer, il est toujours possible de l'utiliser Outer.Inner.myStaticpour faire référence à un membre statique de Innersi 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 Outertant que membres statiques. Ce n'est pas pire que d'utiliser des membres statiques dans Inner, où Outerpeut toujours accéder à n'importe quel membre privé de Innertoute façon (n'améliore pas l'encapsulation).

Si vous avez besoin de partager quelque chose parmi toutes les instances de Innercréé par un outerobjet, il est plus logique de les mettre en Outerclasse 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.


Les classes intérieures ne sont pas non plus un "must". Cependant, comme la langue ne fournira les classes internes, il doit fournir une mise en œuvre complète et significative d'entre eux.
intrepidis

4

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.


3

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.


Je pense qu'une difficulté plus fondamentale pour permettre aux classes internes non statiques d'avoir des membres statiques non constants est que les programmeurs déclarant de tels membres pourraient avoir l'intention de les lier à des instances de la classe externe, ou de les faire être vraiment statiques. Dans les cas où un concept - s'il est légal - pourrait raisonnablement être spécifié comme signifiant l'une ou l'autre de deux choses différentes, et où ces deux choses peuvent être exprimées de manière non ambiguë, spécifier que ce concept est illégal est souvent mieux que de le spécifier comme ayant l'un ou l'autre sens.
supercat

3

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 classtant 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:

  1. Cela signifie-t-il qu'il n'y a qu'une seule instance dans VM?
  2. Ou une seule instance par objet extérieur?

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.


3

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.

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

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


2

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.


Benedikt, que voulez-vous dire quand vous dites "les classes imbriquées statiques ne sont qu'un moyen de regrouper"?
Ankur

0

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.


0

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.


0

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 

-1

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


-2

Vous êtes autorisé à utiliser les méthodes statiques sur les classes imbriquées statiques. Par exemple

public class Outer {

  public static class Inner {

    public static void method() {

    }
  }
}
En utilisant notre site, vous reconnaissez avoir lu et compris notre politique liée aux cookies et notre politique de confidentialité.
Licensed under cc by-sa 3.0 with attribution required.