《Java 8 函数式编程》笔记3

类库

默认方法

Collection 接口中新增了 stream 方法,如果继承它的子类没有实现 stream 方法,就使用它的 stream 方法,这样的方法叫默认方法。

Iterable 接口中也新增了一个默认方法:forEach,允许用户使用 lambda 表达式作为循环体。

JDKforEach 的实现方法:

1
2
3
4
5
default void forEach(Consumer<? super T> action) {
for (T t : this) {
action.accept(t);
}
}

默认方法和子类

Parent 接口定义了默认方法 welcome,而 ParentImpl 类没有实现 welcome 方法,因此它自然继承了默认方法。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
public class Main {
public static void main(String[] args) {
Parent parent = new ParentImpl();
parent.welcome();
assert "Parent: Hi!".equals(parent.getLastMessage());
}
}

// 实现接口时没有实现 welcome
class ParentImpl implements Parent {
String body;

@Override
public void message(String body) {
this.body = body;
}

@Override
public String getLastMessage() {
return this.body;
}
}

interface Parent {
void message(String body);

// 默认方法
default void welcome() {
message("Parent: Hi!");
}

String getLastMessage();
}

新增一个 Child 接口,该类继承 Parent 接口,并且重写 Parent 的默认方法:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
public class Main {
public static void main(String[] args) {
Child child = new ChildImpl();
child.welcome();
assert "Child: Hi!".equals(child.getLastMessage());
}
}

class ChildImpl implements Child {
String body;

@Override
public void message(String body) {
this.body = body;
}

@Override
public String getLastMessage() {
return this.body;
}
}

interface Child extends Parent {
@Override
default void welcome() {
message("Child: Hi!");
}
}

interface Parent {
void message(String body);

default void welcome() {
message("Parent: Hi!");
}

String getLastMessage();
}

现在,默认方法成了虚方法。
任何时候,一旦子类定义的方法和父类的产生冲突,都会优先选择子类定义的方法:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
public class Main {
public static void main(String[] args) {
Parent parent = new OverridingParent();
// 调用的是类的具体方法,而不是默认方法
parent.welcome();
assert "Override Parent".equals(parent.getLastMessage());
}
}

class OverridingParent extends ParentImpl {
// 重写 welcome 默认实现的父类
@Override
public void welcome() {
message("Override Parent");
}
}

class ParentImpl implements Parent {
String body;

@Override
public void message(String body) {
this.body = body;
}

@Override
public String getLastMessage() {
return this.body;
}
}

interface Parent {
void message(String body);

default void welcome() {
message("Parent: Hi!");
}

String getLastMessage();
}

新增 OverridingChild 类,该类本身并没有任何操作,只是继承 Child 接口和 OverridingParent 类。
但调用的 welcome 方法来自 OverridingParent 类。

原因:与 Child 接口定义的默认方法相比,OverridingParent 类中重写后的 welcome 方法更具体。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
public class Main {
public static void main(String[] args) {
Child child = new OverridingChild();
child.welcome();
assert "Override Parent".equals(child.getLastMessage());
}
}

class OverridingChild extends OverridingParent implements Child {

}

class OverridingParent extends ParentImpl {
@Override
public void welcome() {
message("Override Parent");
}
}

interface Child extends Parent {
@Override
default void welcome() {
message("Child: Hi!");
}
}

class ParentImpl implements Parent {
String body;

@Override
public void message(String body) {
this.body = body;
}

@Override
public String getLastMessage() {
return this.body;
}
}

interface Parent {
void message(String body);

default void welcome() {
message("Parent: Hi!");
}

String getLastMessage();
}

多重继承

接口允许多重继承,因此有可能遇到 2 个接口包含签名相同的默认方法的情况:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
// 编译器会报错
// 因为 javac 不明确继承了哪个接口的 rock 方法
class MusicalCarriage implements Carriage, Jukebox {

}

interface Jukebox {
default String rock() {
return "...all over the world";
}
}

interface Carriage {
default String rock() {
return "...from side to side";
}
}

解决:可以使用增强的 super 语法,指定使用某个接口的默认方法。

1
2
3
4
5
6
class MusicalCarriage implements Carriage, Jukebox {
@Override
public String rock() {
return Carriage.super.rock();
}
}

三定律

如果对多重继承下的默认方法工作原理没有把握,可以参考以下3条:

  1. 类 > 接口。如果继承链中有方法体或抽象的方法声明,那就可以忽略接口中定义的方法。
  2. 子类 > 父类。如果一个接口继承了另一个接口,且两个接口都定义了一个默认方法,则子类优先。
  3. 没有3。如果以上2条不适用,子类要么实现该方法,要么将该方法声明为抽象方法。

Optional

Optional 是核心类库新设计的数据类型,用来替换 null 值。

Optional 对象相当于值的容器,可以使用 get() 获得该值。

创建某个值的 Optional 对象:

1
2
Optional<String> a = Optional.of("a");
assert "a".equals(a.get());

Optional 对象可以为空:

1
2
3
4
5
6
7
8
9
10
// 创建空 Optional 对象
Optional<String> emptyOptional = Optional.empty();

// 将空值转换为空 Optional 对象
Optional<String> alsoEmpty = Optional.ofNullable(null);

// isPresent() 检查 Optional 对象是否有值
assert emptyOptional.isPresent() == false;

assert alsoEmpty.isPresent() == false;

但对象为空时,如果希望使用备选值,可以使用 orElse
如果备选值计算太繁琐,可以使用 orElseGet,该方法接受 Supplier 对象。

1
2
3
4
5
6
7
Optional<String> emptyOptional = Optional.empty();

Optional<Integer> alsoEmpty = Optional.ofNullable(null);

assert "b".equals(emptyOptional.orElse("b"));

assert 3 == alsoEmpty.orElseGet(() -> 1 + 2);
Author

Zoctan

Posted on

2018-03-05

Updated on

2023-03-14

Licensed under