为什么这段代码可以在 Java 1.6 中编译,而不能在 Java 1.7 中编译? [英] Why does this code compile in Java 1.6 but not in Java 1.7?

查看:32
本文介绍了为什么这段代码可以在 Java 1.6 中编译,而不能在 Java 1.7 中编译?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

以下代码在 Java 1.6 中编译良好,但在 Java 1.7 中编译失败.为什么?

The following code compiles fine in Java 1.6 but fails to compile in Java 1.7. Why?

代码的相关部分是对私有数据"字段的引用.该引用来自定义该字段的同一个类,因此看起来是合法的.但它是通过泛型变量发生的.此代码 - 基于内部库中的类的精简示例 - 在 Java 1.6 中有效,但现在在 Java 1.7 中无效.

The relevant part of the code is the reference to the private 'data' field. The reference is from within the same class in which the field is defined, and so seems legal. But it is happening via a generically-typed variable. This code - a stripped down example based on a class from an in-house library - worked in Java 1.6 but doesn't now in Java 1.7.

我不是在问如何解决这个问题.我已经这样做了.我正在尝试解释为什么这不再起作用.我想到了三种可能性:

I'm not asking how to work around this. I've already done that. I'm trying to find an explanation of why this doesn't work any more. Three possibilities come to mind:

  • 根据 JLS,此代码不合法,不应编译(1.6 编译器中存在错误,已在 1.7 中修复)
  • 根据 JLS,这段代码是 LEGAL 并且应该可以编译(向后兼容错误已被引入 1.7 编译器)
  • 此代码属于 JLS 中的灰色区域
  • This code is NOT LEGAL according to the JLS and should never have compiled (there was a bug in the 1.6 compiler, fixed in 1.7)
  • This code is LEGAL according to the JLS and should compile (a backward compatibility bug has been introduced into the 1.7 compiler)
  • This code falls into a GREY AREA in the 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:	oolsjdk1.6.0_11injavac -version
javac 1.6.0_11

> c:	oolsjdk1.6.0_11injavac c:	empFoo.java

> c:	oolsjdk1.7.0_10injavac -version
javac 1.7.0_10

> c:	oolsjdk1.7.0_10injavac c:	empFoo.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.

Addendum. The same problem occurs if the reference is to a private method instead of a private member variable. This works in Java 1.6 but not in 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:	oolsjdk1.6.0_11injavac c:	empFoo2.java

> c:	oolsjdk1.7.0_10injavac c:	empFoo2.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);
         ^

推荐答案

所演示的问题似乎与 Oracle 错误 6904536.该错误已被关闭为不是问题",并提供以下解释:

The demonstrated issue seems to match the behavior reported in Oracle bug 6904536. The bug was closed as "Not an Issue" with the following explanation:

javac 的行为符合 JLS.另请参见 65585516711619 和相关的 JLS 问题6644562.

javac is behaving according to the JLS. See also 6558551, 6711619 and related JLS issue 6644562.

对应的JLS问题未解决,注释如下:

The corresponding JLS issue is unresolved, with the following comment:

对类型变量的成员资格的简化解释是欢迎.类型的私有成员存在普遍困难变量的边界.正式的此类成员不会成为类型变量本身,虽然 javac 和 Eclipse 传统上使它们成员和代码已经开始依赖它:

A simplified explanation for the membership of type variables is welcome. There is a general difficulty with private members of a type variable's bounds. Formally such members do not become members of the type variable itself, though javac and Eclipse traditionally made them members and code has come to rely on that:

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

Peter 提交了一个类似的测试:

Peter submitted a similar test:

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
    }

  }
}

由于交集类型是通过继承构造的,私有的成员永远不会被继承,重新指定交集很棘手类型以拥有私有成员.尽管如此,它将是兼容的要做的事情.

Since intersection types are constructed by inheritance, and private members are never inherited, it is tricky to re-specify intersection types to have private members. Nonetheless, it would be the compatible thing to do.

作为参考,JLS 的相关部分是 §4.4.

For reference, the relevant section of the JLS is §4.4.

实际上我倾向于同意 JLS,因为当我们从图片中删除泛型时,它与自身匹配.考虑这个例子:

I tend to agree with the JLS here actually, because it matches up with itself when we remove generics from the picture. Consider this example:

static class Parent {

    private int i;

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

static class Child extends Parent { }

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

child.i isn't visible because access to private members isn't inherited. This point is driven home by the fact that Child can have its own i without any shadowing:

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

所以这将是一个罕见的向上转换的例子:

So this would be a rare example of upcasting being necessary:

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

因此,由于继承了可访问性,JLS 在这里似乎是正确的,因为 Foo> 中的 V 不是必须 Foo 但可以是扩展 Foo 的某种类型.

So with inherited accessibility out of the picture, the JLS seems correct here, given that the V in Foo<V extends Foo<V>> isn't necessarily Foo<V> but could be some type that extends Foo<V>.

这篇关于为什么这段代码可以在 Java 1.6 中编译,而不能在 Java 1.7 中编译?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆