web-dev-qa-db-ja.com

Javaがスーパータイプを推測できないのはなぜですか?

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

使用例については、次を参照してください。 コンパイル時にラムダの戻り値の型がチェックされないのはなぜですか

19
jukzi

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");

    }
}
0
machfour

最も興味深い部分は、これらの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なので、4LNumberです。問題ありません。 2番目のケースでは、4LLongであるため、TLongであるため、関数には互換性がなく、Java NumberまたはLong

0
njzk2

次の署名があります:

public <R> Test<T> with(Function<T, ? super R> getter, R returnValue)

3番目の例を除いて、すべての例がコンパイルされます。3番目の例では、メソッドに2つの型変数が必要です。

バージョンが機能しないのは、Javaのメソッド参照に特定の型がないためです。代わりに、指定されたコンテキストで必要なタイプがあります。あなたの場合、R4LのためにLongであると推測されますが、ゲッターはFunction<MyInterface,Long>を持つことができません。これは、Javaではジェネリック型が不変であるためです彼らの議論。

0
Hoopje