Java:循環ジェネリック型リレーションはスーパータイプからのキャストを許可しません(javacバグ)

StackOverflow https://stackoverflow.com/questions/804247

  •  03-07-2019
  •  | 
  •  

質問

Javaコンパイラのまったく奇妙な動作に遭遇しました。
循環ジェネリック型の場合、スーパータイプをサブタイプにキャストできません 関係が関係しています。

JUnitテストケースで問題を再現します:

public class _SupertypeGenericTest {

    interface ISpace<S extends ISpace<S, A>, A extends IAtom<S, A>> {
    }

    interface IAtom<S extends ISpace<S, A>, A extends IAtom<S, A>> {
    }

    static class Space
            implements ISpace<Space, Atom> {
    }

    static class Atom
            implements IAtom<Space, Atom> {
    }

    public void test() {
        ISpace<?, ?> spaceSupertype = new Space();
        IAtom<?, ?> atomSupertype = new Atom();

        Space space = (Space) spaceSupertype;  // cast error
        Atom atom = (Atom) atomSupertype;  // cast error
    }
}

コンパイラエラー出力:

_SupertypeGenericTest.java:33: inconvertible types
found   : pinetag.data._SupertypeGenericTest.ISpace<capture#341 of ?,capture#820 of ?>
required: pinetag.data._SupertypeGenericTest.Space
                Space space = (Space) spaceSupertype;
                                    ^

_SupertypeGenericTest.java:34: inconvertible types
found   : pinetag.data._SupertypeGenericTest.IAtom<capture#94 of ?,capture#48 of ?>
required: pinetag.data._SupertypeGenericTest.Atom
                Atom atom = (Atom) atomSupertype;
                                ^
2 errors

注:Netbeansの最新のトランク、バンドルされたAnt、最新のJava 6リリースを使用しています。
コマンドラインからAntを使用してみました(Netbeansはbuild.xmlファイルを生成します) ただし、同じエラーが発生します。

何が問題ですか?
問題を解決するエレガントな方法はありますか?

奇妙なことに、Netbeansはエラーをマークしません(警告すらありません)。 指定されたコードで。

編集:
いいえ、今では何もはわかりません!
Eclipse 3.4.1は警告もエラーもマークせず、コンパイルします 問題なくコード!!!
どうすればいいの?コマンドラインからAntを使用して Netbeansが提供するbuild.xmlは中立です。
何かが足りないですか?

編集2:
JDK7 ライブラリとJDK7コード形式を使用すると、netbeansは エラー/警告!
(1.7.0-ea-b55を使用しています)

編集3:
タイトルを変更して、javacのバグに対処していることを示します。

役に立ちましたか?

解決

これらの複雑なジェネリック型を簡単に理解できるとは言いませんが、javacでコンパイルされ、ecj(eclipseコンパイラ)でコンパイルされないコードを見つけた場合は、両方の< a href = "http://bugs.sun.com/" rel = "nofollow noreferrer"> Sun および Eclipse を使用して、状況を明確に説明します(バグレポートを提出し、それぞれのURLについても言及するのが最善です。ただし、Sunの場合、バグが公開されるまでしばらく時間がかかる場合があります)。

過去にそれをやったことがありますが、本当に良い反応がありました

  1. チームの1つが正しいアプローチが何であるかを理解しました(コンパイルエラー、警告、またはなし)
  2. および障害のあるコンパイラが修正されました

両方のコンパイラーが同じ仕様を実装しているため、どちらか一方のみがコードをコンパイルする場合、定義上どちらか一方が間違っています。

記録用:

.class(javac 1.6.0_13)および<=>(Eclipse Java Compiler 0.894_R34x、3.4.2リリース)を使用してサンプルコードをコンパイルしようとしましたが、<=>は大声で文句を言い、<=>を生成できませんでした<=>は一部の未使用の変数(警告)についてのみ文句を言い、予想されるすべての<=>ファイルを生成しました。

他のヒント

このために非ジェネリックを使用することになりました:

    @Test
    public void test() {
            ISpace spaceSupertype = new Space();
            IAtom atomSupertype = new Atom();

            Space space = (Space) spaceSupertype;  // ok
            Atom atom = (Atom) atomSupertype;  // ok
    }

ワイルドカードなしでタイプを使用することを妨げるものは何ですか?

public void test() {
    ISpace<Space, Atom> spaceSupertype = new Space();
    IAtom<Space, Atom> atomSupertype = new Atom();

    Space space = (Space) spaceSupertype;  // no error
    Atom atom = (Atom) atomSupertype;  // no error
}

そのように読むと、はるかに明確になり、さらにコンパイルして実行されます:) これは<!> quot;問題を解決するエレガントな方法<!> quot;

だと思います

問題は、IAtom<?, ?>AtomAtom<Atom, Space>)にキャストしようとしていることです。システムは、?がAtom and Spaceであるかどうかを知っているはずです。

ジェネリックが満たされる場所に固執するタイプがわからない場合は、通常、次のように全体をそのままにします。

ISpace spaceSupertype = new Space();

コンパイラエラー(エラーではない)が生成されますが、コードは引き続き実行されます(実際の型がキャスト互換でない場合、ランタイムエラーが発生します)。

しかし、この全体を見るのは意味がありません。あなたは強い型付けが必要だと言ってから、型の行き先にIAtomを付けます。その後、振り向いてキャストしようとします。

それらをSpaceとAtomにキャストできるようにする必要がある場合は、おそらくそれらを最初から使用する必要があります。最終的にそれらの変数に他の型を固定するためにできない場合、多くのif / thenステートメントを使用しない限り、ランタイム型を変更するとコードは完全に壊れますこのコメントの終わり)。

しかし、この奇妙なことをしているのなら、私たちは貧弱なコード設計を見ていると思います。これをどのように構成しているかを考え直してください。ここにある機能を実現するために、他のクラスまたはインターフェースが必要な場合があります。

自問してください、<!> quot;強い型は本当に必要ですか?何が得られますか?<!> quot;インターフェイスを使用しているので、フィールドを追加しない方が良いでしょう。 (フィールドがメソッドを介してのみアクセスされる場合、パブリックインターフェイスにメソッドを追加するだけであることに注意してください。)メソッドを追加する場合、ここで単一のインターフェイスISpaceおよびtest()を使用するのは悪い考えです。とにかくそのサブタイプではIAtom2しか使用できません。 instanceofは、<=> / <=>の他の実装に一般化しません。ここに配置するすべての実装が<=>に必要なメソッドと同じものを持っているが、<=> / <=>のすべての実装にそれらがあるわけではない場合、中間サブインターフェースが必要です:

public interface IAtom2 extends IAtom
{
    [additional methods]
}

次に、<=>で<=>の代わりに<=>を使用できます。次に、必要なタイピングを自動的に取得し、ジェネリックは必要ありません。クラスのセットがすべて共通のパブリックインターフェイス(メソッドとフィールドのセット)を持っている場合、そのパブリックインターフェイスはスーパータイプまたはインターフェイスの有力な候補であることを忘れないでください。私が説明しているのは、平行四辺形、長方形、正方形の関係のようなもので、長方形の部分をスキップしようとしているところです。

再設計を行わない場合、もう1つの考え方は、循環ジェネリックを削除し、<=>:

を介してインスタンスのテストを行うことです。
if (spaceSupertype instanceof Space)
{
    Space space = (Space)spaceSupertype;
    ...
}
else
...
ライセンス: CC-BY-SA帰属
所属していません StackOverflow
scroll top