为什么这个代码在Java 1.6中编译而不是在Java 1.7中编译? [英] Why does this code compile in Java 1.6 but not in Java 1.7?

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

问题描述

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



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

我不是询问如何解决这个问题。我已经做到了。我试图找到解释为什么这不再起作用。想到三种可能性:根据JLS,该代码是 NOT LEGAL ,不应该编译( 1.6编译器中存在一个bug,在1.7版本中修复)

  • 根据JLS,此代码为 LEGAL ,应编译(已引入向后兼容性错误进入1.7编译器)

  • 这段代码属于JLS中的 grey area



  • Foo.java:

      import java.util.TreeMap; 
    import java.util.Map;

    public abstract class Foo< V extends Foo>> {

    private final Map< String,Object> data = new TreeMap< String,Object>();

    保护Foo(){; }

    //子类应该实现这个'return this;'
    public abstract V getThis();

    //子类应该实现这个'return new SubclassOfFoo();'
    public abstract V getEmpty();

    // ...更多方法在这里...

    public V copy(){
    V x = getEmpty();
    x.data.clear(); //不会在Java 1.7中编译
    x.data.putAll(data); //
    return x;
    }

    }



    编译器输出:

     > c:\tools\jdk1.6.0_11\bin\javac  - 版本
    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:错误:数据在Foo中有私人存取
    x.data.clear();
    ^
    Foo.java:19:错误:数据在Foo中有私人存取
    x.data.putAll(data);
    ^
    2错误

    附录:如果引用是私有方法而不是私有成员变量,则会出现同样的问题。 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(){; }

    //子类应该实现这个'return this;'
    public abstract V getThis();

    //子类应该实现这个'return new SubclassOfFoo();'
    public abstract V getEmpty();

    // ...更多方法在这里...

    public V copy(){
    V x = getEmpty();
    x.theData()。clear(); //不会在Java 1.7中编译
    x.theData()。putAll(data); //
    return x;
    }

    private Map< String,Object> theData(){
    返回数据;
    }


    $ / code>

    编译器输出:

     > 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:错误:theData()在Foo2中有私人存取$ b $ ()函数在foo2
    中有私人存取x.theData()。putAll(data) ;
    ^


    解决方案

    Oracle bug 6904536 中报告的行为相匹配。该错误因下列情况而被封为不是问题解释:


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


    相应的JLS问题尚未解决,有以下评论:


    类型变量成员的简单解释是欢迎
    。类型
    变量边界的私有成员存在一般难度。形式上这样的成员本身并不会成为
    类型变量的成员,尽管javac和Eclipse传统上使它们成为
    成员,并且代码依赖于此:

      class Test {
    private int count = 0;
    < Z延伸测试> void m(Z z){
    count = z.count; //在javac 1.6中合法,由于修复为6711619
    }
    }


    $ b而导致在javac 1.7中非法$ b

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

      class A {
    static class B {private String f; }

    抽象静态类生成器< T extends B> {
    abstract T getB();

    {
    ((B)getB())。f.hashCode();
    getB()。f.hashCode(); //错误:f在AB
    }

    }
    }

    $ b $中有私人存取b

    由于交集类型是通过继承构造的,并且私有
    成员永远不会被继承,因此重新指定交集
    类型以拥有私有成员是非常棘手的。尽管如此,这将是兼容的
    事情。


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



    编辑:



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

     静态类父类{

    private int i;

    void m(小孩){
    i = child.i; //编译错误
    }
    }

    static class Child extends Parent {}

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

      static class Child extends Parent {
    private int i; //完全正确的
    }

    所以这是一个很少见的 upcasting



      void m(Child child){
    i =((Parent)child)。一世;





    $ b

    因此,通过图片中的继承可访问性,JLS在这里似乎是正确的,在 Foo V 延伸Foo V> 不一定 Foo< V> ,但可以是一些延伸 Foo< V> 的类型。


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

    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:

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

    Compiler output:

    > 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
    

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

    Compiler output:

    > 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);
             ^
    

    解决方案

    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 is behaving according to the JLS. See also 6558551, 6711619 and related JLS issue 6644562.

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

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

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

    EDIT:

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

    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天全站免登陆