为什么静态内部类无法调用其外部类的非静态方法?

24

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

静态成员类是最简单的嵌套类。最好将其视为一个普通类,该类恰好在另一个类中声明,并且可以访问所有封闭类(即外部类)的成员变量和方法,即使这些成员变量和方法被声明为私有。

这真的让我感到困惑。我更愿意说:

静态成员类是最简单的嵌套类。最好将其视为一个普通类,该类恰好在另一个类中声明,并且可以访问所有封闭类(即外部类)的静态成员变量和方法,即使这些成员变量和方法被声明为私有。

下面是一个代码片段,说明了我对引用的理解:

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

    }
}
你可以看到InnerClass的sayHello方法无法访问OuterClass的printMessage方法,因为它声明在一个静态内部类中,而printMessage方法是一个实例方法。似乎作者建议静态成员类可以访问封闭类的非静态字段。我确信自己对他最后一句话理解有误,但我无法弄清楚具体是什么错误。希望能得到帮助!注意,我更改了两个方法的可见性,因为这与我的问题无关。我对静态成员感兴趣,而不是私有成员。

@CiaPan 虽然我应该学习阅读错误信息,但你应该学习阅读人们的信息,他们已经用稍微更加外交的方式指出了你所说的话。 - Robin Dos Anjos
5个回答

48

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
        }

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

}

确实如此!但是让我困惑的是“可以访问所有封闭类的成员”。在我看来,这意味着“所有静态和非静态成员”,因为他没有具体说明。这里是否隐含了“静态”一词? - Robin Dos Anjos
6
@RobinDosAnjos,"has access" 的意思是 "被允许"。例如,在上面的第二个例子中,该调用不被允许,因为它试图从一个独立的类中调用一个 private 方法(错误:不可见)。要调用非静态方法,仍然需要引用 OuterClass 的实例。"Has access" 不是关于那个的。 - Andreas
1
非常感谢!我现在明白了!无论如何,这是我在他的书中第一次遇到这么困惑的事情! - Robin Dos Anjos
详细解释为什么 this.printMessage 不可行会有帮助吗? - JollyJoker
@JollyJoker 不明白你为什么要解释这个,因为无论 InnerClass 是否是静态的,this.printMessage 都是无效的。 - Andreas
@Andreas,肯定是OuterClass.this.printMessage。我想解释一下,在静态上下文中没有实例。 - JollyJoker

9

注意错误信息。它并没有说你“没有访问权”。它说的是方法“无法被调用”。没有实例调用它们,实例方法是毫无意义的。错误信息告诉你的是你没有那个实例。

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

假设我们有以下的类:

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

请注意,这些错误信息显示为“私有访问”。 如果我们在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
    }
  }
}

然而,这里存在一些技巧:非静态内部类总是与外部类的一个实例相关联,你真正看到的是:
  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一直是关于访问权限的。 Bloch所说的重点是内部类具有其他类没有的访问权限。但是无论有多少访问权限,都不能在不告诉编译器要调用哪个实例方法的情况下调用实例方法。

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

  }
}

4
但是,静态内部类无法访问printMessage函数并不是因为它是内部类,而是因为它是静态的,不能调用非静态方法。我认为你提出的“静态”一词在第一句话中是隐含的。他所指出或强调的只是内部类仍然可以访问其父类的私有方法。他可能认为在同一句话中进行静态/非静态区分是不必要或令人困惑的。

0
在我看来,这段文字是完全正确的。静态成员类可以访问封闭类的私有成员(某种程度上)。让我给你举个例子:
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;
    }
}

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接