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 ...
随机推荐
- 根据模板导出Excel报表并生成多个Sheet页
因为最近用报表导出比较多,所有就提成了一个工具类,本工具类使用的场景为 根据提供的模板来导出Excel报表 并且可根据提供的模板Sheet页进行复制 从而实现多个Sheet页的需求, 使用本工具类时 ...
- iOS多线程——同步异步串行并行
串行并行异步同步的概念很容易让人混淆,关于这几个概念我在第一篇GCD中有解释,但是还不够清晰,所以这里重写一篇博客专门对这几个概念进行区分: 先说一下队列和任务: (1)队列分为串行和并行,任务的执行 ...
- 关于连接数据库的T-SQL语句中的一种小技巧
(编程生活中,我们经常会用到数据库.然后在通过T-SQL语句来对数据库进行操作的时候,遇到很多麻烦.话说昨天我就被困扰了一天.明明这个T-sql插数据的语句放在数据库运行的时候没有问题,到了java代 ...
- Windows 10 IoT Serials 7 – 如何用树莓派制作家庭流媒体播放器
Windows 10平台引入了AllJoyn开源软件框架,它提供了一组服务可以创建动态近端网络,让设备可以相互连接实现功能交互.目前,AllJoyn开源软件框架由AllSeen联盟负责管理.AllSe ...
- Java EE基础之JSP(三)
原来准备简单点,用一篇文章介绍完JSP的,没想到细节很多.这是第三篇也是最后一篇,这三篇文章介绍了基本的JSP知识,很多名词概念和用法,一定要在实践项目中使用之后才会有更加亲切的体会.第三篇 ...
- python 、mmap 实现内存数据共享
import mmap mmap_file = None ##从内存中读取信息, def read_mmap_info(): global mmap_file mmap_file.seek(0) ## ...
- Unity 3D Framework Designing(9)——构建统一的 Repository
谈到 『Repository』 仓储模式,第一映像就是封装了对数据的访问和持久化.Repository 模式的理念核心是定义了一个规范,即接口『Interface』,在这个规范里面定义了访问以及持久化 ...
- python操作数据库之批量导入
python操作数据库之批量导入 Python语法简洁清晰,特色之一是强制用空白符(white space)作为语句缩进. Python具有丰富和强大的库.它常被昵称为胶水语言,能够把用其他语言制作的 ...
- 老李秘技:loadrunner回放脚本错误提示Error: "HTTP Status-Code 500"
老李秘技:loadrunner回放脚本错误提示Error: "HTTP Status-Code 500" 当脚本回放的时候出现错误提示Error: "HTTP Sta ...
- ecshop SQL注入漏洞导致代码执行
漏洞名称:ecshop SQL注入漏洞导致代码执行补丁编号:11208761补丁文件:/includes/libinsert.php补丁来源:云盾自研漏洞描述:ecshop的/includes/lib ...