为什么要使用lambda表达式

从函数式接口说起

理解Functional Interface(函数式接口)是学习Java8 lambda表达式的关键所在。

函数式接口的定义其实很简单:任何接口,如果只包含唯一一个抽象方法,那么它就是一个函数式接口。对于函数式接口,我们可以通过lambda表达式来创建该接口的对象。

为了让编译器帮助我们确保一个接口满足函数式接口的要求,也就是说有且仅有一个抽象方法。Java8提供了@FunctionalInterface注解。举个简单的例子,Runnable接口就是一个FI,下面是它的源代码:

@FunctionalInterface
public interface Runnable {
public abstract void run();
}

使用@FunctionalInterface注解并不强制要求。但是使用注解会让代码看上去更清楚。

lambda表达式的语法糖

语法糖

Java中lambda表达式的格式:参数、箭头->、一个表达式。
为了演示lambda表达式的语法糖,我们通过Comparator作为例子。在Java8之前,可以借助匿名内部类来实现。

public static List<String> compareTest1(){
List<String> wordList = Arrays.asList("lianggzone", "spring", "summer", "autumn", "winter");
wordList.sort(new Comparator<String>() {
@Override
public int compare(String w1, String w2) {
return Integer.compare(w1.length(), w2.length());
}
});
return wordList;
}

Comparator是个函数式接口,我们可以用Lambda表达式来实现。Lambda表达式,很像一个匿名的方法,只是小括号内的参数列表和花括号内的代码被->分隔开了。

public static List<String> compareTest2(){
List<String> wordList = Arrays.asList("lianggzone", "spring", "summer", "autumn", "winter");
wordList.sort((String w1, String w2) -> {
return Integer.compare(w1.length(), w2.length());
});
return wordList;
}

如果一个lambda表达式的参数类型是可以被推导的,那么就可以省略它们的类型。

public static List<String> compareTest3(){
List<String> wordList = Arrays.asList("lianggzone", "spring", "summer", "autumn", "winter");
wordList.sort((w1, w2) -> {
return Integer.compare(w1.length(), w2.length());
});
return wordList;
}

如果一个lambda表达式的代码块只是return后面跟一个表达式,那么还可以进一步简化。

public static List<String> compareTest4(){
List<String> wordList = Arrays.asList("lianggzone", "spring", "summer", "autumn", "winter");
wordList.sort((w1, w2) -> Integer.compare(w1.length(), w2.length()));
return wordList;
}

如果某个方法只含有一个参数,并且该参数的类型可以被推导出来,你甚至可以省略小括号。

public static List<String> compareTest5(){
List<String> wordList = Arrays.asList("lianggzone", "spring", "summer", "autumn", "winter");
wordList.forEach(word -> System.out.println(word));
return wordList;
}

当我们要在另外一个独立线程中执行一些逻辑时,通常会将代码放在一个实现Runable接口的类的run方法中。

public static void runnableTest1(){
Executors.newSingleThreadExecutor().execute(new Runnable() {
@Override
public void run() {
for (int i = 0; i < 10; i++) {
System.out.println(i);
}
}
});
}

如果lambda表达式没有参数,你仍可以提供一对空的小括号,如同不含参数的方法。

public static void runnableTest2(){
Executors.newSingleThreadExecutor().execute(() -> {
for (int i = 0; i < 10; i++) {
System.out.println(i);
}
});
}

注意点

  • lambda表达式执行返回类型,会根据上下文推到出来,我们不需要设置它的返回类型。
  • lambda表达式中,只有某些分支中返回值,这样是错误的。

方法引用

有些时候,lambda表达式的代码就只是一个简单的方法调用而已,遇到这种情况,lambda表达式还可以进一步简化为方法引用。
::操作符将方法名和对象或类的名字分隔开来。

对象::实例对象

在Java8之前,我们打印List内容,正常是这样做的。

public static void print1(){
List<String> wordList = Arrays.asList("lianggzone", "spring", "summer", "autumn", "winter");
wordList.forEach(new Consumer<String>() {
@Override
public void accept(String word) {
System.out.println(word);
}
});
}

因为Consumer是函数式接口,我们通过lambda表达式改造下。

public static void print2(){
List<String> wordList = Arrays.asList("lianggzone", "spring", "summer", "autumn", "winter");
wordList.forEach(word -> System.out.println(word));
}

如果改造成方法引用,表达式 System.out::println, 等价于word -> System.out.println(word)。

public static void print3(){
List<String> wordList = Arrays.asList("lianggzone", "spring", "summer", "autumn", "winter");
wordList.forEach(System.out::println);
}

类::实例对象

例如,我们不区分大小写对字符串进行排序。

public static String[] sort1(){
String[] words = new String[]{"lianggzone", "spring", "summer", "autumn", "winter"};
Arrays.sort(words, (x, y) -> x.compareToIgnoreCase(y));
return words;
}

如果改造成方法引用,表达式: String::compareToIgnoreCase,等价于(x, y) -> x.compareToIgnoreCase(y),第一参数会成为执行方法的对象。

public static String[] sort2(){
String[] words = new String[]{"lianggzone", "spring", "summer", "autumn", "winter"};
Arrays.sort(words, String::compareToIgnoreCase);
return words;
}

对象::静态方法

例如,我们对集合进行排序。

public static List<Integer> sortList1(){
List<Integer> wordList = Arrays.asList(21, 53);
wordList.sort((w1, w2) -> Integer.compare(w1, w2));
return wordList;
}

如果改造成方法引用,表达式: Integer::compare,等价于(w1, w2) -> Integer.compare(w1, w2),第一参数会成为执行方法的对象。

public static List<Integer> sortList2(){
List<Integer> wordList = Arrays.asList(21, 53);
wordList.sort(Integer::compare);
return wordList;
}

构造器引用

// lambda
words.stream().map(word -> {
return new StringBuilder(word);
});
// constructor reference
words.stream().map(StringBuilder::new);

变量作用域

在Java8之前, 内部类只能访问final的局部变量 ,为了适应lambda表达式,Java8放宽了这种限制,只要变量实际上不可变(effectively final)就可以。

public static void effectivelyFinal(){
int a = 100;
Executors.newSingleThreadExecutor().execute(() -> {
System.out.println(a);
});
}

在lambda表达式中,被引用的变量的值不能被修改。做出这个约束的是有原因的,因为lambda表达式中的变量不是线程安全的。

接口的静态方法

从Java8开始,接口也可以有静态方法了。有了这个语法,我们就可以把和接口相关的帮助方法直接定义在接口里。

比如Function接口就定义了一个工厂方法indentity()。表示, 一个功能接口,可以作为赋值的目标一个lambda表达式或方法参考。

T -函数输入的类型
R -函数的结果的类型

public interface Function<T, R> {
static <T> Function<T, T> identity() {
return t -> t;
}
}

事实上,Java8中,很多接口已经添加了静态方法,例如,Java8中的一个使用案例

public interface Path {
public static Path get(String first, String... more) {
return FileSystems.getDefault().getPath(first, more);
}
}

所以,重要的事情,再说一遍哟,从Java8开始,接口也可以有静态方法了。

原文链接:http://blog.720ui.com/2016/java_se8_01_lambda/

原文标题及作者:Java8 特性详解(一) Lambda | 梁桂钊的博客

Java8 特性详解(一) Lambda的更多相关文章

  1. Java8特性详解 lambda表达式 Stream

    1.lambda表达式 Java8最值得学习的特性就是Lambda表达式和Stream API,如果有python或者javascript的语言基础,对理解Lambda表达式有很大帮助,因为Java正 ...

  2. Java8特性详解 lambda表达式 Stream【转】

    本文转自http://www.cnblogs.com/aoeiuv/p/5911692.html 1.lambda表达式 Java8最值得学习的特性就是Lambda表达式和Stream API,如果有 ...

  3. Atitit.jdk java8的语法特性详解 attilax 总结

    Atitit.jdk java8的语法特性详解 attilax 总结 1.1. 类型推断这个特别有趣的.鲜为人知的特性1 2. Lambda1 2.1. 内部迭代意味着改由Java类库来进行迭代,而不 ...

  4. Java基础学习总结(33)——Java8 十大新特性详解

    Java8 十大新特性详解 本教程将Java8的新特新逐一列出,并将使用简单的代码示例来指导你如何使用默认接口方法,lambda表达式,方法引用以及多重Annotation,之后你将会学到最新的API ...

  5. Java8 Stream新特性详解及实战

    Java8 Stream新特性详解及实战 背景介绍 在阅读Spring Boot源代码时,发现Java 8的新特性已经被广泛使用,如果再不学习Java8的新特性并灵活应用,你可能真的要out了.为此, ...

  6. C#各个版本中的新增特性详解

    序言 自从2000年初期发布以来,c#编程语言不断的得到改进,使我们能够更加清晰的编写代码,也更加容易维护我们的代码,增强的功能已经从1.0搞到啦7.0甚至7.1,每一次改过都伴随着.NET Fram ...

  7. Java9 新特性 详解

    作者:木九天   <   Java9 新特性 详解  > Java9 新特性 详解 摘要: 1.目录结构 2.repl工具 jShell命令 3.模块化 4.多版本兼容jar包 5.接口方 ...

  8. 点击--》java9 新特性 详解

    引言: 点击-->java9 新特性 详解 点击-->java8 新特性 详解 正题: 1.局部变量var 将前端思想var关键字引入java后段,自动检测所属于类型,一种情况除外,不能为 ...

  9. java10 新特性 详解

    引言: 点击-->java9 新特性 详解 点击-->java8 新特性 详解 正题: 1.局部变量var 将前端思想var关键字引入java后段,自动检测所属于类型,一种情况除外,不能为 ...

随机推荐

  1. 匿名函数 python内置方法(max/min/filter/map/sorted/reduce)面向过程编程

    目录 函数进阶三 1. 匿名函数 1. 什么是匿名函数 2. 匿名函数的语法 3. 能和匿名函数联用的一些方法 2. python解释器内置方法 3. 异常处理 面向过程编程 函数进阶三 1. 匿名函 ...

  2. 主席树(静态区间第k大)

    前言 如果要求一些数中的第k大值,怎么做? 可以先就这些数离散化,用线段树记录每个数字出现了多少次. ... 那么考虑用类似的方法来求静态区间第k大. 原理 假设现在要有一些数 我们可以对于每个数都建 ...

  3. 【NOIP2016提高组A组7.16】第三条跑道

    题目 数据范围 分析 时限5000ms. 我们注意到\(a_{i}初始值以及x小于等于600且非零\) 也就是说,\(a_{i}\)的质因数一定小于600,而600以内的质因数只有109个. 那么考虑 ...

  4. vue组件学习(一)

    1, vue中的 is 的用法,有时候我们需要把一个组件绑定到指定的标签下,比如把tr组件放到table下,直接这样写是不行的, <!DOCTYPE html> <html lang ...

  5. 对Nuxt的研究

    Nuxt就是基于Vue的一个应用框架,采用服务端渲染,让你的SPA应用(Vue)也可以拥有SEO Nuxt的生命周期有些在服务端(Node),客户端,甚至两边都在: 1.其他之前都不存在Window对 ...

  6. 给网页头部标题加logo

    现在在写公司的官网,需要在网页的头部加logo,没有加的时候是这样的 那么,现在只要一步,就可以了,加上一行代码 <link rel="icon" href="图标 ...

  7. CruiseControl.NET配置

    CruiseControl.NET简介 CruiseControl.NET是.net平台下,一个开源的自动化持续集成工具. 它是一个程序套件,但其核心是一个叫做CruiseControl.NET Se ...

  8. np.asarray(a, dtype=None, order=None)

    np.asarray(a, dtype=None, order=None) 参数a:可以是,列表, 列表的元组, 元组, 元组的元组, 元组的列表,多维数组 参数dtype=None, order=N ...

  9. C++编译-链接错误集合

    1,无法解析的外部符号,链接错误,原因:没找到某个符号(变量或函数)的定义体,一般是对应函数没实现,或第三方库没有添加到工程设置中 2,重复链接链接错误,一个定义体(实现体)被多个CPPP文件包含,导 ...

  10. layer系列之弹层layer.prompt

    layer官网:https://www.layui.com/doc/modules/layer.html layer在线调试:http://layer.layui.com/ 如何使用layer.pro ...