文档章节

1、Lambda 表达式基础

MiaoXG
 MiaoXG
发布于 2016/06/22 10:15
字数 2101
阅读 59
收藏 0

1.0 前言

lambda 表达式是 java 8 中最引人注目的新特性,但是它自身的概念并不好理解,为了更好的掌握它,我们必须先了解一下函数式编程。网上关于这方面的介绍有很多,但是大多说的含糊不清。这里提供一篇我认为说的最明白的文章,是一篇译文,如果有疑问还可以直接对比原文。 傻瓜函数式编程

1.1 lambda 表达式是什么?

一个lambda 是一段带有参数的代码块,它可以被传递,因此,它可以执行一次或多次。先从一个简单、经典的比较器例子入手,来感受一下 lambda 表达式

List<String> list = Arrays.asList("aaa", "bbb", "ccc");
list.sort(new Comparator<String>() {
    @Override public int compare(String o1, String o2) {
        return Integer.compare(o1.length(), o2.length());
    }
});

它改写成 lambda 表达式的样子是:

List<String> list = Arrays.asList("aaa", "bbb", "ccc");
list.sort((o1, o2) -> Integer.compare(o1.length(), o2.length()));

1.2 为什么要使用 lambda 表达式?

仔细对比一下上面的例子的两段代码,我们思考几个问题:

  1. 我们为什么要 new 一个比较器? 因为我们需要 compara方法的实现,即 Integer.compare(o1.length(), o2.length())。
  2. 为什么不直接把 compara 的实现代码直接给 sort 方法用? 因为 Java 在之前没有这种传递代码的机制。我们只能把复用的代码封装到一个类里。在面向对象的 Java 里,你不可能将一段代码传来传去,想要向一段代码中传递另一段代码你不得不构造一个属于某个类的对象,由它的某个方法包含所需要的代码。我们一直以这种方式达到我们传代码的目的,但是这样做并不好。

1.3 lambda 表达式语法

简单抽象一下,基本语法的格式是这个样子:

(Type1 param1, Type2 param2, ..., TypeN paramN) -> {
  statment1;
  statment2;
  //.............
  return statmentM;
}

但是上面的例子 (o1, o2) -> Integer.compare(o1.length(), o2.length())) 显然和语法格式不一致,我们继续以这段代码为例,详细说一下 lambda 表达式的语法。 o1, o2 是什么呢?它们都是字符串,Java 是强类型语言,我们必须为每个变量或参数指明类型,一个正经 lambda 的写法如下: 如果一个表达式的参数类型是可以被推导的,那么可以忽略它们的类型

(o1, o2) -> {
    return Integer.compare(o1.length(), o2.length());
}

如果一个表达式的返回类型是可以被推导的,那么它将会从上下文自动推导

(o1, o2) -> Integer.compare(o1.length(), o2.length());

也就变成了例子中的样子。 其它简化写法:如果某个表达式不含有参数,你可以使用一对空的小括号,跟无参方法一样;如果只含有一个参数,且可以被推导,可以不写小括号。

() -> System.out.println("无参的 lambda 表达式");
event -> System.out.println("按钮点击");

1.4 函数式接口

这部分是理解 Java lambda 的重点 对于只包含一个抽象 方法的接口,你可以通过 lambda 表达式创建该接口的对象。这种接口被称为函数式接口

上面已经提到,我们有传递一段代码的需求。同样,Java 中很多已有的接口也需要封装代码块,比如 Runnable、Comparator。使用 lambda 表达式实现本应由这些接口实例实现的功能的行为,即lambda 表达式转换成一个接口的实例叫函数式接口转换(ps:个人理解,非标准定义)。 实际上完成函数式接口的转换是 lambda 表达式在Java 中唯一能做的事。我们以API 中的几段代码来说明什么是函数式接口转换:

public void sort(Comparator<? super E> c) {
    final int expectedModCount = modCount;
    Arrays.sort((E[]) elementData, 0, size, c);
    if (modCount != expectedModCount) {
        throw new ConcurrentModificationException();
    }
    modCount++;
}

上面一段代码是 Arraylist 类中的 sort 方法,从方法签名中的参数列表看,这个方法需要一个参数:比较器实现。回顾一下我们最开始的例子,参数传的是什么? lambda 表达式对不对?

List<String> list = Arrays.asList("aaa", "bbb", "ccc");
list.sort((o1, o2) -> Integer.compare(o1.length(), o2.length()));

即我们以 lambda 表达式实现了接口实例的功能。是怎么实现的呢?list.sort 方法会接收一个实现了 Comparator 接口的类的实例,调用该对象的 compare 方法会执行 lambda 表达式中的代码。

Java API 在 java.util.function 包中定义了一些通用的函数式接口,比如 BiFunction<T, U, R>意思是T,U类型的参数以及R类型的返回值。

BiFunction<String, String, Integer> c = (o1, o2) -> Integer.compare(o1.length(), o2.length());

虽然你可以使用 BiFunction 声明、引用一个表达式,但是并没有什么卵用,因为没有任何一个方法可以接收函数式接口作为参数。

1.5 方法引用

把一个已有的方法传递给其他代码。 比如说上面一直用的排序例子,我们不按长度排序了,现在按字符串大小排序,并且忽略大小写,代码应该是这样子的:

List<String> list = new ArrayList<>();
list.sort((String x, String y) -> x.compareToIgnoreCase(y));

其实我们传递给sort 方法的代码片段就是 String 类的 compareToIgnoreCase 方法。为了更优雅的写代码,API 简化这种写法,即

List<String> list = new ArrayList<>();
list.sort(String::compareToIgnoreCase);

::操作符分隔对象/类名 和 方法名,主要有三种情况

  • 对象::实例方法
  • 类::静态方法
  • 类::实例方法 对于前两种情况,方法引用就是对参数执行该方法。比如打印 list 元素,
list.forEach(System.out::print);  // 相当于 list.forEach(x -> System.out.println(x));

对于第三种情况,第一个参数会成为执行方法的对象,第2个是方法的参数。比如上面的字符串排序 demo。

1.6 构造器引用

构造器引用与方法引用类似,不同的是构造器引用引用的方法名是 new。貌似用处不大,了解即可。

1.7 变量作用域

简单地说,lambda 表达式的变量作用域和内部类的变量作用域类似。

static int b = 10;
public static void main(String[] args) {
    int a = 100;
    Runnable r = () -> {
        a++;       // 错误,不能更改局部变量的值
        b++;         // 正确,但有线程安全问题
        int a = 1;  // 错误,不能与局部变量重名
        int b = 1;  // 正确
    };
    new Thread(r).run();
}

1.8 默认方法

接口可以有实现方法,而且不需要实现类去实现其方法。只需在方法名前面加个default关键字即可。

为什么要定义默认方法? Java 的接口是不可扩展的,一旦你为某个父接口增加了一个方法,你就必须为所有的实现类增加这个方法的实现。 因为新增了对 lambda 表达式的支持,现在 Java 有了传递代码块的能力,所以对集合的处理有了更简洁、高效的方式,比如,输出集合元素:

// old
for(String s : list) {
   System.out.println(s);
}
// now
list.forEach(System.out::println);

但是,如果给Collection 或 Iterator 增加forEach 接口,会要求所有的实现类增加 forEach 的实现,显然这是无法接受的。所以,为了解决接口的修改与现有的实现不兼容的问题,引入了默认方法。 在JVM中,默认方法的实现是非常高效的,并且通过字节码指令为方法调用提供了支持。 使用规则 如果接口接口定义了一个默认方法,而另一个父类/接口有定义了一个同名方法,该选择哪个? 1.选择父类中的方法。如果父类中提供了实现,忽略接口中的方法。 2.接口冲突。如果多个父接口中都提供了对同一方法的实现,接口中的默认方法都忽略,实现类必须自己定义实现。

1.9 接口中的静态方法

Java 8 中,接口中允许添加静态方法。比如我们在 章节“5.3 比较器” 中使用到的 Comparator.comparing 方法。使用静态方法,代码会更简洁。我们再以开头的那段代码作为例子:

List<String> list = Arrays.asList("aaa", "bbb", "ccc");
list.sort(new Comparator<String>() {
    @Override public int compare(String o1, String o2) {
        return Integer.compare(o1.length(), o2.length());
    }
});

可以写成

List<String> list = Arrays.asList("aaa", "bbb", "ccc");
list.sort(Comparator.comparing(String::length));

© 著作权归作者所有

上一篇: 2、Stream API
下一篇: Gradle配置
MiaoXG
粉丝 2
博文 19
码字总数 17098
作品 0
朝阳
程序员
私信 提问
Java中的Lambda表达式

最近看到一段代码, 里面用到了lambda表达式。刚开始学习java语法的时候对于这些东西只是草草看了一下,以后也没再用,到现在都忘干净了。突然感觉自己的基础很薄弱呀,,,所以重新学了一下...

zq17865815296
2017/11/30
0
0
C# Lambda表达式详解,及Lambda表达式树的创建

  最近由于项目需要,刚刚学完了Action委托和Func委托,发现学完了委托就必须学习lambda表达式,委托和Lambda表达式联合起来,才能充分的体现委托的便利、才能使代码更加简介、优雅。 Lamb...

雲霏霏
2014/07/15
0
0
语法基础——Kotlin语法基础

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/qq_30379689/article/details/81984443 主函数 变量 1、常量 修饰常量,编译器规定常量必须初始化,若不想初始...

Hensen_
2018/08/23
0
0
C++11 lambda 表达式

C++11 lambda表达式 在C++ 03标准中,并没有lambda这个概念,对于C++来说,boost库提供了lambda的接口。在C++11中,引进了lambda表达式,这也可能是面向对象语言中,比较晚引进这个概念的语...

刘大神
2015/05/25
701
2
Python中 lambda函数 含有多个参数的疑惑理解

lambda的基本语法和基础应用,网上很多。这里就不多说了。这里主要说下lambda函数与其他函数嵌套使用的问题。以下面的函数为例 输出结果: 1、 test(1)中的1 并没有被传到lambda函数里。 2、...

月伯乐
2017/10/26
357
0

没有更多内容

加载失败,请刷新页面

加载更多

Dubbo-自适应拓展机制

背景 在 Dubbo 中,很多拓展都是通过 SPI 机制进行加载的,比如 Protocol、Cluster、LoadBalance 等,这些都是Dubbo的基础组件。这些基础组件的拓展不是在系统框架启动阶段被加载,而是拓展方...

rock-man
19分钟前
4
0
Kali安装fcitx输入法(五笔)

安装fcitx > sudo apt-get install fcitx-rime fcitx-config-gtk3 重启 > sudo reboot fcitx配置 效果就是这样 配置输入法切换 系统设置...

yeahlife
21分钟前
3
0
IE之css3效果兼容

本文转载于:专业的前端网站▷IE之css3效果兼容 一、兼容css阴影效果(ie滤镜) 1.Shadow,阴影 .shadow { -moz-box-shadow: 3px 3px 4px #000; -webkit-box-shadow: 3px 3px 4px #000; box-sha...

前端老手
24分钟前
3
0
NiushopB2C开源商城功能列表说明:

B2C单商户免费版:PC商城+微商城 B2C单商户标准版:PC商城+微商城组合套餐+阶梯优惠核销功能 B2C单商户企业版:PC商城+微商城拼团+组合套餐阶梯优惠+核销功能 B2C单商户分销版:PC商城+微商城...

niushop-芳
25分钟前
3
0
图片如何转GIF图片呢

如何将生活中拍摄的好玩有趣的图片制作成GIF动图呢?相信很多小伙伴都不知道要如何制作,其实制作方法非常的简单,下面分享一个图片转GIF动图的方法,希望这个方法能够帮助大家在与好友斗图时...

白米稀饭2019
32分钟前
3
0

没有更多内容

加载失败,请刷新页面

加载更多

返回顶部
顶部