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 ...
随机推荐
- 1709: [Usaco2007 Oct]Super Paintball超级弹珠
1709: [Usaco2007 Oct]Super Paintball超级弹珠 Time Limit: 5 Sec Memory Limit: 64 MBSubmit: 339 Solved: ...
- sublime 快速安装多个插件的两种方法[Advanced Install Package]与[Package Control.sublime-settings]
环境 sublime 3,目前官网下载的sublime3 已经支持自动安装 Package Control . 第一种: ctrl+shift+p 调出命令面板以后 以前都是使用 Package Co ...
- Redhat
vm1 port:192.168.210.102 user:root;pwd:123456 user:openflowpwd:openflowKkm09!q esx4.1 server 安装一.修改I ...
- [译]Selenium Python文档:一、安装
1.1.简介 Selenium Python为使用Selenium WebDriver来编写功能/验证测试提供了一个简单的API接口.通过Selenium Python API,你可以以一种非常直观的 ...
- [译]Selenium Python文档:六、页面对象
本章是介绍页面对象设计模式的教程.一个页面对象代表了web应用用户接口的一片区域,你的测试代码将与之交互的. 使用页面对象模式的好处: 可以创建在多个测试样例中都可使用的可重用代码 减少重复性代码 如 ...
- Java中static的特点
前两天面试时被问到静态的特点,当时回答地不是很好,现在来总结一下 在了解某样东西的时候我们通常会从它是什么,为什么,和怎么样在三方面来衡量,对于java中的static,我们也这样讨论下,要明确以下几 ...
- Java开发过程中开发工具Eclipse中导入jar包的过程
欢迎欣赏我的第二篇随笔.我们在创建好一个动态网站项目之后,如果没有部署maven的情况下,你可以按照以下的方法,直接把要用的jar包导入你的工程中,而不用再部署maven. 例如在使用JDBC编程时需 ...
- dispaly属性,position属性
position:absolute;绝对定位相对于父元素(父元素设为relative) position:relative;相对定位相对于自己 position:fixed;固定定位相对于浏览器 di ...
- 传输层TCP协议
目录 首部格式数据单位特定注意自动重传请求ARQ具体实现发送缓存接收缓存滑动窗口确认丢失和确认迟到超时重传时间选择报文段的发送时机运输连接发送TCP请求客户端拥塞处理相关概念避免拥塞具体实现TCP 的 ...
- C#调用webbrowser,阻止弹出新HTML页面
参考资料: 1.C#调用webbrowser,阻止弹出新IE窗口 http://www.cnblogs.com/blindman/p/3819649.html 2.[WPF]监听WPF的WebBrow ...