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

采用Lambda表达式

本章,你将学习到如何采用JAVA8的重要特性Lambda表达式。首先,你要了解“行为参数”这种模式。该模式能够使你写出来的代码适应需求变化。然后,你将看到该模式如何使得Lambda表达式的使用与以往比变得更加简洁。然后,你将学习如何精确地定位Lambda表达式的使用场景和使用方式。你也将了解JAVA8的另一个特性-方法参数,它能使你的代码更简洁更易读。带着所有这些新知识实战一个重构代码的例子。最后,你也将学习到如何使用Lambda表达式和方法参数。

为什么使用Lambda表达式

将Lambda表达式引入JAVA中的动机源于一个叫“行为参数”的模式。这种模式能够解决需求变化带来的问题,使代码变得更加灵活。在JAVA8之前,参数模式十分啰嗦。Lambda表达式通过精简的方式使用行为模式克服了这个缺点。举个例子,如果你需要找大于一定金额的发票。你可能写这样一个indInvoicesGreaterThanAmount方法:


List findInvoicesGreaterThanAmount(List invoi
ces, double amount) {
List result = new ArrayList<>();
for(Invoice inv: invoices) {
if(inv.getAmount() > amount) {
result.add(inv);
}
}
return result;
}

这个方法确实足够简单。可是如果你还需要找小于一定金额的发票呢?或者更糟糕的是,你需要从一个指定商户中找发票,并且也需要找一定数额呢?这时你需要一个方式来参数化指定条件下的过滤行为。下面我们将定义一个接口InvoicePredicate来描述条件,使用该接口重构上面的方法。

接口定义

interface InvoicePredicate {

boolean test(invoice inv);

}

重构方法

List<Invoice> findInvoices(List<Invoice> invoices, InvoicePredi

cate p) {

List<Invoice> result = new ArrayList<>();

for(Invoice inv: invoices) {

if(p.test(inv)) {

result.add(inv);

}

}

return result;

}

 

使用这段代码,你能够通过增加一个Invoice对象解决需求变化带来的问题。你只需要创建一个不同的InvoicePredicate 对象,将其传入方法findInvoices中。换句话说,你已经参数化了findInvoices行为。不好的是,使用这个新方法引入了额外的冗余,来看下面的代码:

 

 

List<Invoice> expensiveInvoicesFromOracle

= findInvoices(invoices, new InvoicePredicate() {

public test(Invoice inv) {

return inv.getAmount() > 10_000

&& inv.getCustomer() == Customer.ORACLE;

}

});

 

换句话说,代码变得更灵活的同时可读性变差了。最理想的状态是,代码灵活性和可读性兼备。Lambda表达式的引入能够做到这一点。通过使用它重构上面的代码如下:

 

 

List<Invoice> expensiveInvoicesFromOracle

= findInvoices(invoices, inv ->

inv.getAmount() > 10_000

&& inv.getCustomer() ==

Customer.ORACLE);

Lambda表达式的定义

 

现在知道了为什么需要在代码中引入Lambda表达式,也是时候精准的了解下Lambda表达式的定义。简单的来讲,lambda表达式是一个能够被传递的匿名函数,我们仔细看看这个定义:

 

匿名

Lambda表达式是匿名的,因为它没有像普通方法那样有一个明确的名称。它有点儿像匿名类,因为两者都没有明确的名称。

 

函数

一个 Lambda表达式像一个方法,它包含一串参数、一个体、一个返回类型和一串可能抛出的错误。和方法不同的是,它没有被声明为特殊类的一部分。

 

传递

一个 Lambda表达式能够作为一个方法的参数被传递,也能够作为一个结果被返回。

Lambda表达式语法

在写lambda表达式之前,需要知道它的语法。在本书中已经出现过lambda表达式:

Runnable r = () -> System.out.println("Hi");

FileFilter isXml = (File f) -> f.getName().endsWith(".xml");

这两个lambda表达式有三个部分:

  • 一串参数,比如 (File f)
  • 一个有两个字符组成的箭头:- 和 >
  • 一个体,比如f.getName().endsWith(".xml")

lambda表达式有两种形式。当你的lambda表达式体中包含一个语句的时候可以采用第一种:

(parameters) -> expression

当你的lambda表达式体中包含一个或多个语句的时候可以使用第二种形。式请注意,你必须使用大括号将表达式包含进来:

(parameters) -> { statements;}

大体来说,如果lambda表达式参数的类型被间接的指出过,可以去掉类型声明。另外,如果参数个数只是一个,那么圆括号也可以被去掉。

 

Lambda表达式使用场景

现在,了解了如何写一个lambda表达式,接下来的问题是考虑使用lambda表达式的使用方式和使用场景。简单的说,你可以在函数接口中使用lambda表达式。函数接口里包含一个抽象方法。比如上文的两个lambda表达式。

Runnable r = () -> System.out.println("Hi");

FileFilter isXml = (File f) ->
f.getName().endsWith(".xml");
Runnable就是一个函数接口,因为它包含了一个抽象方法run。FileFilter也是一个函数接口,因为它也定义了一个抽象方法叫accept。

 

 

@FunctionalInterface

public interface Runnable {

void run();

}

 

@FunctionalInterface

public interface FileFilter {

boolean accept(File pathname);

lambda表达式重要的一点就是让你创建函数接口的实例。lambda表达式的体提供了函数接口中单个抽象方法的实现。因此,下面使用匿名类和lamba表达式实现的Runnable的结果是一直的。

 


Runnable r1 = new Runnable() {

public void run() {

System.out.println("Hi!");

}

};

r1.run();

 

Runnable r2 = () -> System.out.println("Hi!");

r2.run();


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

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值