Java 使用(空)默认方法创建FunctionInterface

Java 使用(空)默认方法创建FunctionInterface,java,interface,java-8,functional-interface,Java,Interface,Java 8,Functional Interface,在Java8中,引入了接口的默认方法,用于在不破坏向后兼容性的情况下向现有接口添加方法 由于默认方法是非抽象的,因此可以使用它们与多个可重写方法建立功能接口 比如说,StringTransformer接口有两种方法,transform,用于转换给定的String,以及end以释放资源: interface StringTransformer { String transform(String s); void end(); } 但是有些实现可能没有可用的资源,因此我们可以为e

在Java8中,引入了接口的默认方法,用于在不破坏向后兼容性的情况下向现有接口添加方法

由于默认方法是非抽象的,因此可以使用它们与多个可重写方法建立
功能接口

比如说,
StringTransformer
接口有两种方法,
transform
,用于转换给定的
String
,以及
end
以释放资源:

interface StringTransformer {
    String transform(String s);

    void end();
}
但是有些实现可能没有可用的资源,因此我们可以为
end
提供空的默认方法,并为
StringTransformer
使用lambda函数和方法参考:

interface StringTransformer {
    String transform(String s);

    default void end() {
    }
}

StringTransformer x = String::trim;
StringTransformer y = (x -> x + x);

这是一种有效/最佳实践,还是一种反模式和滥用默认方法?

从理论上讲,这没有什么错。您可以使用这种类型的接口。 我想从你需要这种构造的条件开始

向后能力

正如您所提到的,引入默认方法是为了维护向后功能,所以使用默认方法使接口向后兼容是合理的

可选实施

默认的空方法可用于使任何方法的实现成为可选的。但是,您可以使用默认实现为此类接口创建一个抽象适配器类。这种策略已经使用了很长时间,它优于接口中的默认方法,对于抽象类,我们可以定义复杂的默认实现。此外,我们可以在开始时使用一个简单的空方法,然后将其更改为具有复杂的默认实现,而在接口中使用默认方法的情况下,由于类的某些功能不可用,因此不可能实现


此外,Java8还引入了
functionanterface
,将任何接口标记为功能接口。根据官方指南,如果接口只有一个方法,但没有使用
功能接口

注释,则应避免使用lambda和方法引用。我认为,将可作为数据类型引用的常规接口转换为功能接口不是一个好主意

如果您这样做,那么用户可以在实际需要该接口作为类型以及创建lambda的位置实现StringTransform。这降低了可读性和可维护性。

如中所述,允许创建多个方法仍然是功能接口的接口,这是默认方法的目的之一。正如前面提到的,您将在JavaAPI本身中找到示例,例如,拥有
default
方法并有意成为功能接口

无论
默认实现是否什么都不做,更重要的问题是,这个默认实现有多自然。仅仅为了使lambdas成为可能,这感觉像是一个乱七八糟的问题吗?或者它确实是一些甚至大多数实现所使用的方式(不管它们是如何实现的)

不需要特殊的清理操作可能确实是一种常见情况,即使您遵循注释中的建议,让您的接口扩展并命名方法
close
,而不是
end
。请注意,同样地,
Stream
实现了
AutoCloseable
,其默认行为是在
close()
上不执行任何操作。您甚至可以按照该模式将清理操作指定为单独的
Runnable
,类似于:

分别

如果使用
导入静态
。它还可以确保安全关闭,如果您链接多个操作,如

try(StringTransformer t = transformer(String::toUpperCase)
                         .onClose(()->System.out.println("close 1"))
                         .onClose(()->{ throw new IllegalStateException(); })) {
    System.out.println(t.apply("some text"));
}


请注意,
try(StringTransformer.this){r.run();}
是Java9语法。对于Java8,您需要
try(StringTransformer toClose=StringTransformer.this){r.run();}

意见:这是对默认方法的滥用另外,如果您想要一个清理资源的方法,您应该实现和它的方法,允许调用者使用。小结:Lambda是函数,不是对象。@Andreas在我看来,它是默认方法的一个非常传统的用例。你可能会争辩说,将本质上是抽象类的东西实例化为lambda是不合适的,但即便如此,这与你的链接也有点不同。函数接口是一个常规接口。没有什么不规则的。“他们在哪里实际需要这个接口作为一个类型”是什么意思?
try(StringTransformer t = 
        StringTransformer.transformer(String::toUpperCase)
                         .onClose(()->System.out.println("close"))) {
    System.out.println(t.apply("some text"));
}
try(StringTransformer t = transformer(String::toUpperCase)
                         .onClose(()->System.out.println("close 1"))) {
    System.out.println(t.apply("some text"));
}
try(StringTransformer t = transformer(String::toUpperCase)
                         .onClose(()->System.out.println("close 1"))
                         .onClose(()->{ throw new IllegalStateException(); })) {
    System.out.println(t.apply("some text"));
}
try(StringTransformer t = transformer(String::toUpperCase)
                         .onClose(()->{ throw new IllegalStateException("outer fail"); })
                         .onClose(()->{ throw new IllegalStateException("inner fail"); })){
    System.out.println(t.apply("some text"));
}