【问题标题】:Why can’t this static inner class call a non-static method on its outer class?为什么这个静态内部类不能在其外部类上调用非静态方法?
【发布时间】:2018-09-20 14:26:11
【问题描述】:

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

静态成员类是最简单的嵌套类。这是最好的 被认为是一个恰好在内部声明的普通类 另一个类并且可以访问所有封闭类的成员, 甚至那些被宣布为私有的。

这真的让我很困惑。我宁愿说:

静态成员类是最简单的嵌套类。这是最好的 被认为是一个恰好在内部声明的普通类 另一个类并且可以访问所有封闭类的 static 成员, 甚至那些被宣布为私有的。

这是一个说明我对这句话的理解的 sn-p:

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 虽然我应该学习阅读错误信息,但你应该学习阅读人们的信息,这些信息已经指出了你所说的内容。

标签: java class static member


【解决方案1】:

在我看来,文字是绝对正确的。静态成员类可以访问封闭类的私有成员(有点)。让我给你举个例子:

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

【讨论】:

    【解决方案2】:

    注意错误信息。这并不是说您没有访问权限。就是说方法不能调用。没有实例的实例方法没有任何意义 打电话给他们。错误消息告诉您的是您没有该实例。

    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 的观点是内部类具有其他类没有的访问权限。但是,没有多少访问权限允许您在不告诉编译器您要调用哪个实例的情况下调用实例方法。

    【讨论】:

      【解决方案3】:

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

      【讨论】:

      • 确实!但是让我非常困惑的是“可以访问所有封闭类的成员”。在我看来,这意味着“所有静态和非静态成员”,因为他没有指定它。这里隐含“静态”吗?
      • @RobinDosAnjos 没有。“有权访问”意味着“被允许”。例如。在上面的第二个示例中,不允许调用,因为它试图从独立类调用private 方法(错误:不可见)。要调用非静态方法,它仍然需要引用OuterClass 的实例。 “有访问权限”与此无关。
      • 非常感谢!我想我现在明白了!无论如何,这是我第一次在他的书中发现一些令人困惑的东西!
      • 说明为什么this.printMessage 不是一个选项有用吗?
      • @JollyJoker 不明白你为什么要解释这一点,因为this.printMessage 永远不会有效,无论InnerClass 是否是静态的。
      【解决方案4】:

      但是,静态内部类无法访问 printMessage 函数与它是内部类无关,而是它是静态的并且不能调用非静态方法。我认为您提出的“静态”一词的使用在第一句话中是隐含的。他指出或选择强调的只是内部类仍然可以访问其父类的私有方法。尽管在同一个句子中区分静态/非静态,但他可能也没有必要或令人困惑。

      【讨论】:

        【解决方案5】:

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

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

        【讨论】:

          猜你喜欢
          • 2014-07-13
          • 1970-01-01
          • 1970-01-01
          • 2010-11-01
          • 1970-01-01
          • 1970-01-01
          • 2021-10-01
          • 2011-03-02
          • 2015-10-07
          相关资源
          最近更新 更多