本篇内容
- 函数式编程
- 行为参数化
- 方法引用
- 匿名函数
- 函数式接口
- 一步步演变-lambda表达式
讲真的,在整理这篇文章内容之前,我一直在想要不要整理的这么细节,但俗话说得好,细节决定成败,所以我希望每个对lambda表达式没有概念的同学看过我这篇文章后,都能跟看过一遍java8实战这本书一样,对于lambda表达式出现的前因后果有个比较深入的了解跟理解,同时也能够让已经看过java8实战这本书的内容的人,进一步深入lambda表达式这个概念以及设计的意义。
函数式编程
在JAVA8中引入了比较前沿的就是函数式编程的思想,我们本篇要讲的lambda表达式,一步步演变之前,最开始都是基于我现在说的这个函数式编程的思想。所谓函数式编程,是一种编程范式,我们常见的编程范式有命令式编程(Imperative programming),函数式编程,逻辑式编程,常见的面向对象编程是也是一种命令式编程。
命令式编程是面向计算机硬件的抽象,有变量(对应着存储单元),赋值语句(获取,存储指令),表达式(内存引用和算术运算)和控制语句(跳转指令),一句话,命令式程序就是一个冯诺依曼机的指令序列。
** 而函数式编程是面向数学的抽象,将计算描述为一种表达式求值,一句话,函数式程序就是一个表达式。**
很抽象哈,其实就是你可以把函数当成参数去对待,参数可以承担的角色,函数也可以。在函数式语言中,函数作为"一等公民"(这里是指向),可以在任何地方定义,在函数内或函数外,可以作为函数的参数和返回值,可以对函数进行组合。
在JAVA8中引入的两个最主要的功能,lambda表达式以及stream,都需要基于这种思想来设计。
比如:Java8中从函数式编程中引入的两个核心思想:将方法和Lambda作为一等值,以及在没有可变共享状态时,函数或方法可以有效、安全地并行执行。在我们stream api的设计中,都使用了这两种思想。
后面我会总结出相关内容补上,我们继续来看。下一个
行为参数化
既然前面说了,JAVA8引入了函数式编程的思想的设计,那么如何实现呢?这就需要下面这个功能, 在java8中增加了一种把方法(你自己编写的代码)作为参数传递给另一个方法的能力。所以其实行为参数化就是方法参数化。在我们stream api的设计中也是利用这点:构建在通过传递代码使操作行为实现参数化的思想上的。只有把行为参数化,才可以把函数(或者说行为操作)作为参数传进函数中,进行编程吧。
定义: 就是一个方法可以接受多种不同的行为作为参数,并且在内部使用他们,完成不同行为的能力。可以把一个行为(一段代码,结合匿名类)通过接口封装起来后在调用时候通过lambda方式实现,能够适应不断变化的需求,这种做法类似策略模式。
下面我们还是引用java8实战这本书中的例子: 筛选苹果行为或者方式不同的例子(使用谓词)
我先用一句话总结为什么需要下面的方式写哈,比如你产品经理有个需求,筛选10万个苹果中重量大于50千克的过滤出来。于是你草草写了个遍历、判断重量大于150k、返回结果。这时候客户又提了个需求,筛选10万个苹果中绿色的过滤出来。于是你又草草写了个遍历、判断重量大于150k、返回结果。写完之后,你有点不满意,感觉这代码都差不多啊,还很low,不符合我们减少重复代码原则吧,七七八八的,反正感觉很怪,这时候,产品经理又来了,过滤出红色苹果,小于100g的评估吧。。。于是你小脑筋一动,写了下面一行代码,小小难度。。
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());
}
}
public class FilteringApples{
public static void main(String...args){
List<Apple> inventory = Arrays.asList(new Apple(80,"green"),
new Apple(155, "green"),
new Apple(120, "red"));
List<Apple> heavyApples =
filterApples(inventory, new AppleHeavyWeightPredicate());
List<Apple> greenApples =
filterApples(inventory, new AppleGreenColorPredicate());
}
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;
}
}
这时候,你发现,虽然代码不冗余了,但是每次新的获取苹果的需求过来(其实我不爱吃苹果,为啥老是让我写苹果),你都要new一个类,实现接口,你发现好像代码实现起来不麻烦,写的很多。你想了下,搞个匿名类吧,于是有了下面一个实现:
于是java8中提供了一个新的概念,叫方法引用的语法。
你灵机一动,赶紧试了一下效果,哎,还不错哦:
使用lambda表达式:
List<Apple> result =
filterApples(inventory, (Apple apple) -> "red".equals(apple.getColor()));
方法引用
方法引用是使用::语法(能够把这个方法作为"值",传递给方法中的参数,而不需要把该方法包含在一个对象中,在再该对象中使用该方法,使用比较简洁)。可以让你重复使用现有的方法实现并传递他们。
lambda表达式=lambda函数=(匿名函数)
一般我们都讲lambda表达式,可能有些童鞋看到这里会很奇怪,前面说的方法引用语法,其实就是将函数作为值的思想的使用,还包括了lambda(或者匿名函数)。
我们可以理解为匿名函数,它没有名称,但有参数列表、函数主体、返回类型,可能还有一个可以抛出异常的列表。
你可以这样写: (int x)-> x + 1 , 这是一个lambda表达式,表示"调用时,给定参数x,就返回x+1值的函数",在java8之前,我们要写一个工具类,然后类中包含一个add方法去调用并返回。但现在只需要一行代码就解决了这个问题了,这就是lambda表达式的第一个优点: 语法更简洁。
在看一个例子:
通常我们在程序开发时,经常需要遍历list,过滤一些条件,每次大概思路都是 : 遍历时判断条件去过滤:代码如下所示
public class Apple {
public Apple() {
}
public Apple(String color, int weight) {
this.color = color;
this.weight = weight;
}
private String color; // 颜色
private int weight; // 重量
//这里省略了color、weight的getter和setter方法。
public static boolean filterRedApple(Apple apple) {
return "red".equals(apple.getColor());
}
public static boolean filterWeightApple(Apple apple) {
return apple.getWeight() > 150;
}
@Override
public String toString() {
return "Apple [color=" + color + ", weight=" + weight + "]";
}
}
上面我们在过滤超重的苹果时,需要写一个方法,遍历时判断苹果的重量。
当我们换了一种条件时,比如过滤红色的苹果计算苹果的总库存,又要写一个方法来去过滤。就会变得有很多重复的代码。
我们可以使用Predicate :
public static boolean isGreenApple(Apple apple) {
return "green".equals(apple.getColor());
}
public static boolean isHeavyApple(Apple apple) {
return apple.getWeight() > 150;
}
// 这部分可以不写,直接导入即可
public interface Predicate<T>{
boolean test(T t);
}
static List<Apple> filterApples(List<Apple> inventory,
Predicate<Apple> p) {
List<Apple> result = new ArrayList<>();
for (Apple apple: inventory){
if (p.test(apple)) {
result.add(apple);
}
}
return result;
}
要用它的话,你可以写:
filterApples(inventory, Apple::isGreenApple);
或者
filterApples(inventory, Apple::isHeavyApple);
在java8中,把方法作为值来传递显然很有用,但要是为类似于isHeavyApple和isGreenApple这种可
能只用一两次的短方法写一堆定义有点儿烦人。不过Java 8也解决了这个问题,它引入了一套新
写法法(匿名函数或Lambda),让你可以写
filterApples(inventory, (Apple a) -> "green".equals(a.getColor()) );
或者
filterApples(inventory, (Apple a) -> a.getWeight() > 150 );
这里就引入了一个新的概念,匿名函数与函数式接口,关于函数式接口可以看我这篇文章:
https://www.jianshu.com/p/6b1da190f11c
行为参数化
行为参数化是一个很有用的模式,它能够轻松地适应不断变化的需求。这
种模式可以把一个行为(一段代码)封装起来,并通过传递和使用创建的行为(例如对Apple的不同谓词)将方法的行为参数化。前面提到过,这种做法类似于策略设计模式。
网友评论