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 extends IParameter>
foo的引用分配给IExecutable extends IParameter>
的变量IExecutable extends IParameter>
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 void execute( IExecutable
executable, P param) { executable.execute(param); } }
接口IExecutable
的类型参数P
被约束为IParameter
的子类型。 考虑这两个子类型:
class Parameter implements IParameter { ... } class AnotherParameter implements IParameter { ... }
现在, IExecutable>
对于上述约束并不具体。 事实上, ?
声明它绑定到一个未知的IParameter
子类型,它可以是Parameter
或AnotherParameter
(在我的例子中)。
通过这样的变量声明,您将面临您提到的两个问题。
-
您的方法
foo(Parameter)
与IExecutable>
的更一般约束不匹配。 如上所示,这样的可执行文件可以绑定到AnotherParameter
,这显然违反了foo
的方法签名。 -
即使它匹配,它也不能像你一样使用。 编译器不知道哪种类型的
?
实际上是映射。 它唯一知道的事情是:它必须是IParameter
的子类型,但哪一个是未知的。 这意味着,不允许使用语句executable.execute(new Parameter())
(还有executable.execute(new AnotherParameter())
)。 允许传递给execute
的唯一参数是null
。
结论:点1可以通过声明类型为IExecutable extends Parameter>
的变量executable
来解决IExecutable extends Parameter>
IExecutable extends Parameter>
。 这与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 extends IParameter>
IExecutable extends IParameter>
,并将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;
如果你问语言设计师,他们会认为这一切都很明显……但程序员的最佳动作可能只是尝试不同的东西,直到它起作用,而不是研究实际的语言规范。