概述
Java 8 自发布以来,已过了好几个年头,渐渐地,它已成为主流。在这个版本,是一个拥有丰富特性的主要版本。如果还不熟悉它的使用姿势,赶紧学习起来吧。在编程过程中,会发现它真的值得拥有!
本文将描述 Java 8 的 Lambda表达式,默认方法以及接口中的静态方法。
Lambda 表达式
Lambda 表达式是一个新的语言特性,它可以让我们将功能视为方法参数,或者将代码视为数据。使用 Lambda 表达式,我们可以更简洁地表示单方法接口(称为功能接口)的实例。
这个特性带来的帮助非常大。在以前,经常羡慕 JS能够拥有如下简便的写法:
尽管这种写法饱受诟病,但如果在 Java 中可以选择的话,我们还是应该拥抱它。
Java 文档中关于 Lambda 表达式的介绍是紧挨着匿名类的。所以,不难想象它们之间的关联。
匿名类可以实现一个基类而不给它名字,虽然这比命名类更加简洁,但对于只有一个方法的类,即使是匿名类也显得有点麻烦。我们使用匿名类和 Lambda 表达式的来对集合进行遍历,代码如下:
Lambda 表达式语法如下:
-
用逗号分隔的形式参数列表,用括号括起来;
Note: 数据类型可以省略,只有一个参数还可以省略括号,例如,上图的
element
。 -
标识符: ->
-
主体:放在 {} 内。
Note:如果主体仅仅是一个表达式或者或一个语句块,{} 和 ; 可以省略。只存在单条返回语句的话,return 关键字也可以省略。
我们对集合进行遍历,做一个完整的演进:
不过,Lambda 表达式只能用于功能接口(函数式接口),也就是说只能用在一个接口除了含有单个抽象方法和 Object
中的 public
访问修饰符定义的方法外,不能再含有其它抽象方法。Java 中常见的 Comparator
便是一个很好的例子。
@FunctionalInterface
interface Comparator<T> {
boolean equals(Object obj);
int compare(T o1, T o2);
}
尽管,Comparator
拥有两个抽象方法,但 equals
方法属于 Object
的公共方法,所以,Copmarator
仍可被视作函数式接口,也可以用 Lambda 表达式来表达。
那么,如何理解 Lambda 表达式和函数式接口的关系呢?
我们可以这样理解:对lambda表达式的计算将生成函数式接口的实例。不过请放心,这并不会导致表达式主体的执行,相反,表达式主体只会在调用函数式接口的方法时执行。
还需要注意的是在 Lambda 表达式中,使用外部变量需要变量是 final
或者 等效 final
的。等效 final 是指变量在初始化后,未再被重新赋值。
方法引用
方法引用可看作对 Lambda 表达式在特定场景下的一种简写。这里的特定场景指的便是将已具有名称的方法用作 Lambda 表达式。
方法引用分为四种:
- 引用一个静态方法:
ContainingClass::staticMethodName
- 引用一个特定对象的实例方法:
containingObject::instanceMethodName
- 引用特定类型的任意对象的实例方法:
ContainingType::methodName
- 引用一个构造函数:
ClassName::new
我们通过代码来分析该如何使用:
Student.java
map(Student::getName)
是引用特定类型的任意对象的实例方法。map
接受的函数式接口是 Function
,定义如下:
@FunctionalInterface
public interface Function<T, R> {
R apply(T t);
}
也就是说需要一个入参,并产生返回值,在这里,入参的类型是 Student
,那么如上写法,最终产生的影响是:
将数组中的单个 Student 对象作为参数传入 apply 方法,并调用该 student 对象的 getName
方法作为返回值。
map(Student::new)
是引用一个构造函数,这里,入参的类型是 String,那么这种写法产生的影响是:传入一个 string,并调用 Student 的有参构造函数。这里也可写为 map(Student::newInstance)
,通过静态方法的形式来产生一个对象。
其实分析以上不难发现,方法引用需要已有的方法 “符合” 函数式接口定义的抽象方法。这个 “符合” 指的是:
-
如果函数式接口定义的抽象方法,有返回值,那么方法引用所指向的方法也必须有返回值。
-
如果函数式接口定义的抽象方法,有入参,那么可分为以下两种:
-
静态方法,特定对象的实例方法,构造函数 : 这三种方法引用所指向的方法都必须有对应入参;
-
引用特定类型的任意对象的实例方法:这种方法引用所指向的方法可以没有形式参数。当方法执行时,会使用传入的实参对象来调用所指向的实例方法,所以它们的类型必须匹配。
Note:针对特定类型的任意对象的实例方法,我们可以想象为针对传入这一函数式接口方法的入参都需要执行该实例方法的调用,所以也就不再需要指定具体的对象实例,例如上面的
map(Student::getName)
。
-
依赖于编程工具的智能,其实,我们能够很容易地发现方法引用的问题所在,并很快地解决它!不过学习并知悉语言特性,而不单纯地依赖编程工具,仍然是作为一名合格程序员所必备的技能。
默认方法
默认方法指的是在接口中可以实现方法,所以在 接口和抽象类有什么不同? 便不要再回答抽象类可以有方法的实现,而接口不可以这一条了。
在 Java 版本的迭代中,一切都在发生着变化。唯一不变的可能便是兼容性这一保证了。后续的版本需要兼容前面的版本,被添加有默认方法的接口仍然在二进制上保持着兼容。
Comparator.java
default Comparator<T> reversed() {
return Collections.reverseOrder(this);
}
默认方法其实是为了帮助我们使用上 Lambda,它可以将接受 Lambda 表达式作为参数的方法添加到现有的接口中。其中最主要的可能便是集合类接口了,为了实现 Stream 操作,它添加了大量的默认方法,支持接收 Lambda 表达式作为参数。
Stream 作为一大特性,也通过结合 Lambda 表达式来使流操作更加的简单明了。我们不再关心怎么做,而只关心做什么,这可能是流式操作结合Lambda表达式能给我们带来的最直观的感受了!
接口中的静态方法
除了默认方法外,还可以在接口中定义静态方法。静态方法是与定义它的类而不是与任何对象关联的方法。类的每个实例都共享它的静态方法。
Comparator.java
public static <T extends Comparable<? super T>> Comparator<T> reverseOrder() {
return Collections.reverseOrder();
}
在接口中的所有方法,包括静态方法,都是隐式声明 public
的,所以可以省略 public
。
写在最后
关于 Lambda 表达式,默认方法以及接口中的静态方法介绍便是这些了。相信在不断地编程中,我们会越来越熟悉它们的使用,就像传统的语法使用一样。所以,现在就开始吧!
这是 Java 8 新特性系列 的第一篇文章,如果你觉得我的文章还不错,并对后续文章感兴趣的话,或者说我们有什么能够交流分享的,可以通过扫描下方二维码来关注我的公众号。赞!
认认真真学习,做思想的产出者,而不是文字的搬运工。错误之处,还望指出!
最后
以上就是外向溪流为你收集整理的Java 8 Lambda 表达式默认方法的全部内容,希望文章能够帮你解决Java 8 Lambda 表达式默认方法所遇到的程序开发问题。
如果觉得靠谱客网站的内容还不错,欢迎将靠谱客网站推荐给程序员好友。
发表评论 取消回复