web-dev-qa-db-ja.com

この静的内部クラスが外部クラスで非静的メソッドを呼び出せないのはなぜですか?

現在、Joshua BlochによるEffective Javaを読んでいますが、大好きです!しかし、112ページ(項目24)では、Blochは次のように書いています。

静的メンバークラスは、最も単純な種類のネストされたクラスです。偶然別のクラス内で宣言され、プライベートクラスとして宣言されているクラスのメンバーも含めて、そのクラスのすべてのメンバーにアクセスできる通常のクラスと考えるのが最適です。

そして、それは本当に私を混乱させます。私はむしろ言いたい:

静的メンバークラスは、最も単純な種類のネストされたクラスです。偶然別のクラス内で宣言され、外側のクラスのすべてのstaticメンバー(プライベートとして宣言されたメンバーも含む)にアクセスできるのは、通常のクラスと考えるのが最適です。

以下は、引用についての私の理解を示すスニペットです。

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)
        }

    }
}

PrintMessageメソッドはインスタンスメソッドであるのに対して、InnerClassのsayHelloメソッドは静的な内部クラスで宣言されているため、OuterClassのprintMessageメソッドにアクセスできないことがわかります。著者は、静的メンバークラスが、囲んでいるクラスの非静的フィールドにアクセスできることを示唆しているようです。私は彼の最後の文で何かを誤解していると確信していますが、何を理解することはできません。どんな助けも感謝します!

編集:2つのメソッドの可視性を変更しました。これは私の質問とは無関係だからです。私はプライベートメンバーではなく、静的メンバーに興味があります。

22
Robin Dos Anjos

InnerClassstaticであるからといって、他の手段を介してOuterClassのインスタンスへの参照を取得できなかったわけではありません。

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
        }

    }
}

InnerClassOuterClass内にネストされていなかった場合、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
    }

}
46
Andreas

エラーメッセージに注意してください。 アクセス権がないと言っているわけではありません。メソッド呼び出すことはできませんと言っています。インスタンスメソッドは、それらを呼び出すインスタンスがなければ何も意味しません。エラーメッセージに示されているのは、そのインスタンスがないことです。

Blochが言っているのは、ifそのインスタンスが存在した場合、内部クラスのコードはそのインスタンスインスタンスメソッドを呼び出すことができるということです。

次のクラスがあるとします:

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

ランダムクラスからプライベートメソッドを呼び出そうとすると、次のことができません。

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

これらのエラーメッセージにはprivate accessと書かれていることに注意してください。

OuterClass自体にメソッドを追加すると、それらのメソッドを呼び出すことができます。

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

または、静的内部クラスを追加する場合:

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

非静的な内部クラスを追加すると、魔法をかけることができるように見えます:

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

ただし、ここで進行中の策略があります:non-static内部クラスはalways外部クラスのインスタンスに関連付けられており、実際に見ているものは次のとおりです。

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

ここに、 OuterClass.thisは、その外部インスタンスにアクセスするための特別な構文です。ただし、あいまいな場合にのみ必要です。外部クラスと内部クラスに同じ名前のメソッドがある場合。

また、非静的クラスは、静的クラスが実行できることを引き続き実行できることに注意してください。

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

つまり、publicprivateは常にaccessです。 Blochが指摘しているのは、内部クラスには他のクラスにはないアクセス権があるということです。ただし、どのインスタンスを呼び出したいのかをコンパイラに通知せずに、インスタンスメソッドを呼び出すことはできません。

8
David Moles

あなたがそれを示した方法には、継承が必要です。ただし、メソッドとフィールドには次の方法でアクセスできます。

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

ただし、静的内部クラスはprintMessage関数にアクセスできないこと、内部クラスであることとは関係ありませんが、静的であり、非静的メソッドを呼び出すことはできません。あなたが提案した「静的」という言葉の使用は、最初の文で暗黙的だったと思います。彼が指摘している、または強調することを選んだのは、内部クラスがその親クラスのプライベートメソッドにアクセスできることだけです。彼は、同じ文で静的/非静的の区別をすることも不必要または混乱しているかもしれません。

4
Kris

私の見方では、テキストは絶対に正しいです。静的メンバークラスは、囲んでいるクラス(の一種)のプライベートメンバーにアクセスできます。例を示しましょう:

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