LongはNumber
を拡張することを知っています。では、なぜこれがコンパイルされないのでしょうか?
そして、プログラムが手動キャストなしでコンパイルできるようにメソッドwith
を定義する方法は?
_import Java.util.function.Function;
public class Builder<T> {
static public interface MyInterface {
Number getNumber();
Long getLong();
}
public <F extends Function<T, R>, R> Builder<T> with(F getter, R returnValue) {
return null;//TODO
}
public static void main(String[] args) {
// works:
new Builder<MyInterface>().with(MyInterface::getLong, 4L);
// works:
new Builder<MyInterface>().with(MyInterface::getNumber, (Number) 4L);
// works:
new Builder<MyInterface>().<Function<MyInterface, Number>, Number> with(MyInterface::getNumber, 4L);
// works:
new Builder<MyInterface>().with((Function<MyInterface, Number>) MyInterface::getNumber, 4L);
// compilation error: Cannot infer ...
new Builder<MyInterface>().with(MyInterface::getNumber, 4L);
// compilation error: Cannot infer ...
new Builder<MyInterface>().with(MyInterface::getNumber, Long.valueOf(4));
// compiles but also involves typecast (and Casting Number to Long is not even safe):
new Builder<MyInterface>().with( myInterface->(Long) myInterface.getNumber(), 4L);
// compiles but also involves manual conversion:
new Builder<MyInterface>().with(myInterface -> myInterface.getNumber().longValue(), 4L);
// compiles (compiler you are kidding me?):
new Builder<MyInterface>().with(castToFunction(MyInterface::getNumber), 4L);
}
static <X, Y> Function<X, Y> castToFunction(Function<X, Y> f) {
return f;
}
}
_
<F, R> with(F, R)
の型引数を推測できません- タイプBuilder.MyInterfaceからのgetNumber()のタイプは数値です。これは記述子の戻りタイプと互換性がありません:Long
使用例については、次を参照してください。 コンパイル時にラムダの戻り値の型がチェックされないのはなぜですか
Javaコンパイラは、一般に、複数/ネストされたジェネリック型またはワイルドカードを推測するのは得意ではありません。ヘルパー関数を使用していくつかの型をキャプチャまたは推測しないと、コンパイルするものが得られないことがよくあります。
しかし、本当にFunction
の正確なタイプをF
としてキャプチャする必要がありますか?そうでない場合は、おそらく次のように機能し、ご覧のとおり、Function
のサブタイプでも機能するようです。
import Java.util.function.Function;
import Java.util.function.UnaryOperator;
public class Builder<T> {
public interface MyInterface {
Number getNumber();
Long getLong();
}
public <R> Builder<T> with(Function<T, R> getter, R returnValue) {
return null;
}
// example subclass of Function
private static UnaryOperator<String> stringFunc = (s) -> (s + ".");
public static void main(String[] args) {
// works
new Builder<MyInterface>().with(MyInterface::getNumber, 4L);
// works
new Builder<String>().with(stringFunc, "s");
}
}
最も興味深い部分は、これらの2行の違いにあると思います。
// works:
new Builder<MyInterface>().<Function<MyInterface, Number>, Number> with(MyInterface::getNumber, 4L);
// compilation error: Cannot infer ...
new Builder<MyInterface>().with(MyInterface::getNumber, 4L);
最初のケースでは、T
は明示的にNumber
なので、4L
もNumber
です。問題ありません。 2番目のケースでは、4L
はLong
であるため、T
はLong
であるため、関数には互換性がなく、Java Number
またはLong
。
次の署名があります:
public <R> Test<T> with(Function<T, ? super R> getter, R returnValue)
3番目の例を除いて、すべての例がコンパイルされます。3番目の例では、メソッドに2つの型変数が必要です。
バージョンが機能しないのは、Javaのメソッド参照に特定の型がないためです。代わりに、指定されたコンテキストで必要なタイプがあります。あなたの場合、R
は4L
のためにLong
であると推測されますが、ゲッターはFunction<MyInterface,Long>
を持つことができません。これは、Javaではジェネリック型が不変であるためです彼らの議論。