java8 Lambda表达式的新手上车指南(1)

时间:2022-09-11 13:10:52

背景

  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表达式的新手上车指南&lpar;1&rpar;--基础语法和函数式接口

    背景 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个示例&lt&semi;转&gt&semi;

    例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. hadoop2&period;6&period;2&plus;hbase&plus;zookeeper环境搭建

    1.hadoop环境搭建,版本:2.6.2,参考:http://www.cnblogs.com/bookwed/p/5251393.html 启动服务:在master机器上,进入hadoop安装目录, ...

  2. OpenGL程序运行提示glut32&period;dll丢失问题

    转: http://blog.csdn.net/liufeng520/article/details/8064170 今天调试OpenGl的源程序,编译通过,但一运行就提示,计算机丢失 glut32. ...

  3. 使用VisualSVN Server搭建SVN服务器

    工具: TortoiseSVN-1.8.4.24972-x64-svn-1.8.5(客户端) LanguagePack_1.8.4.24972-x64-zh_CN.msi Setup-Subversi ...

  4. office 2013幻灯片中插入SmartArt图形时出现错误下列一个或多个文件由于包含错误而无法运行

    office 2013幻灯片中插入SmartArt图形时出现错误下列一个或多个文件由于包含错误而无法运行 系统:win8 64位 PowerPoint2013 64位 在幻灯片中插入SmartArt图 ...

  5. Linux&lpar;centos&rpar;下安装JDK

    安装 JDK是运行java程序必不可少的环境,服务器上跑程序也不例外.首先在安装之前,要知道Linux下安装软件有两种,一种是使用yum等命令直接下载,一种是使用上传下载工具,上传至Linux下使用, ...

  6. Presto JVM&period;config

    Presto 如果启动时候 指定 CMS,那么 launcher run 会提示 G1 回收算法是 推荐的垃圾回收算法,针对 Presto 大内存 回收,G1 暂时 应该是最稳妥的选择,调整之后大约如 ...

  7. 《Spring实战》学习笔记-第五章:构建Spring web应用

    之前一直在看<Spring实战>第三版,看到第五章时发现很多东西已经过时被废弃了,于是现在开始读<Spring实战>第四版了,章节安排与之前不同了,里面应用的应该是最新的技术. ...

  8. Myeclipse打war包方法

    Myeclipse三种打包方法第一种方法打开Myeclipse如图操作. 如果该项目已存在需要移除之后在进行第三步,接着往下走. 项目打war包成功,推荐使用第一种方法. 第二种方法https://b ...

  9. Xpath注入学习

    xpath简介: 提到xpath就要先说下xml,xml意为可扩展标记语言,简单来说就是一种存储数据的标准格式,可以把他视为一个小型的数据库,他可以解决数据在网上传输的标准问题.是一种比数据库更具通用 ...

  10. 检查 NaN 数据值 &lpar;C&sol;C&plus;&plus;&sol;Python 实现&rpar;

    NaN 是 Not a Number 的缩写.它是一个数值类型值,通常在浮点计算中,表示未定义或无法表示的值.而且,不能直接使用相等运算符 (==) 检查 NaN.由于在程序中,nan == nan ...