java - JLS 的哪一部分讨论了本地类成员的可访问性?

java 语法允许本地类的成员字段/方法具有访问修饰符。但是它们https://stackoverflow.com/q/29894349。这似乎也适用于匿名类。

这感觉非常违反直觉。 JLS 中是否指定了此行为?

代码:

class Foo {
  public static void main(String[] args) {
    class Bar {
      private int a = 1;
      protected int b = 2;
      public  int c = 3;
    }
    var bar = new Bar();
    System.out.println(bar.a);    // works for private a
    System.out.println(bar.b);    // works for protected b
    System.out.println(bar.c);    // works for public c
  }
}

回答1

是的。就在关于访问修饰符的部分中 - 没有单独的部分关于“访问修饰符的含义,特别是在本地类的上下文中”,出于同样的原因,没有关于“访问修饰符的含义,特别是在满月时的含义”的部分' - 这里没有任何变化。

https://docs.oracle.com/javase/specs/jls/se8/html/jls-6.html#jls-6.6.1 涵盖了确定发生的确切情况所需的所有内容(并得出结论,它们确实在这里没有影响)。我故意选择了 JLS8,因为 9+ 由于模块范围的东西而使水域有些混乱。

引用类型的成员(类、接口、字段或方法)或类类型的构造函数,只有在该类型可访问并且声明成员或构造函数允许访问时才可访问:

强调我的:除了您在其中声明本地类的方法内的代码外,任何和所有代码都无法访问该类型。这通常意味着访问修饰符与您的方法之外的所有代码完全无关——它们可以'甚至不访问 Bar,因此字段 a 的可访问性是没有实际意义的。

因此,我们可以将确定每个访问修饰符的作用仅限于“......对试图访问它的代码,因此它必须存在于这个方法中,因为由于类型不可访问,它在其他任何地方都是不可访问的”。然后:

public

很简单,方法中的代码可以访问 public 的东西。

没有(包私有)

简单地说,方法中的代码与本地类在同一个包中,根据定义 - 包语句只能存在于编译单元级别,并且这些东西必须在同一个单元中。因此,是的,方法中的代码可以访问包私有的东西。

protected

... 是包 private 的超集,因此,是的,方法中的代码可以访问 protected 事物。

private

从 JLS 6.6.1:

否则,成员或构造函数被声明为私有,并且当且仅当它发生在包含成员或构造函数声明的顶级类(第 7.6 节)的主体内时才允许访问。

根据定义,本地类不是顶级类;当应用于本地类时,“在语法树中向上走直到到达顶级类”的工作会导致相同的答案,保证与将其应用于本地类方法中的其他代码时一样是的。因此,是的,您可以访问 private 东西。

因此...

对于所有 4 个访问级别,答案是完全相同的:是的,您可以。因此,访问修饰符是无关紧要的。这在 JLS 中没有提到,因为这是应用规则的结果。

关于意图的猜测

我没有亲自设计 java-the-language。所以,这些只是猜测:

  • 明确地对本地类中访问修饰符的性质进行任何规范意味着 JLS 比它需要的要长 - 上述行为是足够明智的(你还想要什么?假设你希望 private 的东西是不可访问的- 为什么?这不是 private 在 java 中其他任何地方的工作方式,为什么本地类应该是一个例外?)
  • 完全禁止它们可能是值得的,也许可以在心智模型和编译器规范中节省一些“空间”。但是,这是有问题的——本地类可以实现和扩展某些东西——这可能需要一定的访问级别(你不能覆盖一个方法并降低它的访问级别)。然后规范还必须说,对于本地类,“不能降低访问级别”规则被放弃,然后会无缘无故地使一切复杂化。

回答2

原因 能够指定访问修饰符有用的例子是允许本地和匿名类实现接口,这需要实现方法是 public,或者允许本地和匿名类来扩展其他类并覆盖 public 方法。 (当您覆盖它时,您不能使方法的访问说明符更具限制性)。

例如:

class Foo {
    public static void main(String[] args) {
        Comparator<String> byLength = new Comparator<String>() {
            // Need to make this method public because the method
            // in interface Comparator is public
            @Override
            public int compare(String o1, String o2) {
                return Integer.compare(o1.length(), o2.length());
            }
        };

        // ...
    }
}

对于其他用例,Java 语言设计者可能根本不费心添加额外的规则来明确禁止访问修饰符。它只会使语言的规则更加复杂,并且在这里指定访问修饰符的能力没有用,但也不会造成任何伤害。

相似文章

java - 为什么 System.out.println('a' == 97.0) 给出 true

我知道'=='运算符检查它们是否存在于Java中的同一内存位置,但是为什么,当我比较char和double数据类型时,Java给了我true?它们不是不同的数据类型吗?以及为什么它没有给我一个编译时错...