将 java.util.function.Function 投射到界面
Cast java.util.function.Function to Interface
例子
在这个(简化的)示例中,我可以通过使用对 apply
的方法引用来创建我的 MyInterface
-对象,但直接转换不起作用。
@Test
public void testInterfaceCast(){
Function<String, Integer> func = Integer::parseInt;
MyInterface legal = func::apply; // works
MyInterface illegal = func; // error
}
public interface MyInterface extends Function<String, Integer>{}
第二次赋值给出编译器错误:
incompatible types: Function<String,Integer> cannot be converted to MyInterface
问题
我可以做一些泛型魔术,以便能够将 Function<T, R>
转换为接口吗?
由于此代码段无法编译的相同原因,它无法编译:
Animal animal = new Animal();
Cat x = animal; //illegal assignment
即你是在暗示每只动物 都可以是 猫。编译器没有证据表明实际的 super-class 实例 在运行时确实是一只猫 ,因此会引发错误。
您必须将 Integer::parseInt
分配给 MyInterface
实例:
MyInterface func = Integer::parseInt;
MyInterface illegal = func;
因为 func
是 Function<String, Integer>
类型(即 MyInterface
的超类型)。
事实是,在编译时无法将 Integer::parseInt
类型设为 MyInterface
,unless assigning it 变为 MyInterface
变量。
MyInterface illegal = func;
不起作用的原因是因为 func
被声明为类型 Function<String,Integer>
的变量,它不是 MyInterface
的子类型.
MyInterface legal = func::apply;
起作用的原因如下。您可能希望 fun::apply
的类型也为 Function<String,Integer>
,但事实并非如此。 fun::apply
的类型部分取决于编译器期望的类型。由于您在赋值上下文中使用它,因此编译器需要 MyInterface
类型的表达式,因此 在该上下文中 func::apply
是 MyInterface
类型.正是由于这个原因,方法引用表达式只能出现在赋值上下文、调用上下文和转换上下文中(参见Java Language Specification)。
因为 Function<String,Integer>
不是 MyInterface
的子类型,所以将 func
转换为 MyInterface
会抛出 ClassCastException
。因此,将 Function<String,Integer>
转换为 MyInterface
的最明确方法是使用方法引用,就像您已经做过的那样:
MyInterface legal = func::apply;
最根本的事实是,尽管所有漂亮的语法表面上都具有这种外观,Java 并没有采用结构类型; 命名类型 的 Liskov 可替换性规则一如既往地严格适用。在您的实际代码中,您调用 Function.andThen
,其中 returns 是 Function
的某个实例,绝对不是 MyInterface
的实例。所以当你写
MyInterface legal = func::apply;
你会得到另一个对象,它是 MyInterface
的一个实例。相比之下,当你写
MyInterface illegal = func;
您正试图将对 Function
的现有实例的引用直接分配给 illegal
,这将违反 Liskov 可替换性,即使它在结构上与类型匹配。没有任何技巧可以从根本上解决这个问题;即使 Java 引入了一些新的语法糖,使它 看起来 像转换,实际的语义仍然是 conversion 的语义(评估另一个实例)。
例子
在这个(简化的)示例中,我可以通过使用对 apply
的方法引用来创建我的 MyInterface
-对象,但直接转换不起作用。
@Test
public void testInterfaceCast(){
Function<String, Integer> func = Integer::parseInt;
MyInterface legal = func::apply; // works
MyInterface illegal = func; // error
}
public interface MyInterface extends Function<String, Integer>{}
第二次赋值给出编译器错误:
incompatible types: Function<String,Integer> cannot be converted to MyInterface
问题
我可以做一些泛型魔术,以便能够将 Function<T, R>
转换为接口吗?
由于此代码段无法编译的相同原因,它无法编译:
Animal animal = new Animal();
Cat x = animal; //illegal assignment
即你是在暗示每只动物 都可以是 猫。编译器没有证据表明实际的 super-class 实例 在运行时确实是一只猫 ,因此会引发错误。
您必须将 Integer::parseInt
分配给 MyInterface
实例:
MyInterface func = Integer::parseInt;
MyInterface illegal = func;
因为 func
是 Function<String, Integer>
类型(即 MyInterface
的超类型)。
事实是,在编译时无法将 Integer::parseInt
类型设为 MyInterface
,unless assigning it 变为 MyInterface
变量。
MyInterface illegal = func;
不起作用的原因是因为 func
被声明为类型 Function<String,Integer>
的变量,它不是 MyInterface
的子类型.
MyInterface legal = func::apply;
起作用的原因如下。您可能希望 fun::apply
的类型也为 Function<String,Integer>
,但事实并非如此。 fun::apply
的类型部分取决于编译器期望的类型。由于您在赋值上下文中使用它,因此编译器需要 MyInterface
类型的表达式,因此 在该上下文中 func::apply
是 MyInterface
类型.正是由于这个原因,方法引用表达式只能出现在赋值上下文、调用上下文和转换上下文中(参见Java Language Specification)。
因为 Function<String,Integer>
不是 MyInterface
的子类型,所以将 func
转换为 MyInterface
会抛出 ClassCastException
。因此,将 Function<String,Integer>
转换为 MyInterface
的最明确方法是使用方法引用,就像您已经做过的那样:
MyInterface legal = func::apply;
最根本的事实是,尽管所有漂亮的语法表面上都具有这种外观,Java 并没有采用结构类型; 命名类型 的 Liskov 可替换性规则一如既往地严格适用。在您的实际代码中,您调用 Function.andThen
,其中 returns 是 Function
的某个实例,绝对不是 MyInterface
的实例。所以当你写
MyInterface legal = func::apply;
你会得到另一个对象,它是 MyInterface
的一个实例。相比之下,当你写
MyInterface illegal = func;
您正试图将对 Function
的现有实例的引用直接分配给 illegal
,这将违反 Liskov 可替换性,即使它在结构上与类型匹配。没有任何技巧可以从根本上解决这个问题;即使 Java 引入了一些新的语法糖,使它 看起来 像转换,实际的语义仍然是 conversion 的语义(评估另一个实例)。