Zhenyu’s Blog

陌上发花,可以缓缓醉矣, 忍把浮名,换了浅酌低唱。

Java 8 预览之Functional Interface

| Comments

在JDK的类库中,有很多只声明了一个方法的接口,比如java.lang.Iterable<T>java.lang.Runnable。这些接口被称为单抽象方法接口(Single Abstract Method interfaces),它表达了一种逻辑上的单一功能约定。

Java 8为这样的接口引入了一个新概念——函数式接口(Functional Interface),同时引入注解@FunctionalInterface以帮助编译器检查函数式接口的合法性。

何为函数式接口

JSR 335中这样描述函数式接口:

A functional interface is an interface that has just one abstract method, and thus represents a single function contract. (In some cases, this “single” method may take the form of multiple abstract methods with override-equivalent signatures (8.4.2) inherited from superinterfaces; in this case, the inherited methods logically represent a single method.)

函数式接口和所谓的Single Abstract Method interfaces一样,就是只包含一个抽象方法的接口,表达的是逻辑上的单一功能,例如:

  • java.lang.Runnable就是一个函数式接口, 因为它只有一个抽象方法:
public interface Runnable {
    public abstract void run();
}
  • java.lang.Iterable<T>虽然有两个方法,但它仍然是函数式接口,因为forEach方法是一个Default Method,它有其默认实现,且不强制要求实现该接口的类或继承该接口的子接口重写(Override)该方法,因此,在逻辑上,java.lang.Iterable<T>仍然是只约定一个iterator方法的函数式接口。
public interface Iterable<T> {
    Iterator<T> iterator();
    default void forEach(Consumer<? super T> action) {
        // 省略实现代码
    }
}

函数式接口与Lambda

函数式接口的一个非常重要的用途就是对Lambda提供支持,看下面的例子:

public class Book {
    public String name;
    public String author;
    public double price;
}

public class BookStore {
    private List<Book> books = new ArrayList<>();
}

现在我希望BookStore能够筛选出BookStore中符合某种条件书籍,筛选条件可能多种多样,比如,所有Martin Fowler的著作,或者价格大于某个金额的所有书籍,于是,我们新增了BookFilter接口,并且为BookStore添加了list方法:

public interface BookFilter {
    boolean test(Book book);
}

public class BookStore {
    private List<Book> books = new ArrayList<>();

    public List<Book> list(BookFilter filter) {
        List<Book> result = new ArrayList<>();
        books.forEach(book -> {
            if (filter.test(book)) {
                result.add(book);
            }
        });
        return result;
    }
}

现在,我们就可以在调用list方法时使用Lambda表达式了:

// 筛选出所有价格大于15.0的书籍
bookStore.list(book -> book.price > 15.0)

因为BookFilter是一个函数式接口,只具有一个抽象方法,所以在编译期可以很容易推断Lambda表达式和BookFilter是否匹配,于是Lambda表达式的实现就简单了。

JDK提供的通用函数式接口

上面代码中的BookFilter实际上只是一个断言:如果Book符合某种条件则返回true,否则返回false。我们完全可以将这个概念抽象成Predicate供所有人使用,这样项目中就不会充斥过量的仅为Lambda服务的函数式接口了。Java 8的设计者同样考虑到了这个问题,于是新增了java.util.function包,提供通用的函数式接口。Predicate<T>就是其中之一。

于是,上面的list方法使用Predicate就足够了,我们可以痛快的把BookFilter扔进回收站了。

public List<Book> list(Predicate<Book> predicate) {
    List<Book> result = new ArrayList<>();
    books.forEach(book -> {
        if (predicate.test(book)) {
            result.add(book);
        }
    });
    return result;
}

Comments