以下代码在Java 1.6中编译得很好,但在Java 1.7中无法编译。为什么?
代码的相关部分是对私有“data”字段的引用。引用来自定义字段的同一个类,因此看起来是合法的。但它是通过泛型变量实现的。这段代码-基于内部库中的类的简化示例-在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);
^
发布于 2013-04-08 12:56:41
演示的问题似乎与Oracle bug 6904536中报告的行为相匹配。bug被关闭为“不是问题”,并有以下解释:
相应的JLS问题未解决,并有以下注释:
欢迎对类型变量成员的简化解释。对于类型变量边界的私有成员,通常存在困难。在形式上,这样的成员不会成为类型变量本身的成员,尽管javac和Eclipse传统上使它们成为成员,代码已经开始依赖于这一点:
类测试{ private int count = 0;void m(Z z) { count = z.count;//在javac1.6中是合法的,在javac1.7中是非法的,因为修复了6711619 }}
Peter提交了一个类似的测试:
类A{静态类B{私有字符串f;}抽象静态类生成器{抽象T getB();{ ((B)getB()).f.hashCode();getB().f.hashCode();//错误:f在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
}
因此,这将是一个罕见的向上转换是必要的例子:
void m(Child child) {
i = ((Parent)child).i;
}
因此,考虑到Foo<V extends Foo<V>>
中的V
不一定是Foo<V>
,而是可以是某种扩展Foo<V>
的类型,因此,由于继承了可访问性,所以JLS在这里似乎是正确的。
https://stackoverflow.com/questions/15681444
复制相似问题