Zhenyu’s Blog

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

Java 8 预览之Method Reference

| Comments

先看一个Lambda表达式的例子:

Arrays.asList("Windows", "Mac OSX").forEach(x -> System.out.println(x));

既然Lambda表达式x->System.out.println(x)相当于匿名函数(接受一个String类型的参数,无返回值),那么对于其使用者forEach而言,传给它一个匿名函数还是有名字的函数其实没有区别,只要这个函数满足forEach的参数规约即可。而且很多时候有名字的函数反而可读性更好并且更利于代码重用。如此说来,Java 8 引入方法引用(Method Reference)也就顺理成章了。上面的代码用方法引用可以写成:

Arrays.asList("Windows", "Mac OSX").forEach(System.out::println);

下面列举了方法引用的几种使用方式,以书店(BookStore)作为示例:

public class Book {
    public String name;
    public String author;
    public Double price;
    // 省略部分代码
}

public class BookStore {
    private List<Book> books = new ArrayList<>();
    
    public List<Book> list(Predicate<Book> filter) {
        List<Book> result = new ArrayList<>();
        books.forEach(book -> { if (filter.test(book)) result.add(book); });
        return result;
    }
    // 省略部分代码
}

静态方法引用

class BookFilter {
    public static boolean booksOfMartinFowler(Book book) {
        return "Martin Fowler".equals(book.author);
    }
}

List<Book> books = bookStore.list(BookFilter::booksOfMartinFowler);

实例方法引用(instance method reference)

class BookFilter {
    public boolean freeBooks(Book book) {
        return book.price == 0;
    }
}

BookFilter bookFilter = new BookFilter();
List<Book> books = bookStore.list(bookFilter::freeBooks);

一个有趣的例子

Java的Tutorial还提供了一个很有意思的例子,很是让我费解了半天。

String[] stringArray = { "Barbara", "James", "Mary", "John",
    "Patricia", "Robert", "Michael", "Linda" };
Arrays.sort(stringArray, String::compareToIgnoreCase);

问题来了,compareToIgnoreCase方法和compare方法从签名上看完全不匹配啊,编译器在解析方法引用的时候,是怎么判断二者匹配的呢?

// Arrays.sort
public static <T> void sort(T[] a, Comparator<? super T> c) 
// Comparator
public interface Comparator<T> {
    int compare(T o1, T o2);
}
// String::compareToIgnoreCase
public int compareToIgnoreCase(String str)

于是我展开了无耻的联想:)既然同样的方式可以用Lambda写也可以用方法引用写,那么编译器很可能将两种方式最终都转化成匿名函数,这样更简单。先把同样的功能用Lambda写一遍:

Arrays.sort(stringArray, (name1, name2) -> name1.compareToIgnoreCase(name2));

// 更进一步
Comparator<String> comparator = (name1, name2) -> {
    return name1.compareToIgnoreCase(name2);
};
Arrays.sort(stringArray, comparator);

编译器最终交给Arrays.sort的很可能就是这样一个东西:

class Foo implements Comparator<String>{
    int compare(String s1, String s2) {
        return s1.compareToIgnoreCase(s2);
    }
}

编译器在解析String::compareToIgnoreCase时,会试图将compareToIgnoreCase作为Foo.compare的实现体。参数类型和方法引用中指定的类型都是String,返回值都要求是int,恰好匹配!(以上内容纯属猜测,不过帮助我理解了它的行为)

同样的道理,下面的代码将会通过编译并且可以运行,只是结果不是我们期待的罢了:

Arrays.sort(stringArray, String::indexOf);

Comments