Java方法引用具有泛型参数的方法
我试图对一个方法进行方法引用,该方法在类声明中指定了泛型参数。 因此,我:
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> = ...
立即解决问题,但这是不可能的情况
很明显,我做错了什么。但如何让它起作用呢
Thx
# 1 楼答案
这一行暴露了java类型推断的失败
让我们这样看
要编译它,java需要知道
foo(p)
的含义。在java8之前,表达式的类型是建立在子表达式的类型之上的;这里,首先需要知道p
的类型才能解析foo
。但是p
的类型没有指定,它需要从周围的上下文中推断出来。这里的上下文是IExecutable<? extends IParameter>
,并且p
被推断为IParameter
-并且方法foo(Iparameter)
不存在一般来说,类型推断面临一个困境,它是自上而下还是自下而上进行推断?Java8为此定义了一个极其复杂的过程,这是人类无法理解的:)
解决方法:指定
p
的类型或者指定更具体的目标类型
如果你问语言设计师,他们会认为所有这些都是很明显的…但是程序员最好的行动可能是尝试不同的东西直到它工作,而不是研究实际的语言规范# 2 楼答案
在这种情况下,编写foo不是为了处理除
Parameter
之外的任何IParameter
。您可以将对foo的引用分配给类型为IExecutable<? extends IParameter>
的变量,但是这意味着它是一个可执行文件,可以处理一些未知类型的IParameter
(在本例中为Parameter
)。由于特定的子类型未知,因此将IParameter
的任何子类型传递到其execute方法在语法上是不安全的,因为您不知道它可以在这个范围内处理哪个您需要的是另一个类型变量,而不是使用捕获(the?)。通过这种方式,您可以指定要传递的
IParameter
与可执行文件接受的IParameter
类型相同。你可以用一种新的方法来介绍它,就像我下面所做的:# 3 楼答案
接口
IExecutable
中的类型参数P
被约束为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
来解决。这与foo
的方法签名匹配。但是第2点仍然不允许调用execute
您唯一能做的就是将变量声明为
这将编译并允许调用