2017-04-27 8 views
0

何とか別のクラスBまたは他の1にマッピングすることができるクラスAがあります:返される関数に追加のキャストが必要な理由(互換性のない型)?

class A {} 

class B { 
    final A a; 

    B(A a) { 
     this.a = a; 
    } 
} 

引数として渡された第二のクラスタイプに基づいて、別のクラスにAからマッパーを返すマッパーの工場もあります。

class Mapper { 

    static Function<A, B> a2bmapper = B::new; 

    static <R> Function<A, R> findMapper(Class<R> cls) { 
     if(cls == B.class) { 
      return a2bmapper; 
     } 
     return null; 
    }   
} 

問題は、この行に:

return a2bmapper; 

Javaコンパイラで互換性のないタイプが発生する:必須RFound BとIDEがキャストすると、Function<A,R>になります。何故ですか? Rは汎用タイプに過ぎず、Bに置き換えてください。

+4

簡単な答えは、型システムが多くをチェックすることができますが、_everything_をチェックすることはできません。言語仕様とコンパイラに組み込まれた時間と労力の点で、理論的で実用的な制限が作用します。この場合、Javaは、cls == B.class、R == B以降の推移計算を行いません。 – yshavit

答えて

4

意味的には、B.classを渡すときにこのメソッドがFunction<A,B>以外を返す方法はありませんが、コンパイラはこれを実現するほどスマートではありません。例えば、ifの状態に誤って変更された場合、セマンティクスを破るのに十分です。 JLSは、通常、このような状況で注意すべき側面を誤っています。

これは、あなたが望むようにFunction<A,R>に明示的にキャストする必要がある状況です。

5

findMapperFunction<A, R>を返します。Rは何でもかまいません。必ずしもBである必要はありません。

このメソッドをString.classとしましょう。今すぐRStringです。この関数はFunction<A, String>を返しますが、代わりにFunction<A, B>を返しています。コンパイラはこの可能性を見て、あなたにノーと言います。

"でも、返信前にRBかどうか確認しました。あなたは叫んだ。さて、このチェックは実行時に行われ、コンパイラはそれほど気にしません。

タイプ消去のため、すべての汎用パラメータは実行時にただObjectです。そのため、この問題を解決するにはFunction<A, B>にキャストすることができます。

3

ジェネリックは純粋にJavaのコンパイル時のものです。コンパイラはジェネリックを使用して、コードがコンパイル時にタイプセーフであるかどうかをチェックします。

しかし、コンパイラが行うチェックには制限があります。コンパイラが行っているチェックは、コンパイラがifステートメントを分析して、returnステートメントの時点でRが常にBに等しいと結論づけるまで行っていません。

ifステートメントよりも複雑な場合 - コンパイラがコード内のすべての可能なパスを分析し、安全だと判断するでしょうか?論理は任意に複雑になる可能性があります。

0

私は悪い英語を残念に思っていますので、可能な限り多くの例を取ります。私はあなたが私の意味を理解することができればいいと思う。

パラメータタイプがObjectの場合の簡単な例を示します。

String string(Object value){ 
    return value instanceof String ? value : null; 
} 

まだvalueの参照型がObjectあるので、ダウンキャストStringに必要な上記の例:

String string(Object value){ 
    return value instanceof String ? (String)value : null; 
} 

その後、我々は一般的な引数を持つ例を拡大あなたはStringの型をTに強制的にキャストすることで解決できます。バインドされたTStringクラスの型でない場合、実行時にキャストが失敗します。

<T,R> T string(R value){ 
    // the code cast R to T will generate a compile unchecked warnings. 
    return value instanceof String ? (T) value : null; 
} 

// the code is ok on compile stage, but will throw a ClassCastException on runtime. 
Date date= string("bad"); 
// the code is ok both on compile & runtime. 
// because a unbounded generic argument which will reference to Object . 
string("ok"); 

、あなたがFunction<T,R>に関数をキャストすることによって、あなたのコードを解決することができます上記の例のベース:

static <R> Function<A, R> findMapper(Class<R> cls) { 
    if (cls == B.class) { 
     return (Function<A, R>) a2bmapper; 
    } 
    return null; 
} 

我々は、一般的なパラメータRBである知っているが、まだ未確認のコンパイルの警告を生成コンパイラが知りません。コンパイラにそれを知らせるようにすることができます:

static <R> Function<A, R> findMapper(Class<R> cls) { 
    if (cls == B.class) { 
     return a2bmapper.getClass().cast(a2bmapper); 
    } 
    return null; 
} 
関連する問題