Pau*_*aul 16 java generics javac java-6 java-7
以下代码在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;
}
}
Run Code Online (Sandbox Code Playgroud)
编译器输出:
> 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
Run Code Online (Sandbox Code Playgroud)
附录.如果引用是私有方法而不是私有成员变量,则会出现同样的问题.这适用于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;
}
}
Run Code Online (Sandbox Code Playgroud)
编译器输出:
> 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);
^
Run Code Online (Sandbox Code Playgroud)
Pau*_*ora 18
证明的问题似乎与Oracle bug 6904536中报告的行为相符.该错误被关闭为"非问题",并提供以下说明:
相应的JLS问题尚未解决,并带有以下注释:
欢迎对类型变量的成员资格进行简化说明.类型变量边界的私有成员存在一般困难.正式地,这样的成员本身并不成为类型变量的成员,尽管javac和Eclipse传统上使它们成为成员并且代码已经依赖于:
Run Code Online (Sandbox Code Playgroud)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 } }彼得提交了类似的测试:
Run Code Online (Sandbox Code Playgroud)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 { }
Run Code Online (Sandbox Code Playgroud)
child.i不可见,因为不会继承对私有成员的访问权限.这一点是由于它Child可以拥有自己的事实i而没有任何阴影:
static class Child extends Parent {
private int i; //totally fine
}
Run Code Online (Sandbox Code Playgroud)
所以这将是一个罕见的向上转换必要的例子:
void m(Child child) {
i = ((Parent)child).i;
}
Run Code Online (Sandbox Code Playgroud)
因此,对于图片中的继承可访问性,JLS在这里似乎是正确的,因为Vin Foo<V extends Foo<V>>不一定Foo<V>但可能是某种类型的扩展Foo<V>.
| 归档时间: |
|
| 查看次数: |
5079 次 |
| 最近记录: |