【问题标题】:Access field of outer base class外部基类的访问字段
【发布时间】:2021-05-30 13:22:46
【问题描述】:

在 Java 中,内部类通常可以访问外部类的私有成员。 在编写 Android 应用程序时,我有一个静态内部类,它扩展了它的外部类。 结果,无法访问外部类的私有字段:

class Outer {
  private int m_field = 1;

  static class Inner extends Outer {
    Inner() {
      m_field = 2;
    }
  }
}

它给出了一个令人困惑的错误消息:

错误:不能从静态上下文中引用非静态变量 m_field

即使除了类本身之外没有什么是静态的。

当字段 m_field 被保护时,它编译没有问题。 但是,当这样做时:

class Outer {
  private int m_field = 1;

  static class Inner extends Outer {
    Inner() {
      ((Outer)this).m_field = 2;
    }
  }
}

它可以正常工作。 这是编译器中的错误吗?为什么需要强制转换为已经是实例的外部类?


编辑:

对于这个的真实用例,考虑这样一个类:

public abstract class MyItem {
  private int m_counter = 0;
  public abstract int updateSomething();

  public static class CountItem extends MyItem {
    public int updateSomething() { m_counter++; }
  }

  public static class DoubleCountItem extends MyItem {
    public int updateSomething() { m_counter += 2; }
  }
}

相当抽象的例子,但它可以用来为不需要大量代码的抽象类提供基本实现。

EDIT2:

正如@Nathan 建议的那样,这个问题似乎可以通过 2 个类重新创建而无需嵌套:

class Base {
  private int x = 0;

  void a(Extended b) {
    ((Base)b).x = 1; //<-- with cast: compiles, without: error
  }
}

class Extended extends Base {

}

哪个给出更好的错误信息:

错误:x 在 Base 中有私有访问权限

【问题讨论】:

  • 你到底为什么要让一个内部类扩展它的外部类?这真是自找麻烦。
  • 我不这么认为——考虑一个抽象类,它已经提供了自身的基本实现,可能只有一个抽象方法实现。我不想为此创建一个新的源文件。
  • 创建一个内部类只是为了避免它有点疯狂:p(从可读性的角度来看)
  • 我认为在具体类很小的情况下可以提高可读性。
  • 这不是内部类。它是一个嵌套类,也是一个静态类。使它成为非静态的,即内部的,问题就会消失。你写的代码没有意义。

标签: java


【解决方案1】:

您在这里看到的是,只要您在 Outer 的类定义范围内,您就可以访问任何具有 Outer 类的私有成员,包括强制转换为 Outer 的东西。它们必须具有相同的类(而不是类的实例,具有不同的具体子类)。

内部类的东西很复杂,这里有一个更小的例子:

public class A {
    private int foo = 0;

    public String toString() {
        return "A: foo=" + foo;
    }

    public static void main(String[] args) {
        B b = new B();
        System.out.println(b);
        ((A)b).foo = 1;
        System.out.println(b);
    }
}

class B extends A {

}

这可以编译,只是因为它在 A 的类定义中。将 main 方法移到其他地方(例如,移到 B 中),您不能再引用 foo。

这是您在编写 equals 方法时看到的东西,您可以在其中访问同一类的另一个实例的私有字段,因为您正在编写一个属于类定义的方法。

Java language specification, in 6.6.1 Determining Accessibility:

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

如果不强制转换为 Outer,则不允许访问,因为 a) m_field 是 Outer 的私有成员,因此它对子类不可见,并且 b) 它不是所声明的类的成员。添加强制转换意味着编译器将其视为 Outer,并且 m_field 变得可访问。

【讨论】:

  • 这并不能解释为什么代码在没有转换为 Outer 的情况下无法编译,但可以通过转换来编译。我很想在 JLS 中找到解释。
  • 我知道您可以访问您“内部”的类的对象的所有私有字段。但是,如果根据定义,B 已经是 A 的 instanceof,为什么需要将 B 转换为 A?
  • @Chrisu:因为你没有通过通常的渠道。这是编写类声明的特殊例外情况。这就是他们设计的方式,除非您使用强制转换,否则他们不希望将这种特殊访问扩展到子类。
  • 我明白了,也会接受这个答案,但是,JLS 是否没有证据表明这是故意的?
  • @Chrisu:如果你想等待接受,我没问题。到目前为止,答案中的报价是我所拥有的最好的。
猜你喜欢
  • 2011-09-26
  • 2021-03-13
  • 1970-01-01
  • 2021-04-21
  • 1970-01-01
  • 2018-01-04
  • 2015-02-25
  • 2011-05-29
相关资源
最近更新 更多