以下代码在Java 1.6中编译良好,但无法在Java 1.7中编译。为什么呢?
代码的相关部分是对私有“数据”字段的引用。引用来自定义字段的同一类,因此看似合法。但它是通过一般类型的变量发生的。这段代码 - 基于内部库中的类的简化示例 - 在Java 1.6中工作,但现在不在Java 1.7中。
我不是在问这个如何解决这个问题。我已经做到了。我试图找到解释为什么这不再起作用的原因。我想到了三种可能性:
Foo.java:
import java.util.TreeMap;
import java.util.Map;
public abstract class Foo<V extends Foo<V>> {
private final Map<String,Object> data = new TreeMap<String,Object>();
protected Foo() { ; }
// Subclasses should implement this as 'return this;'
public abstract V getThis();
// Subclasses should implement this as 'return new SubclassOfFoo();'
public abstract V getEmpty();
// ... more methods here ...
public V copy() {
V x = getEmpty();
x.data.clear(); // Won't compile in Java 1.7
x.data.putAll(data); // "
return x;
}
}
编译器输出:
> c:\tools\jdk1.6.0_11\bin\javac -version
javac 1.6.0_11
> c:\tools\jdk1.6.0_11\bin\javac c:\temp\Foo.java
> c:\tools\jdk1.7.0_10\bin\javac -version
javac 1.7.0_10
> c:\tools\jdk1.7.0_10\bin\javac c:\temp\Foo.java
Foo.java:18: error: data has private access in Foo
x.data.clear();
^
Foo.java:19: error: data has private access in Foo
x.data.putAll(data);
^
2 errors
附录。如果引用是私有方法而不是私有成员变量,则会出现同样的问题。这适用于Java 1.6,但不适用于1.7。
Foo2.java:
import java.util.TreeMap;
import java.util.Map;
public abstract class Foo2<V extends Foo2<V>> {
private final Map<String,Object> data = new TreeMap<String,Object>();
protected Foo2() { ; }
// Subclasses should implement this as 'return this;'
public abstract V getThis();
// Subclasses should implement this as 'return new SubclassOfFoo();'
public abstract V getEmpty();
// ... more methods here ...
public V copy() {
V x = getEmpty();
x.theData().clear(); // Won't compile in Java 1.7
x.theData().putAll(data); // "
return x;
}
private Map<String,Object> theData() {
return data;
}
}
编译器输出:
> c:\tools\jdk1.6.0_11\bin\javac c:\temp\Foo2.java
> c:\tools\jdk1.7.0_10\bin\javac c:\temp\Foo2.java
Foo2.java:18: error: theData() has private access in Foo2
x.theData().clear();
^
Foo2.java:19: error: theData() has private access in Foo2
x.theData().putAll(data);
^
答案 0 :(得分:18)
所演示的问题似乎与Oracle bug 6904536中报告的行为相符。该错误被关闭为“非问题”,其解释如下:
相应的JLS问题尚未解决,并带有以下注释:
类型变量成员资格的简化说明是 欢迎。一个私人成员存在一般困难 变量的界限。正式的这些成员不会成为成员 类型变量本身,虽然javac和Eclipse传统上是它们 成员和代码已经开始依赖于:
彼得提交了类似的测试:class Test { private int count = 0; <Z extends Test> void m(Z z) { count = z.count; // Legal in javac 1.6, illegal in javac 1.7 due to fix for 6711619 } }
class A { static class B { private String f; } abstract static class Builder<T extends B> { abstract T getB(); { ((B)getB()).f.hashCode(); getB().f.hashCode(); // error: f has private access in A.B } } }
由于交集类型是通过继承和私有构造的 成员永远不会被继承,重新指定交集是很棘手的 拥有私人成员的类型。尽管如此,它将是兼容的 要做的事。
作为参考,JLS的相关部分为§4.4。
修改强>
我实际上倾向于同意这里的JLS,因为当我们从图片中删除泛型时它与自身匹配。考虑这个例子:
static class Parent {
private int i;
void m(Child child) {
i = child.i; //compile error
}
}
static class Child extends Parent { }
child.i
不可见,因为不会继承对私有成员的访问权限。由于Child
可以拥有自己的i
而没有任何阴影,因此这一点得到了回归:
static class Child extends Parent {
private int i; //totally fine
}
所以这将是一个罕见的 upcasting 的例子:
void m(Child child) {
i = ((Parent)child).i;
}
因此,对于图片中的继承可访问性,JLS似乎在这里是正确的,因为V
中的Foo<V extends Foo<V>>
不一定是Foo<V>
,但可能是某种扩展Foo<V>
的类型1}}。