书中源代码:https://github.com/yangxian1229/ThinkingInJava_SourceCode
接口和内部类为我们提供了一种将接口与实现分离的更加结构化的方法。
9.1 抽象类和抽象方法
抽象方法,仅有声明而没有方法体。语法如下:
abstract void f();
包含抽象方法的类叫做抽象类。如果一个类包含一个或多个抽象方法,该类必须被限定为抽象的。
如果从一个抽象类继承,并想创建该新类的对象,那么就必须为基类中的所有抽象方法提供方法定义。如果不这样做(可以选择不做),那么导出类便也是抽象类,且编译器将会强制我们用abstract关键字来限定这个类。
9.2 接口
interface这个关键字产生了一个完全抽象的类,它根本就没有提供任何具体实现。它允许创建者确定方法名,参数列表和返回类型,但是没有任何方法主体。接口只提供了形式,而未提供任何具体实现。
要想创建一个接口,可以用interface关键字。可以在interface关键字前面添加public关键字。如果不添加public关键字,则它只有包访问权限。接口也可以包含域,但是这些域隐式地是static和final的。
可以选择在接口中显式地将方法声明为public的,但即使你不这么做,它们也是public的。因此,当要实现一个接口时,在接口中被定义的方法必须被定义为是public的。
9.3 完全解耦
//: interfaces/classprocessor/Apply.java
package interfaces.classprocessor;
import java.util.*;
import static net.mindview.util.Print.*;
class Processor {
public String name() {
return getClass().getSimpleName();
}
Object process(Object input) { return input; }
}
class Upcase extends Processor {
String process(Object input) { // Covariant return
return ((String)input).toUpperCase();
}
}
class Downcase extends Processor {
String process(Object input) {
return ((String)input).toLowerCase();
}
}
class Splitter extends Processor {
String process(Object input) {
// The split() argument divides a String into pieces:
return Arrays.toString(((String)input).split(" "));
}
}
public class Apply {
public static void process(Processor p, Object s) {
print("Using Processor " + p.name());
print(p.process(s));
}
public static String s =
"Disagreement with beliefs is by definition incorrect";
public static void main(String[] args) {
process(new Upcase(), s);
process(new Downcase(), s);
process(new Splitter(), s);
}
} /* Output:
Using Processor Upcase
DISAGREEMENT WITH BELIEFS IS BY DEFINITION INCORRECT
Using Processor Downcase
disagreement with beliefs is by definition incorrect
Using Processor Splitter
[Disagreement, with, beliefs, is, by, definition, incorrect]
*///:~
Apply.process()方法可以接受任何类型的Processor,并将其应用到一个Object对象上,然后打印结果。像本例这样,创建一个能够根据所传递的参数对象的不同而具有不同行为的方法,被称为策略模式。这类方法包含所要执行的算法中固定不变的部分,而“策略”包含变化的部分。策略就是传递进去的参数对象,它包含要执行的代码。这里,Processor对象就是一个策略。在main()中可以看到三种不同类型的策略应用到了String类型的s对象上。
如果Processor是一个接口。下面是Processor和Apply的修改版本:
//: interfaces/interfaceprocessor/Processor.java
package interfaces.interfaceprocessor;
public interface Processor {
String name();
Object process(Object input);
} ///:~
//: interfaces/interfaceprocessor/Apply.java
package interfaces.interfaceprocessor;
import static net.mindview.util.Print.*;
public class Apply {
public static void process(Processor p, Object s) {
print("Using Processor " + p.name());
print(p.process(s));
}
} ///:~
可以使用适配器模式。适配器中的代码将接受你所拥有的接口,并产生你所需要的接口,就像下面这样:
//: interfaces/interfaceprocessor/FilterProcessor.java
package interfaces.interfaceprocessor;
import interfaces.filters.*;
class FilterAdapter implements Processor {
Filter filter;
public FilterAdapter(Filter filter) {
this.filter = filter;
}
public String name() { return filter.name(); }
public Waveform process(Object input) {
return filter.process((Waveform)input);
}
}
public class FilterProcessor {
public static void main(String[] args) {
Waveform w = new Waveform();
Apply.process(new FilterAdapter(new LowPass(1.0)), w);
Apply.process(new FilterAdapter(new HighPass(2.0)), w);
Apply.process(
new FilterAdapter(new BandPass(3.0, 4.0)), w);
}
} /* Output:
Using Processor LowPass
Waveform 0
Using Processor HighPass
Waveform 0
Using Processor BandPass
Waveform 0
*///:~
9.4 Java中的多重继承
在C++中,组合多个类的接口的行为被称作多重继承。
在导出类中,不强制要求必须有一个是抽象的或“具体的”(没有任何抽象方法的)基类。如果要从一个非接口的类继承,那么只能从一个类去继承。其余的基元素都必须是接口。需要将所有的接口都置于Implements关键字之后,用逗号将它们一一隔开。可以继承任意多个接口,并可以向上转型为每个接口,因为每一个接口都是一个独立类型。
9.5 通过继承来扩展接口
一般情况下,只可以将extends用于单一类,但是可以引用多个基类接口。
9.6 适配接口
(看完第13章字符串,再回来看)
9.7 接口中的域
接口中的域隐式地是static和final的。
9.8 嵌套接口
9.9 接口与工厂
接口是实现多重继承的途径,而生成遵循某个接口的对象的典型方法就是工厂方法设计模式。这与直接调用构造器不同,我们在工厂对象上调用的是创建方法,而该工厂对象将生成接口的某个实现的对象。理论上,通过这种方式,我们的代码将完全与接口的实现分离,这就使得我们可以透明地将某个实现替换为另一个实现。