背景

  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表达式的应用场景之一是替换之前广泛使用的匿名内部类的常规语法.

参考资料

 Java8 Lambda表达式教程

 <精通lambda表达式:java多核编程>

java8 Lambda表达式的新手上车指南(1)的更多相关文章

  1. java8 Lambda表达式的新手上车指南(1)--基础语法和函数式接口

    背景 java9的一再推迟发布,似乎让我们恍然想起离发布java8已经过去了三年之久,java8应该算的上java语言在历代版本中变化最大的一个版本了,最大的新特性应该算得上是增加了lambda表达式 ...

  2. Java8 Lambda表达式详解手册及实例

    先贩卖一下焦虑,Java8发于2014年3月18日,距离现在已经快6年了,如果你对Java8的新特性还没有应用,甚至还一无所知,那你真得关注公众号"程序新视界",好好系列的学习一下 ...

  3. Java8 Lambda表达式(一)

    目录 一.应用场景引入 优化一:使用策略模式 优化二:使用匿名内部类 优化三:使用Lambda表达式 优化四:使用Stream API 二.Lambda运算符和对应语法 语法格式 Lambda表达式需 ...

  4. Java8 Lambda表达式、函数式接口和方法引用

    目录 Java8 Lambda表达式和函数式接口 Lambda表达式 Lambda的使用 函数式接口FunctionalInterface Java内置四大核心函数式接口 方法引用 构造器引用 Jav ...

  5. 【Java学习笔记之三十一】详解Java8 lambda表达式

    Java 8 发布日期是2014年3月18日,这次开创性的发布在Java社区引发了不少讨论,并让大家感到激动.特性之一便是随同发布的lambda表达式,它将允许我们将行为传到函数里.在Java 8之前 ...

  6. java8 快速入门 lambda表达式 Java8 lambda表达式10个示例

    本文由 ImportNew - lemeilleur 翻译自 javarevisited.欢迎加入翻译小组.转载请见文末要求. Java 8 刚于几周前发布,日期是2014年3月18日,这次开创性的发 ...

  7. Java8 lambda表达式10个示例

    Java 8 刚于几周前发布,日期是2014年3月18日,这次开创性的发布在Java社区引发了不少讨论,并让大家感到激动.特性之一便是随同发布的lambda表达式,它将允许我们将行为传到函数里.在Ja ...

  8. Java8 lambda表达式10个示例<转>

    例1.用lambda表达式实现Runnable 我开始使用Java 8时,首先做的就是使用lambda表达式替换匿名类,而实现Runnable接口是匿名类的最好示例.看一下Java 8之前的runna ...

  9. java8 lambda表达式应用

    1.用lambda表达式实现Runnable非常简单// Java 8之前: new Thread(new Runnable() { @Override public void run() { Sys ...

随机推荐

  1. 本机安装mysql服务,步骤教程(另附SQLyog和Navicat工具)

    因为这段时间不是装系统就是换硬盘,导致装了还几次MySql,每次都记不住都要上网找教程,着实麻烦,所以这次干脆直接写到博客上好了,便于自己也便于他人: 百度云:http://pan.baidu.com ...

  2. Ubuntu 开机启动是出现 grub rescue 解决办法

    最近想在Ubuntu的基础上,再在硬盘的一个分区安装Windows,其中有次不小心,在安装windows的时候,删除了一个分区,造成下次启动Ubuntu系统出现 grub rescue 下面是我的修复 ...

  3. Dollar Dayz poj3181

    http://poj.org/problem?id=3181 这个题目一开始就能看出来是个dp问题,但是我并没有一开始就看出来是一个完全背包为题,只是想着根据以前的方法,这个问题应该是可以找到规律的, ...

  4. HTML5学习笔记<四>: 列表, 块和布局

    HTML列表 列表标签 标签 描述 <ol> 定义有序列表. <ul> 定义无序列表. <li> 定义列表项. <dl> 定义定义列表. <dt& ...

  5. webpack 打包js和css

    首先进行全局安装webpack npm install -g webpackcmd跳转到项目的文件夹,安装webpack npm install --save-dev webpack接着需要packj ...

  6. 关于修改编JDK编译环境的问题

    最近从svn下拿的代码本地编译测试总是会出一个Unsupported major.minor version 51.0...的问题,出现这样的问题基本上都是因为JDK版本的问题,由于高版本JDK编译的 ...

  7. SaaS模式应用之多租户系统开发(单数据库多Schema设计)

    SaaS是Software-as-a-Service(软件即服务)的简称,这边具体的解释不介绍. 多租户的系统可以应用这种模式的思想,将思想融入到系统的设计之中. 一.多租户的系统,目前在数据库存储上 ...

  8. 一个基于php+mysql的外卖订餐网站(带源码)

    订饭组 一个基于php+mysql的外卖订餐网站,包括前端和后台.源码地址 源码演示地址:http://dingfanzu.com 商家后台系统:http://dingfanzu.com/admin ...

  9. 译:Missing index DMV的 bug可能会使你失去理智---慎重看待缺失索引DMV中的信息

    注: 本文译自https://www.sqlskills.com/blogs/paul/missing-index-dmvs-bug-that-could-cost-your-sanity/ 原文作者 ...

  10. ELK整体方案

    # ELK日志搜集平台解决方案---------1. 硬件设备2. 系统环境3. elasticsearch 集群部署4. kibana 部署5. logstash 部署6. filebeat 部署7 ...