Java 8中的函数式接口在Java 7中如何实现?

22
Java 8的函数式接口是否可以在某个地方(例如一个JAR包)获取,以便我可以在Java 7项目中使用它们?这样,我稍后就可以更容易地将代码移植到习惯用法的Java 8中。如果没有,那么从技术上讲是否可能,或者它们是否使用了像默认方法这样的新功能?是的,我指的是java.util.function中的接口。由于添加具有Java前缀的包似乎是不允许的,因此从其他地方导入它们不是一个选项。

3
你对哪个接口感兴趣?任何定义单一方法的接口都是函数式接口,但如果没有Lambda表达式,这个概念就不太有用。 - Joni
这基本上是策略模式的一个特例,其中实现类仅实现一个抽象方法。在Java 8中,这只是一种语法糖。 - András Hummer
5个回答

20
一个函数式接口就是只有一个非默认、非静态方法的接口。所有满足这个定义的接口都可以在Java 8中通过lambda实现。
例如,Runnable是一个函数式接口,在Java 8中可以写成:Runnable r = () -> doSomething();
Java 8引入的许多函数式接口都在java.util.function中。最常用的有:
  • Consumer<T>,具有void accept(T t)
  • Supplier<T>,具有T get()
  • Function<T, R>,具有R apply(T t)
  • Predicate<T>,具有boolean test(T t)

在这个阶段,您可以在合适的地方使用单方法接口,如果可能的话,使用类似的签名。当您迁移到Java 8时,您将能够轻松地通过IDE进行重构,从:
someMethod(new MyConsumer<T>() { public void accept(T t) { use(t); } });

进入

someMethod(t -> use(t));

然后将someMethod(MyConsumer<T> mc)的签名更改为someMethod(Consumer<T> c),摆脱你的MyConsumer接口,就完成了。


1
此外,接口需要有一个且仅有一个非默认和非静态方法。(在函数式接口中也允许静态方法) - skiwi
@skiwi确实进行了修改。 - assylias
3
由于你的自定义接口必须位于不同的“package”中(你不能添加到“java. …”中),因此你甚至可以将其命名为“Consumer”,而不是“MyConsumer”。如果签名完全匹配,当迁移到Java 8时,你可以将自己的“Consumer”转换为“java.util.function.Consumer”的子接口。 - Holger
与此处的大多数答案一样,这个回答忽略了一个重要的点。如果您将其与专有软件一起发布,则很可能违反了JDK许可证。 - jherek
@jherek,考虑到我们正在谈论没有实现的众所周知的模式(供应商、消费者等),这可能有些牵强。但是我不是律师... - assylias
告诉甲骨文的律师们。著名的Google vs Oracle诉讼已经持续了十多年,争论的核心正是API接口是否受许可证保护。 - jherek

10
以下是 Java 8 主要函数接口的签名,作为对 Assylias 答案的补充。
public interface Consumer<T> {
    void accept(T t);
}

public interface Supplier<T> {
    T get();
}

public interface Function<T, R> {
    R apply(T t);
}

public interface Predicate<T> {
    boolean test(T t);
}

1
Java 8的函数式接口有限。现在可以使用FunctionalJava类型P1、F、F2、F3、......、F8、TryCatch0、TryCatch1、......、TryCatch8来完成相同的事情,但具有更多的功能。

https://functionaljava.ci.cloudbees.com/job/master/javadoc/

您可以使用 Retro Lambda 项目来编译带有 Java 8 和 Lambdas 的代码,但是针对的是 Java 7 虚拟机。这样可以避免整个匿名内部类的麻烦。请参考 FunctionalJava 项目进行了解(http://www.functionaljava.org/)。

它们有限是有原因的。一个带有8个通用参数的通用8参数函数几乎肯定会让遇到该代码的人感到困惑。 - Vlasec

0
除了@assylias的回答,我认为它在大多数情况下解决了问题之外,还有一个选项,那就是自己制作一个@FunctionalInterface并保持这种方式。
这取决于您在哪里使用函数。 JDK实用程序类都可以使用上述所有接口。 Predicate允许过滤,Supplier允许对象创建,Function允许映射...事实上,Predicate和Supplier相当直截了当,但Function和Consumer可能经常不清楚,特别是BiFunction。它们也可能在某些用例中束缚你的手脚。
您可以编写自己的接口,其中有任意数量的输入,抛出已检查的异常,在需要时仅具有泛型,并且其名称说明应该用于什么。
//@FunctionalInterface
public interface MyCustomInterface {
    <T> MyCustomOutput myCustomAction(MyCustomInput<T> str) throws MyCustomException;
}

因此,虽然JDK提供的接口很有用,但有时您可能更喜欢在Java 8中保留自己的解决方案,只需使用注释和Lambda而不是匿名类。


0

在Java 7中,有一些可以称为功能接口的接口,因为它们只有一个抽象方法,如Runnable、ActionListener、Comparable。 而Java 8则引入了更多的功能接口,比如Function、Predicate、Consumer和Supplier。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接