为什么这个代码在Java 1.6中编译而不是在Java 1.7中编译?

内容来源于 Stack Overflow,并遵循CC BY-SA 3.0许可协议进行翻译与使用

  • 回答 (1)
  • 关注 (0)
  • 查看 (27)

以下代码在Java 1.6中编译得很好,但无法在Java 1.7中编译。为什么?

代码的相关部分是对私有“数据”字段的引用。参考文献来自同一个领域,该领域的定义是合法的。但是它是通过一个通用类型的变量发生的。这个代码 - 基于内部库的一个类的简化示例 - 在Java 1.6中工作,但现在不在Java 1.7中。

我不问如何解决这个问题。我已经做到了。我试图找到解释为什么这不再起作用。想到三种可能性:

  • 根据JLS,该代码不是合法的,也不应该编译(1.6版编译器中存在一个bug,在1.7版中修复)
  • 根据JLS,此代码是LEGAL,并且应该进行编译(1.7版编译器中引入了向后兼容性错误)
  • 此代码属于JLS中的灰色区域

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);
         ^
提问于
用户回答回答于

相应的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,因为当我们从图片中删除泛型时,它与自身相匹配。考虑这个例子:

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;
}

因此,从图片中的继承可访问性来看,JLS在这里似乎是正确的,因为Vin Foo<V extends Foo<V>>不一定是,Foo<V>但可能是某种类型的扩展Foo<V>

扫码关注云+社区