从匿名静态实例访问私有实例成员 - java

考虑以下代码:

enum E {
    A { public int get() { return i; } },
    B { public int get() { return this.i; } },
    C { public int get() { return super.i; } },
    D { public int get() { return D.i; } };

    private int i = 0;
    E() { this.i = 1; }
    public abstract int get();
}

我在前两个枚举常量声明(A和B)上遇到编译时错误,但在后两个编译常量(C和D)上出现编译时错误。错误是:

A行上的错误1:非静态变量i不能从静态上下文中引用
B行上的错误2:我在E中拥有私人访问权限

由于get是实例方法,所以我不明白为什么我无法以所需的方式访问实例变量i

注意:从private声明中删除i关键字也会使代码可编译,我也不明白。

使用Oracle JDK 7u9。

编辑

正如评论中指出的那样,这并非特定于枚举,并且以下代码产生相同的行为:

class E {
    static E a = new E() { public int get() { return i; } };
    static E b = new E() { public int get() { return this.i; } };
    static E c = new E() { public int get() { return super.i; } };
    static E d = new E() { public int get() { return d.i; } };

    private int i = 0;
}

参考方案

Java语言规范规定了观察到的行为,特别是对包含类型的字段的隐式访问,以及不继承私有成员的规则。

非限定字段访问

A { public int get() { return i; } }

规格mandates:

枚举常量的可选类主体隐式定义了一个匿名类声明(第15.9.5节),该声明扩展了立即封闭的枚举类型。类主体受匿名类的通常规则约束;特别是它不能包含任何构造函数。

这使得i表达式有些模棱两可:我们是指封闭实例的实例还是内部实例的字段? las,内部实例doesn't inherit the field:

声明为私有的类的成员不会被该类的子类继承。

因此,编译器得出结论,我们的意思是访问封闭实例的字段-但是在静态块中,没有封闭实例,因此会出现错误。

通过this 进行字段访问

B { public int get() { return this.i; } },

规格mandates:

当用作主要表达式时,关键字this表示一个值,该值是对其调用实例方法(第15.12节)的对象或正在构造的对象的引用。

因此,很明显,我们需要内部类的字段,而不是外部类的字段。

编译器拒绝字段访问表达式this.i is的原因:

声明为私有的类的成员不会被该类的子类继承。

即,只能通过声明该字段的类型的引用而不是其子类型来访问私有字段。确实,

B { public int get() { return ((E)this).i; } },

编译就好了。

通过超级访问

这样,将super refers添加到在其上调用了该方法的对象(或正在构造的对象)。因此很明显,我们指的是内部实例。

此外,super的类型为E,因此该声明可见。

通过其他字段访问

D { public int get() { return D.i; } };

在这里,D是对D中声明的静态字段E的无条件访问。由于它是一个静态字段,因此使用哪个实例的问题尚无定论,并且访问有效。

但是,它非常脆弱,因为只有在枚举对象完全构建后才分配该字段。如果有人在构造过程中调用get(),则会抛出NullPointerException

推荐

如我们所见,访问其他类型的私有字段受到一些复杂的限制。由于很少需要,开发人员可能不知道这些细微之处。

虽然将字段protected设置为会削弱访问控制(即,允许程序包中的其他类访问该字段),但可以避免这些问题。

Java-将int更改为ascii - java

java有没有办法将int转换为ascii符号? 参考方案 您是否要将int转换为char?:int yourInt = 33; char ch = (char) yourInt; System.out.println(yourInt); System.out.println(ch); // Output: // 33 // ! 还是要将int转换为Stri…

Java:线程池如何将线程映射到可运行对象 - java

试图绕过Java并发问题,并且很难理解线程池,线程以及它们正在执行的可运行“任务”之间的关系。如果我创建一个有10个线程的线程池,那么我是否必须将相同的任务传递给池中的每个线程,或者池化的线程实际上只是与任务无关的“工人无人机”可用于执行任何任务?无论哪种方式,Executor / ExecutorService如何将正确的任务分配给正确的线程? 参考方案 …

JAVA:字节码和二进制有什么区别? - java

java字节代码(已编译的语言,也称为目标代码)与机器代码(当前计算机的本机代码)之间有什么区别?我读过一些书,他们将字节码称为二进制指令,但我不知道为什么。 参考方案 字节码是独立于平台的,在Windows中运行的编译器编译的字节码仍将在linux / unix / mac中运行。机器代码是特定于平台的,如果在Windows x86中编译,则它将仅在Win…

java:继承 - java

有哪些替代继承的方法? java大神给出的解决方案 有效的Java:偏重于继承而不是继承。 (这实际上也来自“四人帮”)。他提出的理由是,如果扩展类未明确设计为继承,则继承会引起很多不正常的副作用。例如,对super.someMethod()的任何调用都可以引导您通过未知代码的意外路径。取而代之的是,持有对本来应该扩展的类的引用,然后委托给它。这是与Eric…

Java:BigInteger,如何通过OutputStream编写它 - java

我想将BigInteger写入文件。做这个的最好方式是什么。当然,我想从输入流中读取(使用程序,而不是人工)。我必须使用ObjectOutputStream还是有更好的方法?目的是使用尽可能少的字节。谢谢马丁 参考方案 Java序列化(ObjectOutputStream / ObjectInputStream)是将对象序列化为八位字节序列的一种通用方法。但…