class A {
private int foo;
void bar(B b) { b.foo = 42; }
}
class B extends A { }
无法使用错误进行编译:
A.java:3: error: foo has private access in A
void bar(B b) { b.foo = 42; }
^
1 error
向基类添加强制转换使其有效。
void bar(B b) { ((A) b).foo = 42; }
有人能指出我为什么第一个片段是非法的解释?它被禁止的原因是什么?以下是JLS所说的内容:
否则,成员或构造函数被声明为
private
,当且仅当它发生在包含成员或构造函数声明的顶级类(第7.6节)的主体内时才允许访问。 / p>
我能说清楚,我的代码符合这个措辞。那么这是Java编译器的一个错误,还是我对JLS的解释不正确?
(注意:我不是在寻找变通方法,比如制作变量protected
。我知道如何解决这个问题。)
答案 0 :(得分:16)
错误消息“在A中有私有访问权限”是一个很长时间的java错误。
JDK 1.1:
JDK-4096353:JLS 6.6.1:当子类引用用于访问超类的私有
包含的代码段完全符合问题
class X{
private static int i = 10;
void f() {
Y oy = new Y();
oy.i = 5; // Is this an error? Is i accessable through a reference to Y?
}
}
class Y extends X {}
他们试图修复它并导致
JDK-4122297:javac的错误消息不适合私有字段。
======TP1======
1 class C extends S {
2 void f(){
3 java.lang.System.out.println("foo");
4 }
5 }
6
7 class S {
8 private int java;
9 }
======
% javac C.java
C.java:3: Variable java in class S not accessible from class C.
java.lang.System.out.println("foo");
^
C.java:3: Attempt to reference field lang in a int.
java.lang.System.out.println("foo");
^
2 errors
======
但是规范java
不是在C中继承的,而且这个程序应该编译。
它固定在1.2,但再次出现在1.3中
JDK-4240480:name00705.html:JLS6.3私有成员不应该从超类继承
JDK-4249653:new javac假定私有字段由子类继承
当泛型来的时候
JDK-6246814:类型变量的私有成员可以错误地访问
JDK-7022052:私有方法和泛型上的编译器错误无效
但是,通过JLS,该成员根本不存在于继承类型中。
JLS 8.2。班级成员
声明为private的类的成员不会被该类的子类继承。
所以b.foo
是非法的,因为类B
没有名为foo
的字段。
这不是限制,它是B
中缺席的字段。
Java具有强类型,我们无法访问B
中不存在的字段,即使它们存在于超类A
中。
投靠(A) b
是合法的,因为B
是A
的子类。
A
有一个名为foo
的字段,我们可以访问此私有字段,因为b(B b)
是类A
中的一个函数,即使b != this
归因于< / p>
JLS 6.6.1。确定可访问性
否则,如果成员或构造函数被声明为private,则当且仅当它发生在包含成员或构造函数声明的顶级类(第7.6节)的主体内时才允许访问。
另外,如果我们写
class A {
private int foo;
void baz(A b) { b.foo = 42; }
}
class B extends A { }
class T {
void x() {
B b = new B();
b.baz(b);
}
}
它将编译,因为Java推断多态调用的类型参数。
JLS 15.12.2.7。根据实际参数推断类型参数:
超类型约束T:&gt; X意味着解决方案是X的超类型之一。在T上给出了几个这样的约束,我们可以交叉每个约束隐含的超类型集合,因为类型参数必须是所有约束的成员。然后我们可以选择交叉点中最具体的类型
答案 1 :(得分:13)
您不能说b.foo
因为foo
是私有的,因此不会被继承,因此B
类无法看到foo
变量,我不知道一个名为foo
的变量是否存在 - 除非它被标记为受保护(如你所说)或默认(因为它们在我猜的同一个包中)或公开。
如果您想使用foo
而不使用像第二个示例那样的明确演员,则必须使用this.foo
或foo
具有隐式this
的{{1}}。在指定Javadocs时,this
关键字的主要原因是为了防止:
使用this关键字的最常见原因是因为某个字段 被方法或构造函数参数遮蔽。
当您使用((A) b)
时,您正在构建引用类型,如果您使用A
引用变量类型,编译器将会看到它,换句话说就像A a
之类的内容,以及a.foo
完全合法。
可见性和对超类私有实例变量的访问的说明性摘要:
答案 2 :(得分:2)
specification for field access expressions, chapter 15.11说:
如果标识符未在类型T中命名可访问的成员字段, 那么字段访问是未定义的,并且发生编译时错误。
从超类的角度来看,看看这些类型,我认为该成员无法访问,因此错误。
我认为您提交的案例更接近于作为字段访问成员,如示例15.11-1-1所示。
class S { int x = 0; }
class T extends S { int x = 1; }
class Test1 {
public static void main(String[] args) {
T t = new T();
System.out.println("t.x=" + t.x + when("t", t));
S s = new S();
System.out.println("s.x=" + s.x + when("s", s));
s = t;
System.out.println("s.x=" + s.x + when("s", s));
}
static String when(String name, Object t) {
return " when " + name + " holds a "
+ t.getClass() + " at run time.";
}
}
回答你的问题:
请解释限制所保护的错误代码类型 对
请考虑以下摘录。
public class X {
private int a;
public void bar(Z z) {
z.a // not visible, but if was, what 'a'
// would you actually access at this point 'X' or 'Z'
}
}
public class Z extends X {
private int a;
}
答案 3 :(得分:1)
在我看来,规范是不一致的。正如约翰所说,规范的主体陈述
否则,成员或构造函数被声明为private,并且当且仅当它发生在包含成员或构造函数声明的顶级类(第7.6节)的主体内时才允许访问。
并没有提到子类。所以A类应该正确编译。但是示例6.6-5说明
私有类成员或构造函数只能在包含成员或构造函数声明的顶级类(第7.6节)的主体内访问。 它不是由子类继承的。。
这第二个语句更弱(不仅仅是if),但是将子类带到表中。根据这个A不应该编译。
答案 4 :(得分:0)
Java很难通过引用类型访问私有变量,而引用类型不应该访问该变量。您应该能够通过撰写((A) b).foo = 42
合法地执行此操作。
答案 5 :(得分:-1)
我们无法继承private
字段或方法。因此,在您的代码中,即使您要从自己的类B
访问,foo
类也完全不知道变量A
。
答案 6 :(得分:-1)
这不是默认访问修饰符的用途吗?
试试这个:
public class blah{
static class A {
int foo;
void bar(B b) {b.foo=42;}
}
static class B extends A {
}
}
您不能直接从祖先访问私有成员,这是私有的意思。现在为什么你演员时会有效?这是否意味着文档不正确?
我向一位同事提到java文档可能是错误的,他指出你实际上是从A类中设置了foo的值。所以一切都是正确的。你不能(因为它是私人的)从后代访问foo,所以你必须强制转换。你不能在A的身体之外做到这一点。
我相信这是正确答案。