2017-06-08 6 views
3

私は次のコードを持っています。私は思っているとおりに動作していません。私は、コメントがインラインで、何が起きているのか、そして何が期待されているのかを入れました。ジェネリックと継承のコーナーケース

class C<T> { 
    void m(T arg) { 
    } 
} 

interface I { 
    void m(Class arg); 
} 

class D extends C<Class<String>> implements I { 
} 
// expected : error -- conflicting inherited methods 
// actual: error -- abstract method not overridden 

abstract class E extends C<Class<String>> implements I { 
} 
// expected : error -- conflicting inherited methods 
// actual: no error, but no bridge method 

この動作を理解できたら助けてください。

+2

まあクラスソリューションはI 'I 'にタイプを追加し、私に(クラスを ''持つMにおそらくある

+0

クラスと同じ型ではありません。この場合は

答えて

1

私は、ステップバイステップで、何が起こっているかを説明しよう:

クラスDは、クラスCの定義のようにコンパイル時に署名void m(Class<String> arg)と方法を紹介しC<Class<String>>を拡張します。しかし、タイプ消去のために、型パラメータStringに関する情報は、コンパイル後に消去されます。したがって、バイトコードはCで定義されるメソッドvoid m(Class arg)を作成します。また

、クラスDは、コンパイル時に起因タイプパラメータに方法m(Class<String> arg)異なる方法m(Class arg)を実装するDを必要インターフェイスIを実装します。したがって、以前のメソッドの実装を提供するか、D抽象を宣言しなければなりません。

型消去が適用された後、両方が同じシグネチャを持つことになりますので、あなたは、あなたが実際にクラスCによって定義されたメソッドをオーバーライドIインタフェースによって宣言されたメソッドm(Class arg)の実装を提供する場合。

クラスを作成した場合の変更点はC<Collection<String>>です。この場合、クラスCは、タイプ消去後にバイトコードとしてvoid m(Collection args)に変更される、コンパイル時に署名void m(Collection<String> arg)のメソッドを導入します。しかし今やインターフェイスIによって宣言されたメソッドvoid m(Class arg)は異なる署名を持っているのでオーバーライドされませんが、オーバーヘッドメソッドmはクラスCで定義されます。ここで

簡単なJavaの例:

class C<T> { 
    void m(T arg) { 
     System.out.println("Method [m] of class [C] called"); 
    } 
} 

class D extends C<Class<String>> implements I { 
    @Override 
    public void m(Class arg) { 
     System.out.println("Method [m] of class [D] called"); 
    } 
} 

class DD extends C<Collection<String>> implements I { 

    @Override 
    public void m(Class arg) { 
     System.out.println("Method [m] of class [DD] called"); 
    } 
} 

、ここで呼び出しのシーケンスと出力:

public static void main(String[] args) { 
    new D().m(D.class); 
    new D().m((Class<String>) null); 

    new DD().m(DD.class); 
    new DD().m(new ArrayList<String>()); 
} 

出力

Method [m] of class [D] called 
Method [m] of class [D] called 
Method [m] of class [DD] called 
Method [m] of class [C] called. 
0

Dは、デフォルトメソッドがないため、Iから何も継承しません。実際に実装を提供していないメソッドシグネチャだけです。 @RCはあなたのmのタイプが有効であるとはみなされないと指摘しています。競合に関するエラーを取得するには、実際に競合する実装を追加する必要があります。

Eでは、クラスを抽象としてマークしているため、インターフェイスで必要に応じてメソッドを実装しても問題ありません。 Eを拡張するすべてのクラスで行うと、型の消去後に型が同一になるため、実装が競合するというエラーが発生します。基本的には、問題をからEに広げようとしています。

0

jdk8に次のディスカッションがあります。

class D extends C<Class<String>> implements I { 

}

は次のようにインターフェイスIを実装する必要があります:

class D extends C<Class<String>> implements I { 

    @Override 
    public void m(Class arg) { 

    } 
} 

は.AccordingインタフェースIでメソッドを実装する必要がある理由を見てみましょうあなたのコードで

JLS8.4.8.1

An instance method mC declared in or inherited by class C, overrides from C another 
method mI declared in an interface I, iff all of the following are true: 
• I is a superinterface of C. 
• mI is an abstract or default method. 
• The signature of mC is a subsignature (§8.4.2) of the signature of mI. 
上記のコードで

mCは、クラスDvoid m(Class<String> arg)あるmIvoid m(Class arg)

あり、subsignatureの仕様を参照:

And the `JLS8.4.2` explain the subsignature between two method: 

The signature of a method m1 is a subsignature of the signature of a method m2 if 
either: 
• m2 has the same signature as m1, or 
• the signature of m1 is the same as the erasure (§4.6) of the signature of m2. 

上記の明細書によれば、void m(Class<String> argはのサブシグニチャはありませんvoid m(Class arg)ですが、void m(Class arg)void m(Class<String> argのサブシグニチャです。したがって、クラスDはインターフェイスを実装する必要があります3210を明示的に示します。

はそれを説明するには、次のコードを参照してください。

class C { 
    public void m(Class arg) { 
    } 
} 

interface I<T> { 
    void m(T arg); 
} 

class D extends C implements I<Class<String>> { 

} 

それはOKコンパイルします。ためJLS8.4.8

秒、競合する方法が存在しない理由は、クラスの継承を説明:

A class C inherits from its direct superclass all concrete methods m (both static and instance) of the superclass for which all of the following are true: 
• m is a member of the direct superclass of C. 
• m is public, protected, or declared with package access in the same package as C. 
• No method declared in C has a signature that is a subsignature (§8.4.2) of the signature of m. 

は、上記の仕様によれば、TClass<String>パラメータを持つ方法void m(T arg)void m(Class<String> arg)に変換すること。変換されたメソッドはvoid m(Class arg)に消去されますが、このメソッドはクラスDに存在するため、クラスDはメソッドvoid m(T arg)をクラスCに継承しません。

0

問題はに減少させることができますJavaのジェネリックについてのこの基本的な事実は、instaを割り当てることが安全であるということですジェネリック型を生の型に変換しますが、その逆は真ではありません。メソッドをオーバーライドすると、安全でない変換は許可されません。したがって、Class<String>は生のClass型をオーバーライドできません。

Class a = String.class; //type safe 
    Class<String> b = a; //not type safe 
関連する問題