函数式接口与lambda表达式

1 篇文章 0 订阅

目录

一.Java8

二.行为参数化

2.1 筛选绿苹果

2.2 把颜色作为参数

2.3 筛选轻重苹果

2.4 对每个属性进行筛选

2.5 通过策略模式改变

2.6 抽象行为

2.7 使用匿名类

三.lambda表达式

四.使用lambda表达式

4.1 lambda使用场景

4.2 函数式接口

4.3 @Functionallnerface


一.Java8

1998 JDK 1.0 Java 1.0 )发布以来, Java 已经受到了学生、项目经理和程序员等一大批活跃
用户 的欢迎。这一语言极富活力,不断被用在大大小小的项目里。从 Java 1.1 1997 年) 一直到
Java 7 2011 年), Java 通过增加新功能,不断得到良好的升级。 Java 8 则是在 2014 3 月发布
的。
JDK8 JDK11 JDK17 ,该怎么选择?
Java 8 提供了一个新的 API (称为 Stream ),它支持许多处理数据的并行操作,其思路和在
数据库查询语言中的思路类似 —— 用更高级的方式表达想要的东西,而由 实现 (在这里是
Streams 库)来 选择最佳低级执行机制。这样就可以避免用 synchronized 编写代码,这一代码不
仅容易出错,而且在多核 CPU 上执行所需的成本也比你想象的要高。(多核 CPU 的每个处理器内
核都有独立的高速缓存。加锁需要这些高速缓存同步运行,然而这又需要在内核间进行较慢的缓
存一致性协议通 信。)
  • 日新月异的计算应用背景:多核和处理大型数据集(大数据)
  • 改进的压力:函数式比命令式更适应新的体系架构
  • Java 8的核心新特性:Lambda(匿名函数)、流、默认方法

二.行为参数化

在软件工程中,一个众所周知的问题就是,不管你做什么,用户的需求肯定会变。比方说,有个
应用程序是帮助农民了解自己的库存的。
这位农民可能想有一个查找库存中所有绿色苹果的功能。但到了第二天, 他可能会告诉你:
实我还想找出所有重量超过 150 克的苹果。
又过了两天,农民又跑回来补充 道: 要是我可以找出所有既是绿色,重量也超过 150 克的苹果,
那就太棒了。
你要如何应对这样不断变 化的需求?理想的状态下,应该把你的工作量降到最少。此外,类似的
新功能实现起来还应该很简单,而且易于长期维护。

2.1 筛选绿苹果

public static List < Apple > filterGreenApples ( List < Apple > inventory ) {
List < Apple > result = new ArrayList <> ();
for ( Apple apple : inventory ){
if ( "green" . equals ( apple . getColor () ) {
result . add ( apple );
}
}
return result ;
}
如果还需要筛选红苹果,简单的解决办法就是复制这个方法,把名字改成 filterRedApples
然后更改 if 条件来匹配红苹 果。要是农民想要筛选多种颜色:浅绿色、暗红色、黄色等,这种方
法就应付不了了。

2.2 把颜色作为参数

public static List < Apple > filterApplesByColor ( List < Apple > inventory ,
String color ) {
List < Apple > result = new ArrayList <> ();
for ( Apple apple : inventory ){
if ( apple . getColor (). equals ( color ) ) {
result . add ( apple );
}
}
return result ;
}
这位农民又跑回来和你说: 要是能区分轻的苹果和重 的苹果就太好了。重的苹果一般是重量大
150 克。

2.3 筛选轻重苹果

public static List < Apple > filterApplesByWeight ( List < Apple > inventory ,
int weight ) {
List < Apple > result = new ArrayList <> ();
For ( Apple apple : inventory ){
if ( apple . getWeight () > weight ){
result . add ( apple );
}
}
return result ;
}

 对苹果进行筛选的部分代码大量重复,从工程工作量的角 度来看,这代价太大了。

2.4 对每个属性进行筛选

public static List < Apple > filterApples ( List < Apple > inventory ,
String color ,
int weight ,
boolean flag ) {
List < Apple > result = new ArrayList <> ();
for ( Apple apple : inventory ){
if ( ( flag && apple . getColor (). equals ( color )) ||
( ! flag && apple . getWeight () > weight ) ){
result . add ( apple );
}
}
return result ;
}
List<Apple> greenApples = filterApples(inventory, "green", 0, true);
List<Apple> heavyApples = filterApples(inventory
这个解决方案还是不能很好地应对变化的需求。
如果这位农民要求你对苹果的不同属性做筛选,比如大小、形 状、产地等,又怎么办?而且,如
果农民要求你组合属性,做更复杂的查询,比如绿色的重苹果?你会有好多个重复的 filter 方法,
或一个巨大的非常复杂的方法。

2.5 通过策略模式改变

虽然判断的要求各不相同,但都是通过苹果的属性来做出来的判断,我们便可以从这上面抽象出
一个条件类(谓词类)
public interface ApplePredicate{
boolean test (Apple apple);
}

有了这个谓词接口,便可以实现多个不同的标准

public class AppleHeavyWeightPredicate implements ApplePredicate {
public boolean test ( Apple apple ){
return apple . getWeight () > 150 ;
}
}
public class AppleGreenColorPredicate implements ApplePredicate {
public boolean test ( Apple apple ){
return "green" . equals ( apple . getColor ());
}
}

 

你可以把这些标准看作 filter 方法的不同行为。你刚做的这些和 策略设计模式 相关,它让你
定义一族算法,把它们封装起来(称为 策略 ),然后在运行时选择一个算法。
在这里,算法族就是 ApplePredicate ,不同的策略就是 `AppleHeavyWeightPredicate
AppleGreenColorPredicate
针对策略的使用来对 filter 方法进行改造

 

public static List < Apple > filterApples ( List < Apple > inventory ,
ApplePredicate p ){
List < Apple > result = new ArrayList <> ();
for ( Apple apple : inventory ){
if ( p . test ( apple )){
result . add ( apple );
}
}
return result ;
}

2.6 抽象行为

现在你可以创建不同的 ApplePredicate 对象,并将它们传递给 filterApples 方法。
比如,如果农民让你找出所有重量超过 150 克的红苹果,你只需要创建一个类来实现
ApplePredicate 就行了。

 

public class AppleRedAndHeavyPredicate implements ApplePredicate {
public boolean test ( Apple apple ){
return "red" . equals ( apple . getColor ())
&& apple . getWeight () > 150 ;
}
}
List < Apple > redAndHeavyApples =
filterApples ( inventory , new AppleRedAndHeavyPredicate ());

分析:test方法中放的就是filterApples所需要执行的行为 

 

什么是行为参数化?多种行为,一个参数

 

 通过这种方式,我们可以把行为抽象出来,让代码适应需求的变化,但这个过程很繁琐,因为你

需要声明很多只要实例化一次的类。

2.7 使用匿名类

接口可以直接用匿名类的方式实例化出来
List < Apple > redApples = filterApples ( inventory , new ApplePredicate () {
public boolean test ( Apple apple ){
return "red" . equals ( apple . getColor ());
}
});

但匿名类还是不够好,它往往很笨重,因为它占用了很多空间。

三.使用lambda 

可以把 Lambda 表达式理解为简洁地表示可传递的匿名函数的一种方式:它没有名称,但它有参
数列 表、函数主体、返回类型,可能还有一个可以抛出的异常列表。
  • 匿名——我们说匿名,是因为它不像普通的方法那样有一个明确的名称:写得少而想得多!
  • 函数——我们说它是函数,是因为Lambda函数不像方法那样属于某个特定的类。但和方法
  • 一样, Lambda有参数列表、函数主体、返回类型,还可能有可以抛出的异常列表。
  • 传递——Lambda表达式可以作为参数传递给方法或存储在变量中。
  • 简洁——无需像匿名类那样写很多模板代码。

未使用:

Comparator < Apple > byWeight = new Comparator < Apple > () {
public int compare ( Apple a1 , Apple a2 ){
return a1 . getWeight (). compareTo ( a2 . getWeight ());
}
};

 

使用后:

Comparator < Apple > byWeight =
( Apple a1 , Apple a2 ) -> a1 . getWeight (). compareTo ( a2 . getWeight ());

 

 

四.使用lambda表达式 

List result = filterApples (
inventory ,
( Apple apple ) -> "red" . equals ( apple . getColor ())
);

 

4.1 lambda使用场景

我们可以将任何的函数式接口改写为lambda的调用方式。

4.2 函数式接口

函数式接口就是只定义一个抽象方法的接口。 

public interface Predicate < T > {
boolean test ( T t );
}
public interface Comparator < T > { //java.util.Comparator
int compare ( T o1 , T o2 );
}
public interface Runnable { //java.lang.Runnable
void run ();
}
public interface ActionListener extends EventListener {
//java.awt.event.ActionListener
void actionPerformed ( ActionEvent e );
}
public interface Callable < V > { //java.util.concurrent.Callable
V call ();
}
public interface PrivilegedAction < V > {
//java.security.PrivilegedAction
V run ();
}

 

需要注意的是 : 指的是有一个需要执行的方法,而不是一个方法,因为在 java8 中引入了 default
表达式允许你直接以内联的形式为函数式接口的抽象方法提供实 现,并把整个表达式作为函数式
接口的实例(具体说来,是函数式接口一个具体实现的实例)。

 

4.3 @Functionallnterface

函数式接口带有 @FunctionalInterface 的标注。
这个标注用于表示该接口会设计成一个函数式接 口。如果你用 @FunctionalInterface 定义了
一个接口,而它却不是函数式接口的话,编译器将返 回一个提示原因的错误。
例如,错误消息可能是 “Multiple non-overriding abstract methods found in interface Foo”
表明存在多个抽象方法。请注意, @FunctionalInterface 不是必需 的,但对于为此设计的接
口而言,使用它是比较好的做法。
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值