接口和内部类为我们提供了一种将接口与实现分离的更加结构化的方法。
对于创建类,几乎在任何时刻,都可以替代为创建一个接口和一个工厂。但是这种抽象性应该是应真正的需求而产生,草率的进行设计优化,可能会带来额外的复杂性。对于接口与类的选择,恰当的原则是:优先选择类而不是接口。从类开始,如果接口的必需性变得非常明确,那么就进行重构。接口是一种重要的工具,但是它们容易被滥用,从而带来额外的复杂性。
抽象类和抽象方法
包含抽象方法的类叫做抽象类,如果一个类包含一个或多个抽象方法,该类必须被限定为抽象的。
如果从一个抽象类继承,并想创建该新类的对象,那么就必须为基类中的所有抽象方法提供方法定义。如果不这样做(可以选择不做),那么导出类便也是抽象类,且编译器会强制用 abstract
关键字来限定这个类。1
2
3
4
5
6
7
8
9
10
11
12
13
14
15abstract class A {
abstract void f();
}
class B extends A {
void f() {
}
}
abstract class C extends A {
}
也可以创建一个没有任何抽象方法的抽象类。如果有一个类,让其包含任何 abstract
方法都显得没有实际意义,而且我们也想要阻止产生这个类的任何对象,那么这样做就很有用。1
2
3
4
5
6
7
8abstract class A {
void f() {
System.out.println("A");
}
}
A a = new A(); // 'A' is abstract; cannot be instantiated
接口
interface
这个关键字产生一个完全抽象的类,它根本就没有提供任何具体的实现。它允许创建者确定方法名、参数列表和返回类型,但是没有任何方法体。接口只提供了形式,而未提供任何具体实现。
要想创建一个接口。需要用 interface
关键字来代替 class
,就像类一样,可以在 interface
关键字前面添加 public
(但仅限于该接口在与其同名的文件中被定义)。如果不添加 public
,则它只具有包访问权限。接口也可以包含域,但是这些域隐式地是 static
和 final
的。接口中的方法隐式地是 public
的。
要让一个类遵循某个特定接口(或者是一组接口),需要使用 implements
关键字。
完全解耦
只要一个方法操作的是类而非接口,那么就只能使用这个类及其子类,而不能使用不在此继承结构中的某个类。接口可以在很大程度上放宽这种限制,使得复用代码变得简单。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// Apply.selectF 与 A 耦合过紧,所使用的类只能是 A 及 A 的子类
class A {
Object f(Object input) {
return input;
}
}
class B extends A {
String f(Object input) { // 返回类型为协变类型
return ((String) input).toUpperCase();
}
}
class C extends A {
String f(Object input) {
return ((String) input).toLowerCase();
}
}
class Apply {
// 策略设计模式,根据传递的参数对象不同而具有不同的行为
// 这个例子中,A 就是一个策略,具有两种不同类型
public static void selectF(A a, Object input) {
System.out.println(a.f(input));
}
}
String s = "hello";
Apply.selectF(new B(), s); // HELLO
Apply.selectF(new C(), s); // hello
1 | // 将 A 改为接口,使得可以应用于多种场景 |
Java 中的多重继承
一个类可以继承任意多个接口,并可以向上转型为每个接口。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
34class A {
// Implements method in C via sub-class K
public void f() {
System.out.println("A");
}
}
interface B {
void fb();
}
interface C {
void f();
}
// C 与 A 中的 f() 方法的特征签名相同
// 所以当 K 中没有定义 f() 时,C 的 f() 由 A 来定义
class K extends A implements B, C {
public void fb() {
System.out.println("B");
}
}
A a = new K();
a.f(); // A
B b = new K();
b.fb(); // B
C c = new K();
c.f(); // A
通过继承来扩展接口
通过继承,可以在接口中添加新的方法声明,也可以整合数个接口。另外组合接口时还需要注意方法之间的冲突,例如:两个接口中的方法,仅仅只是方法的返回类型不同,这会使得编译器无法区分。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
30interface A {
void fa();
}
interface B {
void fb();
}
// extends 多继承仅适用于接口继承
interface C extends A, B {
void fc();
}
class K implements C {
public void fa() {
}
public void fb() {
}
public void fc() {
}
}
嵌套接口
1 | // 类嵌套接口 |
1 | // 接口嵌套接口 |
接口与工厂
接口是实现多重继承的途径,而生成遵循某个接口的对象的典型方式就是工厂方法设计模式。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
50interface Service {
void f();
}
interface ServiceFactory {
Service getService();
}
class Factory {
public static void ServiceConsumer(ServiceFactory serviceFactory) {
Service service = serviceFactory.getService();
service.f();
}
}
class A implements Service {
public void f() {
System.out.println("A");
}
}
class AFactory implements ServiceFactory {
public Service getService() {
return new A();
}
}
class B implements Service {
public void f() {
System.out.println("B");
}
}
class BFaotory implements ServiceFactory {
public Service getService() {
return new B();
}
}
Factory.ServiceConsumer(new AFactory()); // A
Factory.ServiceConsumer(new BFaotory()); // B