2013-03-28 18 views
16

次のコードはJava 1.6で問題なくコンパイルできますが、Java 1.7ではコンパイルできません。どうして?なぜこのコードはJava 1.6ではコンパイルされますが、Java 1.7ではコンパイルされませんか?

コードの関連部分は、プライベートの「データ」フィールドへの参照です。参照は、フィールドが定義されている同じクラス内からのものであり、合法的に見えます。しかし、それは一般的に型付けされた変数を介して起こっている。このコードは、社内のライブラリのクラスに基づく抜粋された例で、Java 1.6で動作しましたが、Java 1.7では動作しません。

私はこれを回避する方法を求めていません。私はすでにそれをしています。なぜこれがもううまくいかないのか解明しようとしています。三つの可能性が頭に浮かぶ:

  • このコードではなく法的はJLSに応じてコンパイルされなかったんです
  • このコードは法的である(1.7で修正された1.6コンパイラのバグがありました) JLSに応じてコンパイルすべきである(下位互換性のバグが1.7コンパイラに導入された)
  • このコードはJLS
グレイAREAに落ちます

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の問題は、次のコメントで、未解決です:型変数の会員のために簡単に説明が 歓迎です

。変数のタイプのプライベートメンバーには一般的な問題があります。

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 
    } 

    } 
} 

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

ピーターは同様のテストを提出した:正式なメンバーには、javacのとEclipseが伝統的にそれら メンバーとコードがそれに頼るようになってきた作られたものの、 型変数自体のメンバーになりません。

交差型は継承で構成され、専用の メンバーは継承されないため、交差する タイプをプライベートメンバーに再指定するのは難しいです。それにもかかわらず、互換性のある ものになります。

参考までに、JLSの関連セクションは§4.4です。

EDIT:

私たちは絵からジェネリックを削除すると、それは自分自身でアップ一致するため、実際にここにJLSに同意する傾向があります。この例を考えてみましょう:privateメンバへのアクセスが継承されていないため

static class Parent { 

    private int i; 

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

static class Child extends Parent { } 

child.iは表示されません。継承されたので、

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

と:

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

だから、これはアップキャストが必要であることの稀な例のようになります。この点はChildは、任意のシャドウイングせずに、独自のiを持つことができるという事実によって自宅を駆動され、アクセシビリティが画像から外れている場合、がFoo<V extends Foo<V>>の場合、必ずしもFoo<V>であるとは限りませんが、Foo<V>を拡張するタイプである可能性があります。

+0

Paulに感謝します。はい、答えは6のjavacがJLSに厳密に準拠していない特定のフォームをコンパイルしているようですが、これは例です。 7のjavacがこれを修正しました。特に、プライベートフィールド/メソッドへのアクセスは、同じクラス内であっても*決して許可されてはいけません。 (これは残念ですが、私はJLSの専門家ではありませんが、なぜそうはならないのかすぐにはわかりません)。要するに、JDK 1.6コンパイラにはバグがありました。 – Paul

+1

@Paul問題はありません、興味深い投稿ありがとうございます。興味があれば私の編集を見てください。 –

+4

非常に良い答え!研究をして説明してくれてありがとう。 – Jesse