前言

Java 8是Java开发的一个主要版本,也是一个有着重大改变的版本,在此对Java 8的部分新特性进行总结,主要总结以下几个部分:

  • Lambda表达式
  • 函数式接口
  • 默认方法
  • Stream

Lambda表达式

Lambda表达式能够让我们把函数作为方法参数,或者把代码作为数据对待,使用Lambda表达式可以使代码变得更加简洁紧凑。语法如下:

  1. 方法体为表达式,该表达式的值作为返回值返回:(parameters) -> expression
  2. 方法体为代码块,必须用{}包裹起来,且需要有一个return返回值:(parameters) -> { statements; }

Lambda表达式还有以下几个特征:

  • 可选类型声明:不需要声明参数类型,编译器可以统一识别参数值
  • 可选的参数圆括号:一个参数无需定义圆括号,但多个参数需要定义圆括号

Lambda表达式的简单例子:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15

// 1. 不需要参数,返回值为 5
() -> 5

// 2. 接收一个参数(数字类型),返回其2倍的值
x -> 2 * x

// 3. 接受2个参数(数字),并返回他们的差值
(x, y) -> x – y

// 4. 接收2个int型整数,返回他们的和
(int x, int y) -> x + y

// 5. 接受一个 string 对象,并在控制台打印,不返回任何值(看起来像是返回void)
(String s) -> System.out.print(s)

Lambda表达式的原理:

  • 在类编译时,会生成一个私有静态方法 + 一个内部类
  • 在内部类中实现了函数式接口,在实现接口的方法中,会调用编译器生成的静态方法
  • 在使用lambda表达式的地方,通过传递内部类实例,来调用函数式接口方法

函数式接口

函数式接口就是一个有且仅有一个抽象方法,但是可以有多个非抽象方法的接口。函数式接口的重要特点是,我们能够使用Lambda实例化它们,如定义了一个函数式接口如下:

1
2
3
4
5
@FunctionalInterface
interface GreetingService
{
void sayMessage(String message);
}

那么就可以使用Lambda表达式来表示该接口的一个实现(Java 8之前一般是用匿名类实现):

1
GreetingService greetService1 = message -> System.out.println("Hello " + message);

注意该函数式接口上有一个新的注解@FunctionalInterface,该接口不是必须的,用来标记该接口为只允许有一个抽象方法的函数式接口,当接口不符合函数式接口定义的时候,编译器会报错。

默认方法

接口的默认方法就是接口可以有实现方法,而且不需要实现类去实现其方法,我们只需要在方法名前面加个default关键字即可实现默认方法。

为什么要有这个特性呢?首先,之前的接口是个双刃剑,好处是面向抽象而不是面向具体编程,缺陷是当需要修改接口的时候,需要修改全部实现了该接口的类,但是如果我们想给接口添加新方法的同时不影响已有的实现,就可以使用默认方法这个特性,解决接口的修改与现有的实现不兼容的问题。

如果一个类实现了多个接口,并且这些接口有相同的默认方法,此时可以覆盖接口的默认方法,也可以使用super来调用指定接口的默认方法:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
public class DefaultMethodTest implements A, B {
@Override
public void print() {
A.super.print();
}
}

interface A{
default void print(){
System.out.println("a");
}
}

interface B{
default void print(){
System.out.println("b");
}
}

Java 8还可以在接口中提供静态方法的实现,例如:

1
2
3
4
5
interface C{
static void hello(){
System.out.println("hello");
}
}

Stream

流式操作分为中间操作和最终操作两种,最终操作返回一特定类型的结果,而中间操作返回流本身,这样就可以将多个操作依次串联起来。

生成流

在Java 8中,集合接口有两个方法来生成流:

  • stream():为集合创建串行流
  • parallelStream():为集合创建并行流

forEach

Stream 提供了新的方法forEach来迭代流中的每个数据。以下代码片段使用forEach输出了10个随机数:

1
2
Random random = new Random(); 
random.ints().limit(10).forEach(System.out::println);

需要注意的是,forEach是一个终止操作,也就是说该操作必须是流的最后一个操作,一旦被调用,Stream就不能再使用了。

filter

filter方法用于通过设置的条件过滤出元素。以下代码片段使用filter方法过滤出空字符串:

1
2
List<String>strings = Arrays.asList("abc", "", "bc", "efg", "abcd","", "jkl"); // 获取空字符串的数量
int count = strings.stream().filter(string -> string.isEmpty()).count();

limit

limit方法用于获取指定数量的流。 以下代码片段使用limit方法打印出10条数据:

1
2
Random random = new Random();
random.ints().limit(10).forEach(System.out::println);

sorted

sorted方法用于对流进行排序。以下代码片段使用sorted方法对输出的10个随机数进行排序:

1
2
Random random = new Random(); 
random.ints().limit(10).sorted().forEach(System.out::println);

并行程序

parallelStream是流并行处理程序的代替方法。以下实例我们使用parallelStream来输出空字符串的数量:

1
2
List<String> strings = Arrays.asList("abc", "", "bc", "efg", "abcd","", "jkl"); // 获取空字符串的数量 
int count = strings.parallelStream().filter(string -> string.isEmpty()).count();

我们可以很容易的在顺序运行和并行直接切换。

参考资料