2013-03-28 56 views
16

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

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

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

  • 此代码是并非法律根据JLS和不应该编译(有一个在1.6编译器中的错误,定格在1.7)
  • 此代码是法律根据JLS和应编译(向后兼容性错误已被引入到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); 
     ^
+0

我建议反编译生成的两个类文件,然后区别应该是显而易见的。 – Landei

+0

@Landei在1.7的情况下没有生成类文件,因为编译器拒绝编译它。 –

回答

18

的证明问题似乎匹配Oracle bug 6904536报道的行为。该错误以“不是问题”的形式关闭,并解释如下:

javac根据JLS行事。另见6558551,6711619和相关的JLS问题6644562

相应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是不可见的,因为没有继承访问私有成员。

static class Child extends Parent { 
    private int i; //totally fine 
} 

所以这将是上溯造型是必须的一个罕见的例子:

void m(Child child) { 
    i = ((Parent)child).i; 
} 

因此,与继承这一点是事实,Child可以有自己的i没有任何遮蔽驱动之家无障碍的图片,在这里,JLS似乎是正确的,因为Foo<V extends Foo<V>>中的V不一定是Foo<V>,但可以是延伸Foo<V>的某种类型。

+0

谢谢保罗。是的,答案似乎是javac编译了某些不与JLS严格一致的表单,上面是一个例子。 7中的javac已经修复了这个问题。特别是,通过泛型引用*访问私有字段/方法,即使在同一个类中也是不允许的。 (这很遗憾,我不是JLS专家,但我不能立即明白为什么它不应该)。简言之,JDK 1.6编译器有一个bug。 – Paul

+1

@Paul没问题,感谢有趣的帖子。如果你有兴趣,看看我的编辑。 –

+4

非常好的答案!感谢您进行研究和解释。 – Jesse

相关问题