将java.util.function.Function转换为Interface

tom*_*maj 24 java java-8 method-reference

在这个(简化的)示例中,我可以MyInterface通过使用方法引用来创建我的对象apply,但是直接转换不起作用.

@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>{}
Run Code Online (Sandbox Code Playgroud)

第二个赋值给出了编译器错误:

incompatible types: Function<String,Integer> cannot be converted to MyInterface
Run Code Online (Sandbox Code Playgroud)

这个问题

我可以做一些泛型魔法,能够投射Function<T, R>到界面吗?

Hoo*_*pje 14

原因MyInterface illegal = func;不起作用,是因为func声明为类型的变量Function<String,Integer>,而不是子类型MyInterface.

MyInterface legal = func::apply;作品的原因如下.你可能期望的类型fun::applyFunction<String,Integer>为好,但事实并非如此.类型fun::apply取决于编译器期望的类型.由于您在赋值上下文中使用它,因此编译器需要expressen类型MyInterface,因此在该上下文中 func::apply是类型MyInterface.正是由于这个原因,方法引用表达式只能出现在赋值上下文,调用上下文和转换上下文中(参见Java语言规范).

由于Function<String,Integer>不是一个子类型MyInterface,投掷func到一个MyInterface抛出ClassCastException.因此,将a转换Function<String,Integer>为a 的最明确方法MyInterface是使用方法引用,就像您已经做过的那样:

MyInterface legal = func::apply;
Run Code Online (Sandbox Code Playgroud)


Mar*_*nik 7

最重要的是,尽管所有漂亮的语法都表面上看起来如此,但Java并没有采用结构类型; Liskov 命名类型的可替代性规则与以往一样严格.在您调用的现实代码中Function.andThen,它返回一些实例Function,而且绝对不是实例MyInterface.所以当你写作

MyInterface legal = func::apply;
Run Code Online (Sandbox Code Playgroud)

你会得到另一个对象,这是一个实例MyInterface.相比之下,当你写作

MyInterface illegal = func;
Run Code Online (Sandbox Code Playgroud)

您试图将现有Function直接实例的引用分配给illegal,即使它在结构上与类型匹配,也会违反Liskov的可替代性.没有任何技巧可以从根本上解决这个问题; 即使Java引入了一些新的语法糖,它会使它看起来像转换,实际的语义仍然是转换(评估到另一个实例).