使用(空)默认方法制作 FunctionalInterface

Using (empty) default method to make FunctionalInterface

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

由于默认方法是非抽象的,因此它们可用于制作具有多个可覆盖方法的FunctionalInterface

比如说,StringTransformer 接口有两个方法,transform,将给定的 Stringend 转换为释放资源:

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);

这是 valid/best 做法,还是反模式和滥用默认方法?

理论上是没有问题的。您可以使用这种类型的接口。 我想从您需要这种构造的条件开始。

后备能力

如您所述,引入默认方法是为了保持向后能力,因此使用默认方法使接口向后兼容是合理的。

可选实现

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

此外,Java8还引入了FunctionalInterface来将任何接口标记为功能接口。根据官方指南,如果接口具有一种方法但未使用 FunctionalInterface.

注释,则应避免使用 lambda 和方法引用

在我看来,将可作为数据类型引用的常规接口转换为功能接口并不是一个好主意。

如果您这样做,那么用户可以在他们实际需要此接口作为类型的地方以及在他们创建 lambda 的地方实现 StringTransform。这会降低可读性和可维护性。

this answer, allowing to create interfaces with more than one method still being functional interfaces, is one of the purposes of default methods. As also mentioned there, you will find examples within the Java API itself, say Comparator, Predicate, or Function 中所述,具有 default 方法并有意成为功能接口。

default 实现是否无所事事并不重要,更重要的问题是,这个默认实现有多自然。只是为了让 lambda 成为可能,这感觉像是一种拼凑,还是它确实是一些甚至大多数实现会以任何方式使用的东西(不管它们是如何实现的)?

不需要特殊的清理操作可能确实是常见情况,即使您遵循评论中提出的建议,让您的界面扩展 AutoCloseable and name the method close instead of end. Note that likewise, Stream implements AutoCloseable and its default behavior is to do nothing on close(). You could even follow the pattern to allow specifying the cleanup action as separate Runnable, similar to Stream.onClose(Runnable):

public interface StringTransformer extends UnaryOperator<String>, AutoCloseable {
    static StringTransformer transformer(Function<String,String> f) {
        return f::apply;
    }
    String transform(String s);
    @Override default String apply(String s) { return transform(s); }
    @Override default void close() {}
    default StringTransformer onClose(Runnable r) {
        return new StringTransformer() {
            @Override public String transform(String s) {
                return StringTransformer.this.transform(s);
            }
            @Override public void close() {
                try(StringTransformer.this) { r.run(); }
            }
        };
    }
}

这允许通过 onClose 注册清理操作,因此以下工作:

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"));
}

如果您使用 import static。如果您链接

等多个操作,它还可以确保安全关闭
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"));
}

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