且构网

分享程序员开发的那些事...
且构网 - 分享程序员编程开发的那些事

《JAVA8开发指南》第二章采用Lambda表达式(二)

更新时间:2022-08-13 08:57:55

注意事项

你会经常看到在接口类上标有@FunctionalInterface的注解。它和标签@Override很相似,表示方法是可以被覆盖的。本文中,@FunctionalInterface标签用来写在文档中,表示接口是一个函数接口。编译器也将在接口注解不符合函数接口定义的时候抛错。

你也将发现一些新的函数接口,比如在包java.util.function里面的Function<T, R>和Supplier<T>,你可以通过这些来使用任意形式的lambda表达式。

方法参数

方法参数允许你重用已经存在的方法定义并将它们像lamda表达式一样传递进来。它们的实用性在你写代码的时候展现,会使代码跟单纯的使用lambda表达式比更自然更易读。比如使用lambda表达式找隐藏的文件。

 

File[] hiddenFiles = mainDirectory.listFiles(f -> f.isHid

den());

使用方法参数,你能够使用双冒号直接应用方法isHidden。

File[] hiddenFiles = mainDirectory.listFiles(File::isHidden);

最直接理解lambda表达式的方法参数的方式是叫它"指定方法"。

方法参数的类型有以下四种:

1、静态方法的方法参数

Function<String, Integer> converter = Integer::parseInt;

Integer number = converter.apply("10");

2、实例方法的方法参数。

意思是将一个对象的方法运用到lambda表达式的第一个参数位置上。

 

Function<Invoice, Integer> invoiceToId = Invoice::getId;

3、已有对象的实例化方法参数。

Consumer<Object> print = System.out::println;

需要指出的是,这种方法参数在你遇到一个私有辅助方法并想将其注入到另一个方法中时非常有用。

 

File[] hidden = mainDirectory.listFiles(this::isXML);

private boolean isXML(File f) {

return f.getName.endsWith(".xml");

}

4、构造器参数:

Supplier<List<String>> listOfString = List::new;

大汇合

在本章的开始,你看到了一段冗长的分类变量invoices的java代码。

 

Collections.sort(invoices, new Comparator<Invoice>() {

public int compare(Invoice inv1, Invoice inv2) {

return Double.compare(inv2.getAmount(), inv1.getAmount());

}

});

现在,你将看到如何通过我们目前所掌握的java8特性重构这段代码,使其变得更可读更简洁。

首先,Comparator是一个函数接口,它仅定义了一个抽象方法叫“compare”,这个类传入了两个相同类型的对象,并返回一个整数。这种情况lambda表达式能够有很好的表现,如:

 

Collections.sort(invoices,

(Invoice inv1, Invoice inv2) -> {

return Double.compare(inv2.getAmount(),

inv1.getAmount());

});

我们发现lambda表达式的体仅仅的返回一个简单的表达式,所以可以使用更加简洁的形式:

 

Collections.sort(invoices,

(Invoice inv1, Invoice inv2)

-> Double.compare(inv2.getAmount(),

inv1.getAmount()));

java8中List接口支持sort方法,所以可以使用List代替Collections.sort,如下:

invoices.sort((Invoice inv1, Invoice inv2)

-> Double.compare(inv2.getAmount(),

inv1.getAmount()));

接下来,java8引入了一个静态辅助方法Comparator.comparing,它使用一个lambda参数抽取出比较的key值。该地方最后生成了一个Compare对象。你可以采用如下方法使用它。

 

Comparator<Invoice> byAmount

= Comparator.comparing((Invoice inv) -> inv.getAmount());

invoices.sort(byAmount);

你可能注意到了,更简洁的办法是采用Invoice::getAmount代替(Invoice inv)

-> inv.getAmount()。如下:

 

Comparator<Invoice> byAmount

= Comparator.comparing(Invoice::getAmount);

invoices.sort(byAmount);

由于getAmount方法返回的是一个double类型的值,所以使用Comparator.comparingDouble指定类型能够避免一些不必要的问题:

 

 Comparator<Invoice> byAmount
= Comparator.comparingDouble(Invoice::getAmount);
invoices.sort(byAmount);

最后,让我们整理下代码,使用import static引入方法,去掉持有Comparator对象的变量,给出本章开头引入的问题的解决方案。


 import static java.util.Comparator.comparingDouble;
invoices.sort(comparingDouble(Invoice::getAmount));

使用Lambda表达式测试

你可能关心lambda表达式如何影响测试。毕竟,lambda表达式引入的行为需要被测试验证。当你决定测试包含lambda表达式的时候,可以考虑以下两个方面。

如果lambda表达式很小,那么可以去测试使用了lambda表达式的周围代码行为。

如果lambda表达式非常复杂,则抽取它们到一个单独的方法参数中,这样你就可以将其注入并独立测试它们了。

总结

本章的重点概念如下:

lambda表达式可以被理解为一种匿名函数。

Lambda表达式和行为参数模型会代码更灵活更简洁。

函数接口是一个仅声明了一个抽象方法的接口。

Lambda表达式只能用在函数接口上下文中。

在你需要重用一个已有方法时,方法参数比单纯的lambda表达式更加自然,只需要将该方法传入参数位置即可。

测试时,将复杂的lambda表达式拆开,方便你将它们注入到方法参数中。


转载自 并发编程网 - ifeve.com