`

Java8初体验(一)lambda表达式语法

 
阅读更多

我就是一个搬运工,有时候收藏后网站链接失效了,唉……

转自:http://ifeve.com/lambda/

 

Java8初体验(一)lambda表达式语法

感谢同事【天锦】的投稿。投稿请联系 tengfei@ifeve.com

本文主要记录自己学习Java8的历程,方便大家一起探讨和自己的备忘。因为本人也是刚刚开始学习Java8,所以文中肯定有错误和理解偏差的地方,希望大家帮忙指出,我会持续修改和优化。本文是该系列的第一篇,主要介绍Java8对屌丝码农最有吸引力的一个特性—lambda表达式。

java8的安装

工欲善其器必先利其器,首先安装JDK8。过程省略,大家应该都可以自己搞定。但是有一点这里强调一下(Windows系统):目前我们工作的版本一般是java 6或者java 7,所以很多人安装java8基本都是学习为主。这样就在自己的机器上会存在多版本的JDK。而且大家一般是希望在命令行中执行java命令是基于老版本的jdk。但是在安装完jdk8并且没有设置path的情况下,你如果在命令行中输入:java -version,屏幕上会显示是jdk 8。这是因为jdk8安装的时候,会默认在C:/Windows/System32中增加java.exe,这个调用的优先级比path设置要高。所以即使path里指定是老版本的jdk,但是执行java命令显示的依然是新版本的jdk。这里我们要做的就是删除C:/Windows/System32中的java.exe文件(不要手抖!)。

 

Lambda初体验

下面进入本文的正题–lambda表达式。首先我们看一下什么是lambda表达式。以下是维基百科上对于”Lambda expression”的解释:

 a function (or a subroutine) defined, and possibly called, without being bound to an identifier。

简单点说就是:一个不用被绑定到一个标识符上,并且可能被调用的函数。这个解释还不够通俗,lambda表达式可以这样定义(不精确,自己的理解):一段带有输入参数的可执行语句块。这样就比较好理解了吧?一例胜千言。有读者反馈:不理解Stream的含义,所以这里先提供一个没用stream的lambda表达式的例子。

1 //这里省略list的构造
2 List<String> names = ...;
3 Collections.sort(names, (o1, o2) -> o1.compareTo(o2));

 

1 //这里省略list的构造
2 List<String> names = ...;
3 Collections.sort(names, new Comparator<String>() {
4   @Override
5   public int compare(String o1, String o2) {
6     return o1.compareTo(o2);
7   }
8 });

上面两段代码分别是:使用lambda表达式来排序和使用匿名内部类来排序。这个例子可以很明显的看出lambda表达式简化代码的效果。接下来展示lambda表达式和其好基友Stream的配合。

1 List<String> names = new ArrayList<>();
2 names.add("TaoBao");
3 names.add("ZhiFuBao");
4 List<String> lowercaseNames = names.stream().map((String name) -> {return name.toLowerCase();}).collect(Collectors.toList());

这段代码就是对一个字符串的列表,把其中包含的每个字符串都转换成全小写的字符串(熟悉Groovy和Scala的同学肯定会感觉很亲切)。注意代码第四行的map方法调用,这里map方法就是接受了一个lambda表达式(其实是一个java.util.function.Function的实例,后面会介绍)。

为什么需要Lambda表达式呢?在尝试回答这个问题之前,我们先看看在Java8之前,如果我们想做上面代码的操作应该怎么办。

先看看普通青年的代码:

1 List<String> names = new ArrayList<>();
2 names.add("TaoBao");
3 names.add("ZhiFuBao");
4 List<String> lowercaseNames = new ArrayList<>();
5 for (String name : names) {
6   lowercaseNames.add(name.toLowerCase());
7 }

接下来看看文艺青年的代码(借助Guava):

1 List<String> names = new ArrayList<>();
2 names.add("TaoBao");
3 names.add("ZhiFuBao");
4 List<String> lowercaseNames = FluentIterable.from(names).transform(new Function<String, String>() {
5   @Override
6   public String apply(String name) {
7     return name.toLowerCase();
8   }
9 }).toList();

在此,我们不再讨论普通青年和文艺青年的代码风格孰优孰劣(有兴趣的可以去google搜索“命令式编程vs声明式编程”)。本人更加喜欢声明式的编程风格,所以偏好文艺青年的写法。但是在文艺青年代码初看起来看起来干扰信息有点多,Function匿名类的构造语法稍稍有点冗长。所以Java8的lambda表达式给我们提供了创建SAM(Single Abstract Method)接口更加简单的语法糖。


Lambda语法详解

我们在此抽象一下lambda表达式的一般语法:

1 (Type1 param1, Type2 param2, ..., TypeN paramN) -> {
2   statment1;
3   statment2;
4   //.............
5   return statmentM;
6 }

从lambda表达式的一般语法可以看出来,还是挺符合上面给出的非精确版本的定义–“一段带有输入参数的可执行语句块”。

上面的lambda表达式语法可以认为是最全的版本,写起来还是稍稍有些繁琐。别着急,下面陆续介绍一下lambda表达式的各种简化版:

1. 参数类型省略–绝大多数情况,编译器都可以从上下文环境中推断出lambda表达式的参数类型。这样lambda表达式就变成了:

1 (param1,param2, ..., paramN) -> {
2   statment1;
3   statment2;
4   //.............
5   return statmentM;
6 }

所以我们最开始的例子就变成了(省略了List的创建):

1 List<String> lowercaseNames = names.stream().map((name) -> {return name.toLowerCase();}).collect(Collectors.toList());

2. 当lambda表达式的参数个数只有一个,可以省略小括号。lambda表达式简写为:

1 param1 -> {
2   statment1;
3   statment2;
4   //.............
5   return statmentM;
6 }

所以最开始的例子再次简化为:

1 List<String> lowercaseNames = names.stream().map(name -> {return name.toLowerCase();}).collect(Collectors.toList());

3. 当lambda表达式只包含一条语句时,可以省略大括号、return和语句结尾的分号。lambda表达式简化为:

1 param1 -> statment

所以最开始的例子再次简化为:

1 List<String> lowercaseNames = names.stream().map(name -> name.toLowerCase()).collect(Collectors.toList());

4. 使用Method Reference(具体语法后面介绍)

1 //注意,这段代码在Idea 13.0.2中显示有错误,但是可以正常运行
2 List<String> lowercaseNames = names.stream().map(String::toLowerCase).collect(Collectors.toList());

 

Lambda表达式眼中的外部世界

我们前面所有的介绍,感觉上lambda表达式像一个闭关锁国的家伙,可以访问给它传递的参数,也能自己内部定义变量。但是却从来没看到其访问它外部的变量。是不是lambda表达式不能访问其外部变量?我们可以这样想:lambda表达式其实是快速创建SAM接口的语法糖,原先的SAM接口都可以访问接口外部变量,lambda表达式肯定也是可以(不但可以,在java8中还做了一个小小的升级,后面会介绍)。

1 String[] array = {"a""b""c"};
2 for(Integer i : Lists.newArrayList(1,2,3)){
3   Stream.of(array).map(item -> Strings.padEnd(item, i, '@')).forEach(System.out::println);
4 }

上面的这个例子中,map中的lambda表达式访问外部变量Integer i。并且可以访问外部变量是lambda表达式的一个重要特性,这样我们可以看出来lambda表达式的三个重要组成部分:

  • 输入参数
  • 可执行语句
  • 存放外部变量的空间

不过lambda表达式访问外部变量有一个非常重要的限制:变量不可变(只是引用不可变,而不是真正的不可变)。

1 String[] array = {"a""b""c"};
2 for(int i = 1; i<4; i++){
3   Stream.of(array).map(item -> Strings.padEnd(item, i, '@')).forEach(System.out::println);
4 }

上面的代码,会报编译错误。因为变量i被lambda表达式引用,所以编译器会隐式的把其当成final来处理(ps:大家可以想象问什么上一个例子不报错,而这个报错。)细心的读者肯定会发现不对啊,以前java的匿名内部类在访问外部变量的时候,外部变量必须用final修饰。Bingo,在java8对这个限制做了优化(前面说的小小优化),可以不用显示使用final修饰,但是编译器隐式当成final来处理。

lambda眼中的this

在lambda中,this不是指向lambda表达式产生的那个SAM对象,而是声明它的外部对象。

 

方法引用(Method reference)和构造器引用(construct reference)

方法引用

前面介绍lambda表达式简化的时候,已经看过方法引用的身影了。方法引用可以在某些条件成立的情况下,更加简化lambda表达式的声明。方法引用语法格式有以下三种:

  • objectName::instanceMethod
  • ClassName::staticMethod
  • ClassName::instanceMethod

前两种方式类似,等同于把lambda表达式的参数直接当成instanceMethod|staticMethod的参数来调用。比如System.out::println等同于x->System.out.println(x);Math::max等同于(x, y)->Math.max(x,y)。

最后一种方式,等同于把lambda表达式的第一个参数当成instanceMethod的目标对象,其他剩余参数当成该方法的参数。比如String::toLowerCase等同于x->x.toLowerCase()。

构造器引用

构造器引用语法如下:ClassName::new,把lambda表达式的参数当成ClassName构造器的参数 。例如BigDecimal::new等同于x->new BigDecimal(x)。

吐槽一下方法引用

表面上看起来方法引用和构造器引用进一步简化了lambda表达式的书写,但是个人觉得这方面没有Scala的下划线语法更加通用。比较才能看出,翠花,上代码!

1 List<String> names = new ArrayList<>();
2 names.add("TaoBao");
3 names.add("ZhiFuBao");
4 names.stream().map(name -> name.charAt(0)).collect(Collectors.toList());

上面的这段代码就是给定一个String类型的List,获取每个String的首字母,并将其组合成新的List。这段代码就没办法使用方法引用来简化。接下来,我们简单对比一下Scala的下划线语法(不必太纠结Scala的语法,这里只是做个对比):

1 //省略List的初始化
2 List[String] names = ....
3 names.map(_.charAt(0))

在Scala中基本不用写lambda表达式的参数声明。

分享到:
评论

相关推荐

    java8 lambda表达式学习总结

    1. **类型推断**:Java 8 可以自动推断Lambda表达式的参数类型,因此在许多情况下,我们无需显式声明类型。 2. **捕获外部变量**:Lambda 表达式可以访问和修改其定义范围内的 final 或基本类型的局部变量。 3. **...

    Java8的lambda表达式

    Java8的Lambda表达式是Java语言的一次重大更新,它引入了函数式编程的概念,极大地简化了处理匿名函数的方式,特别是在处理集合和并发操作时。Lambda表达式使得代码更加简洁、易读,同时也提升了程序的执行效率。在...

    Java 8 lambda表达式

    Java 8 的引入,尤其是Lambda表达式,对Java语言产生了深远的影响,它为Java开发者带来了更简洁、更灵活的编程方式。Lambda表达式是函数式编程的核心元素,它允许我们将函数作为方法参数传递,或者将代码块当作数据...

    Java8新特性之Lambda表达式和Optional类

    Lambda 表达式的语法糖是指 Java 8 中引入的 Lambda 表达式语法,允许开发者使用更简洁的方式编写 Lambda 表达式。Lambda 语法糖包括参数列表、箭头和函数体三个部分。 1.1.1 语法 Lambda 表达式的语法糖可以使用...

    java8中的Lambda表达式

    Java 8 中的 Lambda 表达式是语言的重大更新,它引入了函数式编程的概念,使得代码更加简洁、易读。Lambda 表达式的主要目的是简化处理匿名内部类的情况,尤其是当这些类只需要一个方法时。 在传统的 Java 编程中,...

    670.668.JAVA基础教程_动态代理与Java8新特性-Lambda表达式语法的使用(670).rar

    Lambda表达式是Java 8引入的一种简洁、函数式编程风格的语法糖。它使得可以将匿名函数作为方法参数传递,或者直接在方法内部定义。Lambda表达式的格式如下:`(parameters) -&gt; expression`。这里的"parameters"代表...

    669.667.JAVA基础教程_动态代理与Java8新特性-Lambda表达式语法的使用(669).rar

    在Java 8之前,我们通常需要创建一个匿名内部类来实现单方法接口,而Lambda表达式使得这一过程更加直观和紧凑。Lambda表达式的语法格式为:`(parameters) -&gt; expression`或`(parameters) -&gt; { statements; }`。其中...

    java8新特性,Lambda表达式

    Java 8是Java编程语言的一个重大更新,引入了许多新特性,其中最为显著的就是Lambda表达式。Lambda表达式是函数式编程的关键元素,它允许我们以更简洁的方式编写匿名函数,从而简化了处理集合数据和事件驱动编程的...

    Java8的Lambda表达式

    Java 8引入了Lambda表达式,这是一种简洁的编写代码的方式,可以将代码块作为参数传递给方法,或者作为赋值给变量的对象。Lambda表达式为Java增加了函数式编程的能力,使代码更加简洁和灵活。在Java 8中,Lambda...

    JavaLambda表达式和匿名类.pdf

    Lambda表达式是Java 8中的一种语法糖,它允许开发者以更简洁的方式定义无状态、无副作用的函数。在描述中提到的代码示例中,对比了使用匿名类和Lambda表达式实现相同功能的方法。 首先,让我们看看匿名类的使用。在...

    JAVA 8 Lambda表达式-Lambda Expressions.rar

    Java 8 的 Lambda 表达式是 Java 编程语言的重大革新,它引入了一种新的函数式编程概念,使得代码更加简洁、可读性更强。Lambda 表达式允许我们将函数作为一个方法参数,或者把函数作为方法返回值,极大地简化了处理...

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

    Lambda表达式是Java 8引入的关键特性之一,它极大地简化了函数式编程,使得Java开发者能够更有效地处理并发问题,从而更好地利用现代计算机的多核处理器资源。 Lambda表达式的核心概念在于它的简洁性和可传递性。它...

    java lambda 表达式(语言篇和类库篇)

    Lambda表达式在Java中被设计为一种匿名函数,可以没有名称地传递,并且可以当作参数或返回值。这种特性在处理高阶函数时特别有用,例如在集合API的流(Stream API)操作中。 **语言篇:** 1. **定义与语法**: Java...

    2_Lambda表达式.zip

    Lambda表达式是Java编程语言中的一个关键特性,自Java 8开始引入,它极大地简化了函数式编程,尤其是在处理集合数据时。Lambda表达式的主要目的是为了创建匿名函数,即没有名字的函数,它可以被当作一个值传递给方法...

    Java lambda表达式和JVM字节码功能详解.pdf

    Java Lambda表达式是Java 8引入的一个重要特性,它极大地简化了函数式编程风格的实现。Lambda表达式允许我们将函数作为一个值传递,就像传递其他数据类型一样。这在处理集合、事件驱动编程以及多线程等场景下尤其...

    jdk1.8 Lambda表达式

    Java 8 引入的 Lambda 表达式是 Java 编程语言的重大变革之一,它允许以更简洁的方式表示单方法接口的实现,极大地增强了 Java 语言的表达能力,尤其是结合了函数式接口、Stream API 和其他新特性后。接下来,我们将...

    将字符串转换为lambda表达式

    在编程领域,`lambda`表达式是一种简洁的创建匿名函数的方式,它允许我们在不定义完整函数的情况下使用函数。在Python中,`lambda`表达式特别常见,因为它们可以帮助我们快速编写简洁的一行函数。本文将深入探讨如何...

    【Java8】Lambda表达式 和 Stream API 详解笔记.zip

    Lambda表达式是Java 8引入的一种函数式编程概念,它允许我们将函数作为一个值传递,这在处理匿名函数时非常有用。Lambda表达式的语法结构如下: ```java (参数列表) -&gt; {函数体} ``` 例如,我们可以用Lambda表达式...

Global site tag (gtag.js) - Google Analytics