为什么要使用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基础练习题1

    深深感知python基础是有多么重要,Ljh说一定要多练题,so,我现在开始要每天打卡练习python.加油! 01:求‘1-100’的偶数和 #第一种解法: sum=0 num=0 while nu ...

  2. 如何判断系统是32位还是64位的linux系统

    如何判断系统是32位还是64位的linux系统 某日,需要下载个安装包,忽然忘记了自己的系统是32位还是64位的系统了,一时想不起来怎么查看时32位还是64位,呵呵,随便百度下,就发现有好多方法,这里 ...

  3. Vue刷新token,判断token是否过期

    1.判断token是否过期,前端请求后,后台会返回一个状态给你.根据状态判断是否过期,刷新token 2.是否每次请求后端都会返回新的token给你.或者后端给你定义了一个刷新token的方法,那此时 ...

  4. extjs定时器TaskRunner

    Extjs封装了一个定时器类叫TaskRunner,该类在Ext.util下,先看看其公共方法: TaskRunner( Number interval )   interval : Number ( ...

  5. spring中试用junit4测试

    一:加入jar包 <!-- 单元测试 --> <dependency> <groupId>junit</groupId> <artifactId& ...

  6. CF D. Labyrinth 01BFS

    由于上下走不限制,所以按照贪心,我们应该尽可能走上下方向. 我们可以开一个双端队列,并认为每次提取队首的时候得到的是到达该点的最优策略.(这个一定是唯一的,因为不可能向右走几格,然后再退回去. ) 那 ...

  7. Codeforces 878A - Short Program(位运算)

    原题链接:http://codeforces.com/problemset/problem/878/A 题意:给出n个位运算操作, 化简这些操作, 使化简后的操作次数不多于5步. 思路:我们可以对二进 ...

  8. 3D Computer Grapihcs Using OpenGL - 01 环境设置

    这系列文章是我学习Youtube上一套OpenGL教程的笔记,自己对教程的案例重新制作并且做了一定程度的修改(更有条理,且修正了一些问题).后续将持续更新. Visual Studio 2017工程 ...

  9. Presenter 层

    后是 Presenter 层,它是处理业务逻辑和业务数据的,所以必须持有 Model 的引用,同时要将处理完的数据交给 View 层用于显示,也必须持有 View 的引用,那么,一开始我们就要把这两层 ...

  10. 关于Java泛型实现原理的思考与一般用法示例总结

    面向对象的一个重要目标是对代码重用的支持.支持这个目标的一个重要机制就是泛型机制.在1.5版本之前,java并没有直接支持泛型实现,泛型编程的实现时通过使用继承的一些基本概念来完成的. 这种方式的局限 ...