Java 对具有泛型参数的方法的方法引用
Java method reference to a method with generic parameter
我正在尝试对具有 class 声明中指定的通用参数的方法进行方法引用。
所以我有:
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> = ...
立即解决问题,但不可能。
很明显,我做错了什么。但是如何让它发挥作用呢?
谢谢。
在这种情况下,foo 不会被写入来处理除 Parameter
之外的任何 IParameter
。您可以将对 foo 的引用分配给 IExecutable<? extends IParameter>
类型的变量,但这意味着它是一个可执行的可执行文件,可以处理某种未知类型的 IParameter
(在本例中为 Parameter
)。由于具体的子类型是未知的,将 IParameter
的任何子类型传递给它的 execute 方法在语法上是不安全的,因为您不知道它可以在此范围内处理哪个!
您需要的是另一种类型变量,而不是使用捕获(?)。通过这种方式,您可以指定您传入的 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);
}
}
此行暴露了 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;
如果你问语言设计者,他们会认为所有这些都是显而易见的......但程序员最好的行动可能只是尝试不同的事情直到它起作用,而不是研究实际的语言规范。
您的接口 IExecutable
中的类型参数 P
被限制为 IParameter
的子类型。考虑这两个子类型:
class Parameter implements IParameter { ... }
class AnotherParameter implements IParameter { ... }
现在,IExecutable<?>
对上述约束没有更具体的说明。事实上,?
声明它绑定到 IParameter
的 unknown 子类型,可能是 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
.
你唯一能做的就是将变量声明为
IExecutable<Parameter> executable = this::foo;
这将编译并允许调用
executable.execute(new Parameter());
我正在尝试对具有 class 声明中指定的通用参数的方法进行方法引用。 所以我有:
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> = ...
立即解决问题,但不可能。
很明显,我做错了什么。但是如何让它发挥作用呢?
谢谢。
在这种情况下,foo 不会被写入来处理除 Parameter
之外的任何 IParameter
。您可以将对 foo 的引用分配给 IExecutable<? extends IParameter>
类型的变量,但这意味着它是一个可执行的可执行文件,可以处理某种未知类型的 IParameter
(在本例中为 Parameter
)。由于具体的子类型是未知的,将 IParameter
的任何子类型传递给它的 execute 方法在语法上是不安全的,因为您不知道它可以在此范围内处理哪个!
您需要的是另一种类型变量,而不是使用捕获(?)。通过这种方式,您可以指定您传入的 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);
}
}
此行暴露了 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;
如果你问语言设计者,他们会认为所有这些都是显而易见的......但程序员最好的行动可能只是尝试不同的事情直到它起作用,而不是研究实际的语言规范。
您的接口 IExecutable
中的类型参数 P
被限制为 IParameter
的子类型。考虑这两个子类型:
class Parameter implements IParameter { ... }
class AnotherParameter implements IParameter { ... }
现在,IExecutable<?>
对上述约束没有更具体的说明。事实上,?
声明它绑定到 IParameter
的 unknown 子类型,可能是 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
.
你唯一能做的就是将变量声明为
IExecutable<Parameter> executable = this::foo;
这将编译并允许调用
executable.execute(new Parameter());