2013-01-31 10 views
8

なぜユーティリティファクトリメソッドは、(? super Tのような)ワイルドカードパラメータの代わりに特定の汎用パラメータ(Tなど)を使用するのはなぜですか?例えばグアバでは、なぜ "?super T"が可能なところで "T"だけが使用されますか?

Functions#forPredicateのシグネチャは次のとおりです。

public static <T> Function<T, Boolean> forPredicate(Predicate<T> predicate) 

なぜ使用しない:

public static <T> Function<T, Boolean> forPredicate(Predicate<? super T> predicate) 

を以下の可能性のようなものを作ることになりますか? 消費者FunctionPredicateは、この不要なを作るために必要な有界ワイルドカードパラメータを持つことが期待されているので、それは

Predicate<Number> isPositivePredicate = ... 
Function<Integer, Boolean> isPositiveInteger = Functions.forPredicate(isPositivePredicate); 
// above line is compiler error: 
// Type mismatch: cannot convert from Function<Number,Boolean> to Function<Integer,Boolean> 

ですか?例えば、Iterables#find上の一般的な境界がPredicate<Number>Iterable<Integer>上で使用することができるようになる:

public static <T> T find(Iterable<T> iterable, 
         Predicate<? super T> predicate) 

が他の理由はありますか?

答えて

6

はい、それは私たちは、消費者が右有界ワイルドカードのパラメータを持つことを期待することを絶対的に正確だが、夫婦の追加理由が心に春:

一般に
  • 、我々は一般的な方法の種類を広げていません私たちに特別な理由があるまで。この方針は何度か功を奏しました。
  • Javaの型推論は、より高度なジェネリックを自動的に把握するのに必ずしも適しているわけではないので、より狭いジェネリックを維持すると、明示的にTを指定する必要があるユーザーの数が減ります。
+0

re。最初のポイントは、消費者をキャストするだけで、2つの悪の悪いところです。また好奇心が強い、あなたが「我々」と言うときは、あなたがこのソフトウェアコミュニティで開発したのか、それとも一般的なソフトウェアコミュニティで開発したのかを意味します。 – djechlin

+0

具体的には、この方針はどのように実践していますか?現時点では、私がそれが重要であると想像できる唯一の状況は、消費者は派生型へのキャスティングをバイパスするだけです。 – djechlin

+0

私が「私たち」と言うとき、私はGuavaを開発するGoogle Java Core Librariesチームにいることを意味します。しかし、消費者は絶対にキャストする必要はありません: 'Predicate 'は 'Predicate 'を使う場所であればいつでも直接使うことができます。キャストする必要はありません。正確に同じ値に適用することができます。 –

2

find()の例では、Tは必ず明白に推測できます。

forPredicate[1]()の例では、Tも明白に推測できます。

forPredicate[2]()の例では、Tには不確実性があります。メソッドの結果がターゲット・タイプに割り当てられている場合は、ターゲット・タイプからTを判別できます。そうでなければ小さな頭で傷つくでしょう:

forPredicate(isPositive).apply(42); // T is a subtype of Number, but which one? 

java5/6ではコンパイルしないでください。 (よく私はJava 6でそれをテストしてコンパイルしますが、java 6もコンパイルするので、おそらくバグでしょう)

Java 7は少し改善され、新しいルールはT=Numberとなります。それは機能しますが、それはそれのために仲裁のように感じます。


理想的には、ワイルドカードについて心配する必要はありません。私の整数の述語が必要な場合は、Predicate<Integer>というパラメータを宣言する必要があります。引数がPredicate<Number>でも受け入れられるという事実は別の話です。 Predicate<Number>Predicate<Integer>に変換するのはコンパイラの仕事でなければなりません。既存のJavaジェネリック型システムを見直すことなく実行できます。新しい変換ルールはすべて必要です。一つは、また

Predicate<Number> pn = ...; 
Predicate<Integer> pi = convert(pn); 

Iterable<Integer> iter1 = ...; 
Iterable<Number> iter2 = convert(iter1); 

すべてconvert()方法は、機械的に生成することができ、変換ライブラリを提供することができます。


Java 8では少し簡単です。我々はまだ

Predicate<Number> pn = n -> n.intValue()>0; 
Predicate<Integer> pi = pn; // error! 

を行うことはできませんが、我々はまた、

f = forPredicate[2](pn)と同等です
Function<Integer, Boolean> f = pn::test; // ok 

Predicate<Integer> pi = pn::test; // ok 
// it means  pi = (Integer i)->pn.test(i) 

を行うことができます。 Java 8では、関数型間の変換にはほとんどの場合、forPredicate()などが必要です。

+0

forPredicate(isPositive).apply(42) 'に問題はありませんこの場合、—であるが、「T」は最も具体的な型であり、「番号」である。 'Integer'は' Number'から継承しているので、 'apply'呼び出しは問題ありません。文字列を渡すときにコンパイルしていると、rawtypesなどでコンパイルされたような音がします。 – matts

+0

私の考えは:自分のプライベート '述語'を私のクラスに持っていて、 'Function 'としてメソッドから戻したいとします。呼び出し元にそれを公開する必要はありません本当に 'Predicate 'です。また、すべての 'Number'を受け入れるので、' Integer'もすべて受け入れます。これを行うには、私はラッパークラスまたは明示的なクラスで変換する必要があります。 – matts

+0

政治的に正しい答えはあなたのメソッドの戻り値の型は 'Function <?スーパー整数、ブール値> 'を使用します。これはあまりにも醜いです。 – irreputable

関連する問題