web-dev-qa-db-fra.com

Pourquoi cette classe interne statique ne peut-elle pas appeler une méthode non statique sur sa classe externe?

Je lis actuellement Effective Java de Joshua Bloch et j'adore! Mais à la page 112 (élément 24), Bloch écrit:

Une classe de membre statique est le type le plus simple de classe imbriquée. C'est mieux pensé comme une classe ordinaire qui se trouve être déclarée à l'intérieur une autre classe et a accès à tous les membres de la classe englobante, même ceux déclarés privés.

Et ça me trouble vraiment. Je dirais plutôt:

Une classe de membre statique est le type le plus simple de classe imbriquée. C'est mieux pensé comme une classe ordinaire qui se trouve être déclarée à l'intérieur une autre classe et a accès à tous les membres static members de la classe englobante, même ceux déclarés privés.

Voici un extrait qui illustre ma compréhension de la citation:

public class OuterClass {

    public void printMessage(String message) {
        System.out.println(message);
    }

    private static class InnerClass {

        public void sayHello() {
            printMessage("Hello world!"); //error: Cannot make a static reference to the non-static method printMessage(String)
        }

    }
}

Vous pouvez constater que la méthode sayHello d'InnerClass n'a pas accès à la méthode printMessage d'OuterClass car elle est déclarée dans une classe interne statique alors que la méthode printMessage est une méthode d'instance. Il semble que l'auteur suggère qu'une classe membre statique puisse accéder à des champs non statiques de la classe englobante. Je suis convaincu d'avoir mal compris quelque chose dans sa dernière phrase, mais je ne sais pas quoi. Toute aide serait appréciée!

edit: J'ai changé la visibilité des deux méthodes parce que cela n'a rien à voir avec ma question. Je suis intéressé par les membres statiques, pas les membres privés.

21
Robin Dos Anjos

Le fait que InnerClass soit static ne signifie pas qu'il ne pouvait pas obtenir une référence à une instance de OuterClass par d'autres moyens, le plus souvent en tant que paramètre, par exemple.

public class OuterClass {

    private void printMessage(String message) {
        System.out.println(message);
    }

    private static class InnerClass {

        private void sayHello(OuterClass outer) {
            outer.printMessage("Hello world!"); // allowed
        }

    }
}

Si InnerClass n'avait pas été imbriqué dans OuterClass, il n'aurait pas eu accès à la méthode private.

public class OuterClass {

    private void printMessage(String message) {
        System.out.println(message);
    }

}

class InnerClass {

    private void sayHello(OuterClass outer) {
        outer.printMessage("Hello world!"); // ERROR: The method printMessage(String) from the type OuterClass is not visible
    }

}
45
Andreas

Notez le message d'erreur. Cela ne veut pas dire que vous n’avez pas accès . Cela signifie que la méthode ne peut pas être appelée . Les méthodes d'instance ne veulent rien dire sans instance à Les appeler. Le message d'erreur vous indique que vous ne disposez pas de cette instance.

Ce que Bloch vous dit, c'est que si cette instance existait, le code de la classe interne pourrait appeler des méthodes d'instance privée.

Disons que nous avons la classe suivante:

public class OuterClass {
  public void publicInstanceMethod() {}
  public static void publicClassMethod() {}
  private void privateInstanceMethod() {}
  private static void privateClassMethod() {}
}

Si nous essayons d'appeler ces méthodes privées depuis une classe aléatoire, nous ne pouvons pas:

class SomeOtherClass {
  void doTheThing() {
    OuterClass.publicClassMethod();
    OuterClass.privateClassMethod(); // Error: privateClassMethod() has private access in OuterClass
  }
  void doTheThingWithTheThing(OuterClass oc) {
    oc.publicInstanceMethod();
    oc.privateInstanceMethod();      // Error: privateInstanceMethod() has private access in OuterClass
  }
}

Notez que ces messages d'erreur disent accès privé .

Si nous ajoutons une méthode à OuterClass elle-même, nous pouvons appeler ces méthodes:

public class OuterClass {
  // ...declarations etc.
  private void doAThing() {
    publicInstanceMethod();  // OK; same as this.publicInstanceMethod();
    privateInstanceMethod(); // OK; same as this.privateInstanceMethod();
    publicClassMethod();
    privateClassMethod();
  }
}

Ou si nous ajoutons une classe interne statique:

public class OuterClass {
  // ...declarations etc.
  private static class StaticInnerClass {
    private void doTheThingWithTheThing(OuterClass oc) {
      publicClassMethod();  // OK
      privateClassMethod(); // OK, because we're "inside"
      oc.publicInstanceMethod();  // OK, because we have an instance
      oc.privateInstanceMethod(); // OK, because we have an instance
      publicInstanceMethod();  // no instance -> Error: non-static method publicInstanceMethod() cannot be referenced from a static context
      privateInstanceMethod(); // no instance -> Error: Java: non-static method privateInstanceMethod() cannot be referenced from a static context
    }
  }
}

Si nous ajoutons une classe interne non statique, il semble que nous puissions faire de la magie:

public class OuterClass {
  // ...declarations etc.
  private class NonStaticInnerClass {
    private void doTheThing() {
      publicClassMethod();     // OK
      privateClassMethod();    // OK
      publicInstanceMethod();  // OK
      privateInstanceMethod(); // OK
    }
  }
}

Cependant, il y a une supercherie: une classe interne non-statique est associée à une instance de la classe externe always, et ce que vous regardez réellement est la suivante:

  private class NonStaticInnerClass {
    private void doTheThing() {
      publicClassMethod();     // OK
      privateClassMethod();    // OK
      OuterClass.this.publicInstanceMethod();  // still OK
      OuterClass.this.privateInstanceMethod(); // still OK
    }
  }

OuterClass.this est ici une syntaxe spéciale pour accéder à cette instance externe. Mais vous n’en avez besoin que si cela est ambigu, par exemple. si les classes externes et internes ont des méthodes portant le même nom.

Notez aussi que la classe non statique peut toujours faire ce que la statique peut faire:

  private class NonStaticInnerClass {
    private void doTheThingWithTheThing(OuterClass oc) {
      // 'oc' does *not* have to be the same instance as 'OuterClass.this'
      oc.publicInstanceMethod();
      oc.privateInstanceMethod();
    }
  }

En bref: public et private concernent toujours accès . Ce que fait valoir Bloch, c'est que les classes internes ont un accès auquel les autres classes n'ont pas accès. Mais aucune quantité d'accès ne vous permet d'appeler une méthode d'instance sans indiquer au compilateur sur quelle instance vous souhaitez l'appeler.

8
David Moles

La façon dont vous l'avez montrée nécessite un héritage. Mais les méthodes et les champs pourraient être accessibles de cette façon:

public class OuterClass {

  private void printMessage(String message) {
    System.out.println(message);
  }

  private static class InnerClass {

    private void sayHello() {
        OuterClass outer = new OuterClass();
        outer.printMessage("Hello world!"); 
    }

  }
}
6
Ivan

Cependant, le fait que la classe interne statique n'ait pas accès à la fonction printMessage n'a pas à voir avec le fait qu'il s'agit d'une classe interne mais qu'elle est statique et ne peut pas invoquer une méthode non statique. Je pense que l'utilisation du mot "statique" que vous avez proposé était implicite dans la première phrase. Ce qu'il souligne ou a choisi de souligner est simplement que la classe interne peut toujours accéder aux méthodes privées de sa classe parente. Il aurait peut-être simplement jugé inutile ou déroutant de faire la distinction statique/non statique dans la même phrase également. 

4
Kris

De la façon dont je le vois, le texte est absolument correct. Les classes membres statiques peuvent accéder aux membres privés des classes englobantes (en quelque sorte). Permettez-moi de vous montrer un exemple:

public class OuterClass {
    String _name;
    int _age;
    public OuterClass(String name) {
        _name = name;
    }
    public static OuterClass CreateOuterClass(String name, int age) {
        OuterClass instance = new OuterClass(name);
        instance._age = age; // Notice that the private field "_age" of the enclosing class is visible/accessible inside this static method (as it would also be inside of a static member class).
        return instance;
    }
}
0
Vinicius