为什么这个静态内部类不能在其外部类上调用非静态方法?

Rob*_*jos 22 java static class member

我目前正在阅读Joshua Bloch的Effective Java,我喜欢它!但在第112页(第24项)布洛赫写道:

静态成员类是最简单的嵌套类.最好将其视为普通类,恰好在另一个类中声明,并且可以访问所有封闭类的成员,甚至是那些声明为private的成员.

这真让我困惑.我宁愿说:

静态成员类是最简单的嵌套类.它最好被认为是一个普通的类,碰巧在另一个类中声明,并且可以访问所有封闭类的静态成员,甚至是那些声明为private的成员.

这是一个片段,说明了我对引用的理解:

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

    }
}
Run Code Online (Sandbox Code Playgroud)

您可以看到InnerClass的sayHello方法无法访问OuterClass的printMessage方法,因为它是在静态内部类中声明的,而printMessage方法是一个实例方法.看起来作者似乎建议静态成员类可以访问封闭类的非静态字段.我确信我在他的最后一句话中误解了一些内容,但我无法弄清楚是什么.任何帮助将不胜感激!

编辑:我改变了两种方法的可见性,因为它与我的问题无关.我对静态成员感兴趣,而不是私人成员.

And*_*eas 46

正因为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
        }

    }
}
Run Code Online (Sandbox Code Playgroud)

如果InnerClass没有嵌套在里面OuterClass,它将无法访问该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
    }

}
Run Code Online (Sandbox Code Playgroud)

  • @RobinDosAnjos号"有访问权"意味着"被允许".例如,在上面的第二个例子中,不允许调用,因为它试图从一个独立的类调用`private`方法(错误:*不可见*).要调用非静态方法,它仍然需要引用`OuterClass`的实例."有访问权"并非如此. (5认同)

Dav*_*les 8

请注意错误消息.这并不是说你没有访问权限.它说这个方法无法调用.实例方法并不意味着没有实例来调用它们.错误消息告诉您的是您没有该实例.

Bloch告诉你的是,如果存在该实例,内部类中的代码可以在其上调用私有实例方法.

假设我们有以下课程:

public class OuterClass {
  public void publicInstanceMethod() {}
  public static void publicClassMethod() {}
  private void privateInstanceMethod() {}
  private static void privateClassMethod() {}
}
Run Code Online (Sandbox Code Playgroud)

如果我们尝试从一些随机类调用这些私有方法,我们不能:

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
  }
}
Run Code Online (Sandbox Code Playgroud)

请注意,这些错误消息表示私有访问.

如果我们向OuterClass自己添加一个方法,我们可以调用这些方法:

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

或者如果我们添加一个静态内部类:

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
    }
  }
}
Run Code Online (Sandbox Code Playgroud)

如果我们添加一个非静态内部类,看起来我们可以做魔术:

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

然而,这里有一些技巧:非静态内部类总是与外部类的实例相关联,而您真正关注的是:

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

这里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();
    }
  }
Run Code Online (Sandbox Code Playgroud)

简而言之:public并且private总是关于访问.Bloch的观点是内部类具有其他类不具备的访问权限.但是没有任何访问权限允许您调用实例方法而不告诉编译器您要调用它的实例.


Iva*_*van 6

你展示它的方式需要继承.但方法和字段可以通过这种方式访问​​:

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!"); 
    }

  }
}
Run Code Online (Sandbox Code Playgroud)