lambda表达式

lambda表达式

1.lambda表达式的定义

​ 可以把lambda表达式理解为一种可传递的匿名函数,有参数列表、函数主题,返回类型,可能还有一个可抛出的异常列表

​ 匿名:不需要拥有一个明确的名称

​ 函数:不属于某个特定的类,但和方法一样拥有参数列表、函数主题,返回类型,可能还有一个可抛出的异常列表。

​ 传递:可以作为参数传递给方法或存放再变量中

​ 简洁: 无需像匿名类那样写很多模板代码

1.1用Comparator排序

Java中List(实际是Collections)有一个sort方法,可用java.util.Comparator进行参数化,如这里有一个Comparator,实现按照苹果的重量排序

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

采用lambda表达式的话

apples.sort((Apple a1,Apple a2)->
             a1.getWeight().compareTo(a2.getWeight())
        );

2.使用lambda表达式

2.1函数式接口

定义:只定义了一个抽象方法的接口

举例:Comparator,Runnable

Lambda表达式允许直接以内联的形式为函数式接口的抽象方法提供实现,并把整个表达式作为函数式接口的实例。即Lambda表达式可以作为一个函数式接口的具体实现。

函数描述符:接口的抽象方法。

函数式接口中定义了一个抽象方法,这个抽象方法的签名(入参,返回)就是Lambda表达式的签名。

2.2编写lambda表达式

1.行为参数化

2.使用函数式接口传递行为

3.执行一个行为

4.传递lambda

直接以筛选苹果为例

目的:筛选出所有重量大于150g的苹果

1.行为参数化

显然,这里的行为指的是筛选重量大于150g的苹果,即

return apple.getWeight()>150;

因此,这里筛选苹果的代码应为

List<Apple> resultList5 = FilterApple.filterApplesByPredicate(apples,(Apple apple)-> apple.getWeight()>150);
        for (Apple apple : resultList5){
            System.out.println(apple.getId());
        }

调用的筛选方法为

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

可以看到,在filterApplesByPredicate()方法中参数ApplePredicate,我们传入的是

(Apple apple)-> apple.getWeight()>150

2.使用函数式接口传递行为

使用lambda表达式,势必要有对应的函数式接口(只有一个抽象方法的接口),这里我们定义的函数式接口为ApplePredicate

@FunctionalInterface
public interface ApplePredicate {

    boolean test(Apple apple);
}

3.执行一个行为

可以看到,在filterApplesByPredicate()方法中,我们调用了这一行为参数,即

predicate.test(apple)

调用ApplePredicate.test()对Apple进行处理

4.传递lambda

实际上,在开头我们就是一次成功的传递lambda。

List<Apple> resultList5 = FilterApple.filterApplesByPredicate(apples,(Apple apple)-> apple.getWeight()>150);

同理,进行不同的筛选肯定还有

List<Apple> resultList5 = FilterApple.filterApplesByPredicate(apples,(Apple apple)-> apple.getWeight()<100);
List<Apple> resultList5 = FilterApple.filterApplesByPredicate(apples,(Apple apple)-> apple.getColor().equals("red")); //字符串比较时最好是"red"等确保有值的String写在前面调用equals方法,避免NPE

2.3使用函数式接口

首先了解一些函数式接口

函数式接口 参数类型 返回类型 描述
Supplier T 接收一个T类型的值
Consumer T 处理一个T类型的值
BiConsumer<T, U> T,U 处理T类型和U类型的值
Predicate T boolean 处理T类型的值,并返回true或者false.
ToIntFunction T int 处理T类型的值,并返回int值
ToLongFunction T long 处理T类型的值,并返回long值
ToDoubleFunction T double 处理T类型的值,并返回double值
Function<T, R> T R 处理T类型的值,并返回R类型值
BiFunction<T, U, R> T,U R 处理T类型和U类型的值,并返回R类型值
BiFunction<T, U, R> T,U R 处理T类型和U类型的值,并返回R类型值
UnaryOperator T T 处理T类型值,并返回T类型值,
BinaryOperator T,T T 处理T类型值,并返回T类型值

如果写过stream的话,可以看到实际上stream里很多操作都是通过上述函数式接口实现的,在这里简单列几个

Stream<T> filter(Predicate<? super T> predicate);
<R> Stream<R> map(Function<? super T, ? extends R> mapper);
IntStream mapToInt(ToIntFunction<? super T> mapper);
void forEach(Consumer<? super T> action);

这里,我们用stream简单写一下筛选出重量大于150的并打印他们。

apples.stream().filter(apple->apple.getWeight()>180)
        .forEach(apple -> {System.out.println(apple);});

在这个stream计算里,我们调用了2个stream的操作:filter(),forEach()

从上面这两个方法的定义可以看出

fiter方法接收一个Predicate接口行为作为参数,

forEach方法接收一个Consumer接口行为作为参数

这一个stream通过调用2个函数式接口实现了我们的编码目标。

2.4 类型检查、类型推断以及限制

2.4.1 类型检查

Lambda的类型从使用Lambda啥的上下文推断出来。上下文(接受它传递的方法的参数,或接受它的值的局部变量)中Lambda表达式需要的类型称为目标类型

以2.2表达式为例

List<Apple> resultList5 = FilterApple.filterApplesByPredicate(apples,(Apple apple)-> apple.getWeight()>150);

使用Lambda的上下文是什么呢?首先我们看一下filterApplesByPredicate的定义

public static List<Apple> filterApplesByPredicate(List<Apple> apples, ApplePredicate predicate)

显然,目标类型为ApplePredicate predicate

而这个ApplePredicate 接口的抽象方法为

boolean test(Apple apple);

接收一个Apple,返回一个boolean,函数描述符 Apple ->boolean 匹配lambda的签名,即接收一个Apple,返回boolean,因为代码类型检查没问题。

2.4.2 同一个Lambda表达式,不同的函数式接口

由上一小节可知lambda的类型检查是通过目标类型的校验完成的,显然,只要满足类型检查,lambda可以用于不同的函数式接口。

注:如果一个lambda表达式的主体是一个语句表达式,他就和一个返回void的函数描述符兼容

2.4.3类型推断

上述的lambda表达式还可以写成这样

List<Apple> resultList5 = FilterApple.filterApplesByPredicate(apples,apple-> apple.getWeight()>150);

比较不同,可以看到从

(Apple apple)-> apple.getWeight()>150

变为

apple-> apple.getWeight()>150

对于后者,java编译器从上下文(目标类型)推断出用什么函数式接口来配合lambda表达式。

2.4.4 使用局部变量

lambda可以使用自由变量,,即可以在主体中引用实例变量和静态变量,但局部变量必须显式声明为final,或者事实上fianl,就是说lambda表达式只能捕获指派给他们的局部变量一次。

2.5方法引用

方法引用可以重复使用现有的方法定义,并且像lambda一样传递它们。

(Apple a) -> a.getWeight() =================>   Apple::getWeight
(String s) -> System.out.println(s) =============> System.out::println
2.5.1如何构建方法引用

方法引用主要有3类

(1) 指向静态方法的方法引用。

Integer.parseInt(String s)    ============> Integer::parseInt

(2) 指向任意类型实例方法的方法引用。

String.length()  =================> String::length

(3)指向现有对象的实例方法的方法引用

apple.getWeight() =============> Apple::getWeight
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 159,458评论 4 363
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 67,454评论 1 294
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 109,171评论 0 243
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 44,062评论 0 207
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 52,440评论 3 287
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 40,661评论 1 219
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 31,906评论 2 313
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 30,609评论 0 200
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 34,379评论 1 246
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,600评论 2 246
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 32,085评论 1 261
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,409评论 2 254
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 33,072评论 3 237
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 26,088评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,860评论 0 195
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 35,704评论 2 276
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 35,608评论 2 270

推荐阅读更多精彩内容