2016-06-15 6 views
2

私は、関数を2つのインタフェースを実装することが保証されているオブジェクトを返すようにしたいと思います。正確なオブジェクトは、コンパイル時に必ずしも知られているわけではありません。私は次のエラーを取得するコンパイルしようとするとジェネリック型の戻りの交差

class HelloWorld { 

    public interface A {} 
    public interface B {} 

    public static class C implements A, B {} 
    public static class D implements A, B {} 

    public static <T extends A & B> void g(T t) {} 

    public static <T extends A & B> T f(boolean b) { 
    if (b) 
     return new C(); // Doesn't compile 
    return new D(); // Doesn't compile 
    } 

    public static void main(String []args){ 
    g(f(true)); 
    g(f(false)); 
    <what_should_I_write_here> x = f(<user_inputted_boolean>); 
    } 
} 

:私のコードは次のように見えますが、二つの異なる機能の種類とCを返すことができませんので

HelloWorld.java:13: error: incompatible types: C cannot be converted to T
return new C();
^
where T is a type-variable:
T extends A,B declared in method f(boolean)
HelloWorld.java:14: error: incompatible types: D cannot be converted to T
return new D();
^
where T is a type-variable:
T extends A,B declared in method f(boolean)

これは動作しませんし、 Dは異なるタイプです。

上記のコードをコンパイルする方法はありますか?

答えて

4

あなたは型変数に関する基本的な誤解があります。もし

public static <T extends A & B> T f(boolean b) { … } 

ような方法を宣言するときには、発信者は、実際の型(または呼び出し元のコンテキストの型変数)を割り当てることができるTれる変数タイプを宣言する。例えば、呼び出し側は、次の操作を行うことがあります。

コンパイラが受け入れる
class SomethingCompletelyUnknownToF implements A,B {} 

SomethingCompletelyUnknownToF var = f(trueOrFalse); 

fの呼び出し側によってTために使用されるタイプSomethingCompletelyUnknownToFはタイプがAまたはBを実装しなければならない制約を満たしているため。もちろん、これは実行時には失敗します.CDSomethingCompletelyUnknownToFに割り当てられないためです。実際、fは、それが知らない特定の型を返すというこの期待を満たすことは不可能です。要約すると、型変数は、メソッドが型を割り当てる可能性のある変数ではなく、型変数は、によって選択された型のプレースホルダです。

呼び出し側がTのために選択した実際のどんなタイプ、それがパラメータとして渡されるときABを実現する方法の期待を果たしていきますようにメソッドのシグネチャ

public static <T extends A & B> void g(T t) { … } 

は、より理にかなっています。もちろん、gABを実装する全く知られていないタイプかもしれないので、それがDまたはCのいずれかであるとは期待できません。

しかし、戻り値の型は2つの型(両方を拡張する具体的な型の宣言以外)を拡張することをJavaでは表現する方法はありません。 RandomAccessの場合は、結果がないので、とにかく気にする必要はありません。返されたListがこのマーカーインタフェースを実装することが保証されるとき、JREクラスは決して宣言しないことに注意してください。これはパラメータ型としても決して期待しないことで対応されます。同様に、Serializableはどこでもリターンまたはパラメータ型として決して宣言されません。

+0

ありがとうございました。私の問題は、ランタイムO(n^2)を作る方法でユーザが私の関数を使用しないコンパイル時の保証であるため、関数を 'RandomAccess'' Lists'だけを受け入れるものとして宣言したいということから始めました。このような形でユーザーが誤って自分の機能を呼び出すことは望ましくありません。現在、私は与えられたリストが 'RandomAccess'のinstanceofであることをランタイムチェックしています。私はこれをコンパイル時のチェックに変更したい。残念ながら、私の関数に渡されるリストは、2番目の関数によって作成されます。この関数は常に 'RandomAccess List'を返しますが、必ずしも同じ具体的な型であるとは限りません。 –

+0

もう1つの質問を見ましたが、そこに回答を追加します。この答えで言われているように、JREクラスは戻り値型で 'RandomAccess'を宣言していないことに注意してください。 'Arrays.asList'の結果は、適切であり、実行時に' RandomAccess'を実装するという事実にもかかわらず、コンパイル時にプロパティを強制するときメソッドによって拒否されます。 – Holger

1

私はホーゲルが言う通り、their answerに同意します。しかし、トリッキーなタイプがとても面白いからといって、ひとつ追加したいのです。私はあなたがするようをいただきたいものを信じる

はこのように、f交差点戻り値の型を持つことです。

public static A & B f(boolean b) 

その後fABの両方を実装して、いくつかのオブジェクトを返します。これにより、メソッド型チェックのreturn文が作成され、その戻り値をAまたはB変数に代入できます。

もちろん、これは許可されていません。Javaの交差型は、型変数でのみ使用できます。

あなたはこれを試すことができます:ここで

public static Optional<? extends A & B> f(boolean b) 

それはOptionalの型パラメータをインスタンス化するため、交差点の種類は大丈夫です。ただし、ワイルドカードは1つしかバインドできないため、これは許可されません。&は使用できません。


Ceylonファーストクラスの交差点と組合の種類をサポートしています非常に興味深いプログラミング言語です。上記の例をセイロンに書くことができます。

それを確認してください、そのは本当に興味深い!

関連する問題