使用(empty)默认方法来创建FunctionalInterface

时间:2018-04-02 01:53:03

标签: java interface java-8 functional-interface

在Java 8中,引入了接口的默认方法,用于向现有接口添加方法,而不会破坏向后兼容性。

由于默认方法是非抽象的,因此它们可用于制作具有多个可覆盖方法的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);

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

3 个答案:

答案 0 :(得分:1)

this answer中所述,允许创建具有多个方法的接口仍然是功能接口,这是默认方法的目的之一。如前所述,您将在Java API本身中找到示例,例如ComparatorPredicateFunction,具有default方法并且故意成为功能接口。

default实现是否无效并不重要,更重要的问题是,这个默认实现的自然程度如何。它是否只是一个kludge,只是为了使lambdas成为可能,或者它确实是某些甚至大多数实现都会以任何方式使用它们(无论它们是如何实现的)?

即使您遵循评论中的建议,也不需要特殊的清理操{1}}。请注意,同样,close实现end,其默认行为是对Stream不执行任何操作。您甚至可以遵循该模式以允许将清理操作指定为单独的AutoCloseable,类似于AutoCloseable

close()

这允许通过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(); }
            }
        };
    }
}

RESP。

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")); } 是Java 9语法。对于Java 8,您需要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")); }

答案 1 :(得分:0)

从理论上讲,它没有任何问题。您可以使用此类接口。 我想从你需要这种类型构造的条件开始。

向后能力

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

可选实施

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

此外,Java 8还引入了FunctionalInterface来将任何接口标记为功能接口。根据官方指南,lambda&amp; amp;如果接口有一个方法但没有用FunctionalInterface注释,则应避免使用方法引用。

答案 2 :(得分:0)

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

如果你这样做,那么用户可以实现StringTransform,他们实际需要这个接口作为一种类型,并在那里创建lambdas。这降低了可读性和可维护性。