3

クラス宣言で指定されたジェネリック パラメータを持つメソッドへのメソッド参照を作成しようとしています。ので、私は持っています:

public interface IExecutable<P extends IParameter> {

    void execute(P parameter);

}

public class Parameter implements IParameter {

    public void childSpecific() {
    ...
    }
}

public class TestClass {
    ...
    //somewhere in the code
    public void foo(Parameter parameter) {
        parameter.childSpecific();
    }

    public void test() {
        IExecutable<?> executable = this::foo; //compilation error
        // The type TestClass does not define inner(IParameter) that is applicable here
        executable.execute(new Parameter()); //compilation error as well
        // The method execute(capture#4-of ?) in the type IExecutable<capture#4-of ?> is not applicable for the arguments (Parameter)
    }
    ...
}

ここで実行可能ファイルの具体的なジェネリック型がわからないことは明確です。使用する

IExecutable<Parameter> = ...

すぐに問題を解決しますが、その場合は不可能です。

明らかに、私は何か間違ったことをしています。しかし、それを機能させる方法は?

どうも。

4

3 に答える 3

3

この場合、 foo はIParameter以外を処理するように書かれていませんParameter。タイプ の変数に foo への参照を割り当てることもできますが、IExecutable<? extends IParameter>これは未知のタイプIParameter(この場合はParameter) を処理する実行可能ファイルであることを意味します。特定のサブタイプが不明であるため、IParameterこのスコープ内で処理できるものがわからないため、実行メソッドに のサブタイプを渡すことは構文的に安全ではありません!

必要なのは、キャプチャ (?) を使用する代わりに別の型変数です。このようにして、渡す が実行可能ファイルが受け入れるIParameterのと同じ型であることを指定できます。IParameter以下で行っているように、新しい方法でこれを導入できます。

public class TestClass {
  public static void foo(Parameter parameter) {
    parameter.childSpecific();
  }

  public static void main(String args) {
    execute(TestClass::foo, new Parameter());
  }

  public static <P extends IParameter> void execute(
        IExecutable<P> executable, P param) {
    executable.execute(param);
  }
}
于 2015-05-27T01:03:45.880 に答える
1

Pインターフェイスの type パラメータはIExecutable、 のサブタイプに制限されていIParameterます。次の 2 つのサブタイプを検討してください。

class Parameter implements IParameter { ... }
class AnotherParameter implements IParameter { ... }

ここで、IExecutable<?>上記の制約に関してこれ以上具体的ではありません。実際、 は、またはの未知のサブタイプに?バインドされていると述べています(私の例では)。IParameterParameterAnotherParameter

このような変数宣言では、あなたが言及した 2 つの問題に直面します。

  1. あなたのメソッドfoo(Parameter)は、 のより一般的な制約と一致しませんIExecutable<?>。上記のように、そのような実行可能ファイルはAnotherParameter、 のメソッド シグネチャに明らかに違反するものにバインドされる可能性がありfooます。

  2. 一致したとしても、あなたのようには使えません。?コンパイラは、実際にマップされた型を認識しません。それが知っている唯一のこと: それは のサブタイプでなければなりませんが、IParameterどれが知られていません。つまり、ステートメントexecutable.execute(new Parameter())は許可されていません (同様にexecutable.execute(new AnotherParameter()))。渡すことができる唯一のパラメーターexecuteは ですnull

executable結論: ポイント 1 は、変数を type で宣言することで解決できますIExecutable<? extends Parameter>。これは、 のメソッド シグネチャと一致しfooます。しかし、ポイント 2 ではまだ への呼び出しが許可されていませんexecute

あなたができる唯一のことは、変数を次のように宣言することです

IExecutable<Parameter> executable = this::foo;

これによりコンパイルされ、呼び出しが許可されます

executable.execute(new Parameter());
于 2015-05-27T07:08:48.250 に答える
0

この行は、Java 型推論の失敗を明らかにします

IExecutable<?> executable = this::foo;

このように見てみましょう

IExecutable<?> executable = p->this.foo(p);

それをコンパイルするには、java は の意味を知る必要がありfoo(p)ます。java8 より前では、式の型は部分式の型に基づいて構築されていました。ここでは、 の型をp最初に解決する必要がありますfoo。ただし、 の型pは指定されていないため、周囲のコンテキストから推測する必要があります。ここで、コンテキストはIExecutable<? extends IParameter>であり、 - とp推論されIParameter、メソッドfoo(Iparameter)は存在しません。

一般に、型推論はジレンマに直面しています。それはトップダウンで推論しますか、それともボトムアップで推論しますか? Java8 では、そのための非常に複雑な手順が定義されていますが、これは人間には理解できません:)

回避策: のタイプを指定しますp

IExecutable<?> executable = (Parameter p)->this.foo(p);

または、より具体的なターゲット タイプを指定します

IExecutable<?> executable = (IExecutable<Parameter>)p->this.foo(p);

IExecutable<?> executable = (IExecutable<Parameter>)this::foo;

言語設計者に尋ねると、彼らはこれらすべてが非常に明白であると考えるでしょう...しかし、プログラマーの最善の行動は、おそらく実際の言語仕様を研究するよりも、うまくいくまで別のことを試すことです.

于 2015-05-27T01:24:41.803 に答える