首页
学习
活动
专区
工具
TVP
发布
社区首页 >问答首页 >为什么这段代码可以在Java 1.6中编译,而不能在Java 1.7中编译?

为什么这段代码可以在Java 1.6中编译,而不能在Java 1.7中编译?
EN

Stack Overflow用户
提问于 2013-03-28 20:09:48
回答 1查看 7.5K关注 0票数 16

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

代码的相关部分是对私有“data”字段的引用。引用来自定义字段的同一个类,因此看起来是合法的。但它是通过泛型变量实现的。这段代码-基于内部库中的类的简化示例-在Java 1.6中有效,但现在在Java 1.7中不起作用。

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

  • 根据JLS,此代码是非法的,并且永远不应该编译( 1.6版编译器中存在错误,已在1.7中修复)
  • 根据JLS,此代码是合法的,应该进行编译(1.7版编译器中引入了向后兼容性错误)
  • 此代码在JLS

中处于灰色区域。

Foo.java:

代码语言:javascript
复制
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;
    }

}

编译器输出:

代码语言:javascript
复制
> 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:

代码语言:javascript
复制
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;
    }

}

编译器输出:

代码语言:javascript
复制
> 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);
         ^
EN

回答 1

Stack Overflow用户

回答已采纳

发布于 2013-04-08 12:56:41

演示的问题似乎与Oracle bug 6904536中报告的行为相匹配。bug被关闭为“不是问题”,并有以下解释:

javac的行为符合JLS。另请参阅65585516711619和相关的JLS issue 6644562

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

代码语言:javascript
复制
static class Parent {

    private int i;

    void m(Child child) {
        i = child.i; //compile error
    }
}

static class Child extends Parent { }

child.i不可见,因为对私有成员的访问不是继承的。这一点是因为Child可以拥有自己的i,而不需要任何遮蔽:

代码语言:javascript
复制
static class Child extends Parent {
    private int i; //totally fine
}

因此,这将是一个罕见的向上转换是必要的例子:

代码语言:javascript
复制
void m(Child child) {
    i = ((Parent)child).i;
}

因此,考虑到Foo<V extends Foo<V>>中的V不一定是Foo<V>,而是可以是某种扩展Foo<V>的类型,因此,由于继承了可访问性,所以JLS在这里似乎是正确的。

票数 18
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/15681444

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档