ass*_*ias 17 java enums static private
请考虑以下代码:
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();
}
Run Code Online (Sandbox Code Playgroud)
我在前2个枚举常量声明(A和B)上得到编译时错误,但最后2个编译正常(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;
}
Run Code Online (Sandbox Code Playgroud)
观察到的行为是由Java语言规范强制执行的,特别是对封闭类型字段的隐式访问,以及不继承私有成员的规则.
不合格的现场访问
A { public int get() { return i; } }
Run Code Online (Sandbox Code Playgroud)
规范要求:
枚举常量的可选类体隐式定义了一个匿名类声明(第15.9.5节),该声明扩展了直接封闭的枚举类型.班级团体由匿名班级的通常规则管理; 特别是它不能包含任何构造函数.
这使得表达式i
有些含糊不清:我们是指封闭实例的字段还是内部实例?唉,内部实例不继承该字段:
声明为private的类的成员不会被该类的子类继承.
因此,编译总结我们的意思来访问外围实例的领域-但在静态块之中,还有就是没有外围实例,因此错误.
现场访问 this
B { public int get() { return this.i; } },
Run Code Online (Sandbox Code Playgroud)
规范要求:
当用作主表达式时,关键字this表示一个值,该值是对调用实例方法的对象(第15.12节)的引用,或者是对正在构造的对象的引用.
因此,很明显我们想要内部阶级的领域,而不是外部领域.
编译器拒绝字段访问表达式的原因this.i
是:
声明为private的类的成员不会被该类的子类继承.
也就是说,私有字段只能通过声明字段的类型的引用来访问,而不能通过其子类型来访问.确实,
B { public int get() { return ((E)this).i; } },
Run Code Online (Sandbox Code Playgroud)
编译得很好.
通过超级访问
像这样,super 指的是调用方法的对象(或正在构造的对象).因此,很清楚我们的意思是内在的实例.
另外,super是类型的E
,因此声明可见.
通过其他领域访问
D { public int get() { return D.i; } };
Run Code Online (Sandbox Code Playgroud)
这里D
是对在其中D
声明的静态字段的非限定访问E
.因为它是一个静态字段,所以使用哪个实例的问题没有实际意义,并且访问有效.
然而它非常脆弱,因为只有在枚举对象完全构造时才分配字段.如果有人在施工期间调用get(),NullPointerException
则会抛出.
建议
正如我们所看到的,访问其他类型的私有字段受到一些复杂的限制.由于很少需要,开发人员可能不知道这些微妙之处.
虽然使该字段protected
会削弱访问控制(即允许包中的其他类访问该字段),但它将避免这些问题.