java8 Lambda表达式的新手上车指南(1)
背景
java9的一再推迟发布,似乎让我们恍然想起离发布java8已经过去了三年之久,java8应该算的上java语言在历代版本中变化最大的一个版本了,最大的新特性应该算得上是增加了lambda表达式,借助lambda表达式,我们可以编写出性能更好,可读性更强的代码,更重要的,它给我们带来了一种编程思想的改革,作为一个活跃了20多年的编程语言,java能够做到与时俱进,拥抱新变化,实属不易,虽然现在很多公司包括我所在的公司尚未把jdk升级到最新的版本,但关注并学习每一个版本带来的新变化,是每个java程序员都该做的事,因为正是这些变化代表了这门语言未来的发展方向.可以预想,几年以后lambda表达式必将在整个java开发领域完成普及和应用,因此现在,对我们来说,是时候把lambda表达式学起来了,废话就说这么多,下面就让我们了解一下lambda的强大.
入门
从外部迭代到内部迭代
拿一个简单的例子来说,如果我们需要遍历一个List集合,需要怎么做,一般是下边这样:
List<String> lists=Arrays.asList("a","b","c","d");
for (String s:lists){
System.out.println(s);
}
java8给我提供了Collection.forEach()方法,于是我们可以这样编程:
List<String> lists=Arrays.asList("a","b","c","d");
lists.forEach(i-> System.out.println(i));
这是一个最简单的例子,但是我们能看出来一点,之前遍历是写在外部的,即客户端代码,而使用了lambda表达式将迭代的操作放到了forEach()方法中,即封装到了java类库中,这样对外界是没有任何侵入性的,现在不用纠结于这么一行代码到底是怎么实现的,是什么意思,下面会慢慢剖析.
lambda表达式基础
定义
在数学计算的角度,lambda表达式指的是一种函数,但在java中现在还不能编写一个独立的函数,毕竟java不是函数式编程语言,在java中,lambda可以看做一种匿名方法,一种格式非常简洁的匿名方法,可以省略修饰符,返回类型等.
语法
lambda表达式可以分解成三部分来看: (1)参数列表 (2)-> (3)lambda体,如上面例子中的
i -> System.out.println(i))
左边是参数列表,lambda可以接收任意多个参数,当参数数量大于1时,需要将参数括起来,如
(i,j)->System.out.println(i+j))
如果不需要指定参数,需要使用()来表示无参,如
()->System.out.println("hello lambda")
可以看到我们并没有声明参数的数据类型,这是因为很多情况下,编译器能从上下文中推导出数据的参数类型,当然我们可以显示的指定类型
(int i,int j)->System.out.println(i+j))
函数箭头的右侧是lambda体,上面的例子中只有一行代码,当有多行代码时,需要使用{}括起来,如
(i,j) ->{System.out.println(i);System.out.println("----"); }
如果lambda体中的表达式有返回值,需要使用return来返回,也可以后面跟一个参数来中止lambda体,
(i,j)->return i+j 或 (i,j)->i+j;
函数式接口
理解函数式接口,是学习lambda表达式的关键,函数式接口的定义其实比较简单,对于任意一个接口(interface),如果他只包含一个(抽象)方法,那么这个接口就可以称之为函数式接口,这种接口被@FunctionalInterface注解标示,现在我们来回忆一下在java8以前,我们经常碰到的函数式接口.
public interface Runnable { void run(); }
public interface Callable<V> { V call() throws Exception; }
public interface Comparator<T> { int compare(T o1, T o2); boolean equals(Object obj); }
现在我们以第三个方法Comparator来举例,以往我们使用这个类来实现自定义功能一般是这样的,这里假设我们需要以Person的age来对Person对象进行排列,一般这样实现:
Person[] persons=new Person[]{new Person("a",7),new Person("b",9),new Person("c",5)};
Arrays.sort(persons, new Comparator<Person>() {
public int compare(Person o1, Person o2) {
return Integer.compare(o1.getScore(),o2.getScore());
}
});
System.out.println(Arrays.toString(persons));
Person类这里不再给出定义,Person有两个属性name和age.运行程序,在打印台我们可以看到输出顺序是 c,a,b
我们已经知道Comparator是一个函数式接口,我们可以使用lambda表达式来得到一个实例,现在我们来观察sort方法的第二个参数,它接受一个Comparator对象用于定义比较的规则,new Comparator<Person>表示new一个Comparatotr对象,这不是废话吗,sort方法已经定义了第二个参数必须为Comparator对象,因此这段代码是可以省略的,因为聪明的编译器可以从上下文中推测出来,那让我们来看一下,还有什么东西是编译器能够推测出来的,我们稍加思索便会发现,public int compare,返回类型,o1,o2的数据类型,都是可以推测出来的,因为只有这一个方法嘛,那么使用lambda改造一下的模样是这样的.
Person[] persons=new Person[]{new Person("a",7),new Person("b",9),new Person("c",5)};
Arrays.sort(persons, ( o1, o2) -> Integer.compare(o1.getScore(),o2.getScore()) );
System.out.println(Arrays.toString(persons));
一个匿名内部类对象就这样被我们用lambda表达式改造成上面这样子,我们可以这么理解,上面红色加粗的代码,就相当于创建了一个Comparator对象并重写了compare方法内容.这时候,你该问了,这不就是一个匿名内部类的语法糖吗?事实上,从语法上看,的确很像一个语法糖,但二者之间存在很多显著的差异.这里暂时不做深入讨论.
在java8中引入了一个新的包java.util.function,这个包中定义了很多函数式接口用于支持lambda表达式,下面简单介绍几个常见接口.
Function接口,有一个参数,并且返回一个结果,观察该接口,我们发现Function能接受一个T类型的参数并返回R类型,这里使用泛型有更好的扩展性.
@FunctionalInterface
public interface Function<T, R> {
R apply(T t);
}
下面我们使用Function接口来写一个小例子,如下:
public static int changeTheValue(int value ,Function<Integer,Integer> function){
return function.apply(value);
}
@Test
public void testChangeTheValue(){
int value=10;
int result1=changeTheValue(value,i->i+5);
System.out.println("result1:"+result1);
int result2=changeTheValue(value,(i)->i*30);
System.out.println("result2"+result2);
}
上述代码中,我们定义了一个方法changeTheValue,接收两个参数,第一个是需要修改的value,第二个是一个function对象,在下面的测试方法中,我们根据自己的想法灵活的改变第二个参数,可以得到对象的结果.运行测试,可以得到result1:15 result2:300.
除了Function接口之外,类库还提供了一个Consumer接口,他和Function接口唯一的区别就是,方法没有返回值,还记得最上面我们遍历集合的时候使用的list.forEach方法吗,它接受的就是一个Consumer实例.
类库给我们预定义的函数式接口,当然不止上面提的这两个,还有一个Predicate,他返回一个boolean类型的值,用来判断某项条件是否满足,经常用来进行筛滤操作,其他接口这里也不再讨论.
总结
我们初步认识了lambda的简单语法结构: 参数列表->lambda体
lambda的函数式接口(目标类型),java8引进了用于支持lambda表达式的java.util.Function接口
lambda表达式的应用场景之一是替换之前广泛使用的匿名内部类的常规语法.
参考资料
<精通lambda表达式:java多核编程>
java8 Lambda表达式的新手上车指南(1)的更多相关文章
- java8 Lambda表达式的新手上车指南(1)--基础语法和函数式接口
背景 java9的一再推迟发布,似乎让我们恍然想起离发布java8已经过去了三年之久,java8应该算的上java语言在历代版本中变化最大的一个版本了,最大的新特性应该算得上是增加了lambda表达式 ...
- Java8 Lambda表达式详解手册及实例
先贩卖一下焦虑,Java8发于2014年3月18日,距离现在已经快6年了,如果你对Java8的新特性还没有应用,甚至还一无所知,那你真得关注公众号"程序新视界",好好系列的学习一下 ...
- Java8 Lambda表达式(一)
目录 一.应用场景引入 优化一:使用策略模式 优化二:使用匿名内部类 优化三:使用Lambda表达式 优化四:使用Stream API 二.Lambda运算符和对应语法 语法格式 Lambda表达式需 ...
- Java8 Lambda表达式、函数式接口和方法引用
目录 Java8 Lambda表达式和函数式接口 Lambda表达式 Lambda的使用 函数式接口FunctionalInterface Java内置四大核心函数式接口 方法引用 构造器引用 Jav ...
- 【Java学习笔记之三十一】详解Java8 lambda表达式
Java 8 发布日期是2014年3月18日,这次开创性的发布在Java社区引发了不少讨论,并让大家感到激动.特性之一便是随同发布的lambda表达式,它将允许我们将行为传到函数里.在Java 8之前 ...
- java8 快速入门 lambda表达式 Java8 lambda表达式10个示例
本文由 ImportNew - lemeilleur 翻译自 javarevisited.欢迎加入翻译小组.转载请见文末要求. Java 8 刚于几周前发布,日期是2014年3月18日,这次开创性的发 ...
- Java8 lambda表达式10个示例
Java 8 刚于几周前发布,日期是2014年3月18日,这次开创性的发布在Java社区引发了不少讨论,并让大家感到激动.特性之一便是随同发布的lambda表达式,它将允许我们将行为传到函数里.在Ja ...
- Java8 lambda表达式10个示例<转>
例1.用lambda表达式实现Runnable 我开始使用Java 8时,首先做的就是使用lambda表达式替换匿名类,而实现Runnable接口是匿名类的最好示例.看一下Java 8之前的runna ...
- java8 lambda表达式应用
1.用lambda表达式实现Runnable非常简单// Java 8之前: new Thread(new Runnable() { @Override public void run() { Sys ...
随机推荐
- UINavigationBar统一修改导航条样式
#pragma mark -- 统一导航条样式 //统一导航条样式 UIFont *font = [UIFont systemFontOfSize:19.f]; NSDictionary *textA ...
- Python的.py文件打包成exe可执行文件
前几天做了几个简单的爬虫python程序,于是就想做个窗口看看效果. 首先是,窗口的话,以前没怎么接触过,就先考虑用Qt制作简单的ui.这里用前面sinanews的爬虫脚本为例,制作一个获取当天sin ...
- ICC_lab总结——ICC_lab3:布局
PS:字丑,禁止转载. 做到了ICC workshop的第三个实验,理论与实践相结合,于是,先放一张总结图,这张总结图来自前面的博客,放在这里用来体现理论和实践的联系: 这个就是布局的理论部分,在IC ...
- SQL AlawaysOn 之一:安装域控制器
一.准备阶段 1. 计算机名称命名 2.IP地址修改.DNS修改 IP地址和DNS不一定要和图上的一致,只要固定就行了 二.安装阶段 1.服务器管理器,仪表盘,点击“添加角色和功能” 2.添加角色和 ...
- Android开发之NavigationView的使用
NavigationView主要是和DrawerLayout框架结合使用,来完成抽屉导航实现侧边栏 引用一段官方文档的示例代码 <android.support.v4.widget.Drawer ...
- java学习笔记 --- 条件,循环语句
一.三元运算符 A:格式 比较表达式?表达式1:表达式2; B:执行流程: 首先计算比较表达式的值,看是true还是false. 如果是true,表达式1就是结果. 如果是 ...
- 彻底理解容器类(2)------- AbstractCollection深入了解
h2 { background-color: Skyblue } AbstractCollection认识 AbstractCollection是Collection接口的抽象实现.实现了一部分Col ...
- SEQ序号与ACK序号理解总结
备查 SEQ序号与ACK序号理解总结
- 使用Python对Excel进行读写操作
学习Python的过程中,我们会遇到Excel的读写问题.这时,我们可以使用xlwt模块将数据写入Excel表格中,使用xlrd模块从Excel中读取数据.下面我们介绍如何实现使用Python对Exc ...
- Akari谜题(关灯问题)的开灯解法
提高解时预处理的速度 本方法的结果是得到满足所有黑色有数字方块的一个带有未照亮的块的可能解集. 解集大小为 4~(3号块数量+1号块数量)+6~(2号块数量)-灯互相照射到的解的集合.集合中的灯为黄色 ...