2016-02-10 5 views
14

で使用し、私は次のコードスニペットに出くわしたとき、下界を理解する:一般的なパラメータが下限であるので問題Java8ストリームにアップ勉強しながら、ラムダと機能インターフェース

Predicate<? super String> predicate = s -> s.startsWith("g"); 

、私はこれがないと考えましたコンパイル。 ObjectがstartsWith()関数を持たないので、ObjectがStringのスーパータイプである場合、Object型を渡すと、Object型を渡す必要があります。しかし、私はそれが何の問題もなく動作するのを見て驚いた。さらに、まだ

、私は上限を取る述語を微調整:

<? extends String>, 

それがコンパイルされないでしょう。

私は上下限の意味を理解していると思っていましたが、明らかに何か不足しています。誰もこのラムダで下限が動作する理由を説明するのを助けることができますか?

+2

'extend'もコンパイルします。 - 述語<?この述語に 'String'を与えることはできません:)また、my [article](http://bayou.io/draft/)も参照してください。ワイルドカード – ZhongYu

+1

http://stackoverflow.com/questions/33085151/bad-return-type-in​​-lambda-expression/33085893#33085893 – ZhongYu

+1

'述語のCapturing_Wildcards.html)<? super String>は 'Object'を渡すことはできません(そしてそうしようとするとコンパイルエラーが発生します)。署名 '述語<?スーパーストリング>は '述語'であることを意味するだけであり、その述語は依然として 'String'入力を処理することができるので、大丈夫です。つまり、述語<?この述語は実際の型に関係なく文字列を消費することができるので、 'Predicate 'は '述語<?スーパーストリング>。 – Holger

答えて

8

Lambda引数の型は正確です。? superまたは? extendsにはできません。これはJLS 15.27.3. Type of a Lambda Expressionでカバーされています。それはグラウンドターゲットタイプ概念(基本的にラムダタイプです)を紹介します。とりわけ、それがあると述べています:

T場合は、ワイルドカードパラメータ化機能インターフェイスタイプであり、ラムダ式が暗黙に型付けされ、その後、地上ターゲットタイプはT.の非ワイルドカードパラメータ§9.9)であります

強調鉱山。あなたが書くときの本質的に

Predicate<? super String> predicate = s -> s.startsWith("g"); 

ラムダタイプはPredicate<String>です。

Predicate<? super String> predicate = (Predicate<String>)(s -> s.startsWith("g")); 

か、その通常の型変換ルールが適用された後、ラムダ式の引数は、コンクリートであるという事実を考えるとさえ

Predicate<String> pred = (Predicate<String>)(s -> s.startsWith("g")); 
Predicate<? super String> predicate = pred; 

Predicate<String>Predicate<? super String>、またはPredicate<? extends String>あるそれは同じです。したがって、Predicate<? super String>Predicate<? extends String>の両方をコンパイルする必要があります。そして、どちらも実際にjavac 8u25、8u45、8u71、ecj 3.11.1で動作します。

+0

ご清聴ありがとうございます。ラウンドされたパラメータでのラムダのデフォルト動作は私の混乱の核心にありました。 – piper1970

7

私はそれをテストしましたが、割り当て自体がコンパイルされました。どのような変更が実際にpredicate.test()に電話できるかどうかです。

説明を省略してプレースホルダGenericClass<T>を使用して説明します。型引数の場合、FooBarとなり、BarBazに拡張されます。

は拡張: あなたがGenericClass<? extends Bar>を宣言するときに、あなたが言っている「私はそのジェネリック型引数が実際にあるかわからないが、それはBarのサブクラスです。」実際のインスタンスは常に非ワイルドカード型の引数を持ちますが、コードのこの部分では、その値が何であるか分かりません。次に、メソッド呼び出しの意味を考えてみましょう。

あなたは何を実際に持っていることはGenericClass<Foo>GenericClass<Bar>のいずれかであることを知っています。 Tを返すメソッドを考えてみましょう。前者の場合、戻り値の型はFooです。後者では、Barです。いずれにせよ、それはBarのサブタイプであり、Bar変数に割り当てるのが安全です。

Tパラメータを持つメソッドを考えてみましょう。 GenericClass<Foo>の場合は、Barを渡すとエラーとなります。BarFooのサブタイプではありません。

したがって、上限を使用すると、汎用の戻り値を使用できますが、汎用のメソッドのパラメータは使用できません。

スーパー:あなたはGenericClass<? super Bar>を宣言するときに 、あなたは「私はそのジェネリック型引数が実際にあるかわからないが、それはBarのスーパークラスです。」と言っています次に、メソッド呼び出しの意味を考えてみましょう。

あなたが実際に持っていることは、GenericClass<Bar>またはGenericClass<Baz>です。 Tを返すメソッドを考えてみましょう。前者の場合は、Barを返します。後者では、BazBazが返された場合、その値をBar変数に代入するとエラーになります。あなたはそれがどれなのかわからないので、安全に何もここで取ることはできません。

Tパラメータを持つメソッドを考えてみましょう。 GenericClass<Bar>の場合は、Barを渡しても問題ありません。 GenericClass<Baz>の場合、BarBazのサブタイプであるため、Barを渡すことはまだ有効です。

したがって、下限を使用すると、汎用メソッドパラメータを使用できますが、汎用戻り値は使用できません。要約すると

<? extends T>あなたは、一般的な戻り値ではなくパラメータを使用できることを意味します。 <? super T>は、汎用パラメータを使用できますが、戻り値は使用できないことを意味します。 Predicate.test()には汎用パラメータがあるため、superが必要です。

もう1つ考えるべきこと:ワイルドカードで述べた境界は、オブジェクトの実際の型引数についてです。それらのタイプの結果はで使用すると反対です。上限ワイルドカード(extends)は、戻り値を割り当てることができる変数の型の下限です。下限ワイルドカード(super)は、パラメータとして渡すことができるタイプの上限です。 Stringの下限ではStringのサブクラスしか受け入れないため、predicate.test(new Object())はコンパイルされません。

+0

あなたの洞察をいただきありがとうございます。戻り値の型/パラメータの要約は、その使用法をより簡単に理解できるようにします。 – piper1970

関連する問題