2017-02-11 11 views
7

Iている以下のクラス:同じイレーズバイナリを持つリターンタイプジェネリックは互換性がありますか?

public abstract Foo { 
    Foo() {} 

    public abstract Foo doSomething(); 

    public static Foo create() { 
    return new SomePrivateSubclassOfFoo(); 
    } 
} 

が、私は次の定義にそれを変更したい:この変更は、バイナリ互換性

public abstract Foo<T extends Foo<T>> { 
    Foo() {} 

    public abstract T doSomething(); 

    public static Foo<?> create() { 
    return new SomePrivateSubclassOfFoo(); 
    } 
} 

ですか? つまり、reocmpilationせずにクラスの古いバージョンに対してコンパイルされたコードは新しいバージョンで動作しますか?

私はSomePrivateSubclassOfFooを変更する必要があることを知っていますが、これは問題ありません。また、古いクライアントコードがコンパイルされたときに、この変更によって生のタイプに関する警告が表示されることもわかります。私は、古いクライアントコードを再コンパイルする必要がないことを確認したいだけです。

Tの消去がFooであり、したがって、バイトコード内のdoSomethingの署名が以前と同じであるため、これは問題ありません。 javap -sで印刷された内部型署名を見ると、確かにこれが確認されています(-sを付けずに印刷された「非内部型」署名は異なります)。 私もこれをテストし、それは私のために働いた。

しかし、Java API Compliance Checkerは、2つのバージョンがバイナリ互換ではないことを示しています。

だから何が正しいですか? JLSはここでバイナリ互換性を保証していますか、私のテストでは幸運でしたか? (なぜこれが起こるのでしょうか)

答えて

4

あなたのコードはバイナリ互換性を壊すようには見えません。
私はこれらを発見したいくつかのクロールの後に/言うどの
http://docs.oracle.com/javase/specs/jls/se8/html/jls-13.html#jls-13.4.5 を読んで: -

をクラスの型パラメータを追加または削除すると、それ自体が、バイナリ互換性のためにどんな意味を持っていません。
...
クラスの型パラメータの最初の境界を変更すると、その型パラメータをその型で使用するメンバの消去(4.6)が変更され、バイナリ互換性に影響する可能性があります。このような境界の変更は、メソッドまたはコンストラクタの型パラメータの最初の境界の変更(13.4.13節)に似ています。

そしてこのhttp://wiki.eclipse.org/Evolving_Java-based_APIs_2#Turning_non-generic_types_and_methods_into_generic_onesさらに明確化: - 特殊な互換性の話によると

、Javaコンパイラは、型の消去を基準として、生タイプを扱います。既存の型は、型パラメータを型宣言に追加し、型変数の使用を既存のメソッドおよびフィールドのシグネチャに慎重に導入することによって、汎用型に発展させることができます。一般化の前に対応する宣言のように消去が見える限り、変更は既存のコードとバイナリ互換です。

このクラスを初めて生成するので、今のところ問題はありません。

しかし、上記のドキュメントも言うように、心に保管してください: - もすでに一般的なものでありタイプまたはメソッドは互換進化させることができる方法についての厳しい制約があることを心に留め、

をしかし、 (上記の表を参照してください)。したがって、APIを生成する場合は、正しいチャンスを得るためにチャンス(リリース)を1つだけ取得することを覚えておいてください。具体的には、API署名のタイプを生のタイプの「リスト」から「リスト<?>」または「リスト<オブジェクト>」に変更すると、その決定に固定されます。既存のAPIを生成することは、メソッドごとまたはクラスごとに分割するのではなく、API全体の観点から考慮する必要があることです。

だから私はこの変更を非常に初めてと思っていますが、あなたは1チャンスしか持っていないので、それを十分に活用してください!

関連する問題