Java方法引用具有generics参数的方法

我正在尝试对一个方法进行方法引用,该方法具有在类声明中指定的generics参数。 所以我有:

public interface IExecutable

{ 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 is not applicable for the arguments (Parameter) } ... }

具体是我不知道可执行文件的具体generics类型。 运用

 IExecutable = ... 

立即解决问题,但案件不可能。

显然,我做错了什么。 但是如何让它发挥作用?

谢谢。

在这种情况下,不会编写foo来处理除Parameter之外的任何IParameter 。 您可以IExecutable foo的引用分配给IExecutable的变量IExecutable IExecutable ,但这意味着它是一个可执行文件,可以处理一些未知类型的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 

void execute( IExecutable

executable, P param) { executable.execute(param); } }

接口IExecutable的类型参数P被约束为IParameter的子类型。 考虑这两个子类型:

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

现在, IExecutable对于上述约束并不具体。 事实上, ? 声明它绑定到一个未知IParameter子类型,它可以是ParameterAnotherParameter (在我的例子中)。

通过这样的变量声明,您将面临您提到的两个问题。

  1. 您的方法foo(Parameter)IExecutable的更一般约束不匹配。 如上所示,这样的可执行文件可以绑定到AnotherParameter ,这显然违反了foo的方法签名。

  2. 即使它匹配,它也不能像你一样使用。 编译器不知道哪种类型的? 实际上是映射。 它唯一知道的事情是:它必须是IParameter的子类型,但哪一个是未知的。 这意味着,不允许使用语句executable.execute(new Parameter()) (还有executable.execute(new AnotherParameter()) )。 允许传递给execute的唯一参数是null

结论:点1可以通过声明类型为IExecutable的变量executable来解决IExecutable IExecutable 。 这与foo的方法签名匹配。 但是第2点仍然不允许execute调用。

你唯一能做的就是将变量声明为

 IExecutable executable = this::foo; 

这将编译并允许调用

 executable.execute(new Parameter()); 

此行暴露了java类型推断中的失败

 IExecutable executable = this::foo; 

让我们这样看待它

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

要编译它,java需要知道foo(p)的含义。 在java8之前,表达式的类型建立在子表达式的类型上; 在这里,需要知道p的类型才能解析foo 。 但是没有指定p的类型,它需要从周围的上下文推断出来。 这里的上下文是IExecutable IExecutable ,并将p推断为IParameter – 并且方法foo(Iparameter)不存在。

一般来说,类型推断面临两难选择,它是从上到下推断还是自下而上? Java8为此定义了一个非常复杂的过程,这是人类无法理解的:)

解决方法:指定p的类型

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

或指定更具体的目标类型

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

如果你问语言设计师,他们会认为这一切都很明显……但程序员的最佳动作可能只是尝试不同的东西,直到它起作用,而不是研究实际的语言规范。