Thinking in java 第9章 接口
9.1 抽象类和抽象方法
1. 抽象类是普通类与接口之间的一种中庸之道。
2. 有抽象方法的类一定要是抽象类,抽象类可以有有非抽象方法。抽象方法只需声明方法体。
3. 抽象类和抽象方法的意义:
- 可以使类的抽象性明确起来,并告诉用户和编译器打算怎样来使用它们。
- 重要的重构工具,使我们可以很容易地将公共方法沿着继承层次结构向上移动。
9.2 接口
1. 接口允许人们通过创建一个能够被向上转型为多种基类的类型,来实现类似多重继承的特性。
2. 接口隐式是static和final的。
9.3 完全解耦
1. 如果某个继承结构以外的类想要用里面的方法,就会很麻烦,而接口可以在很大程度上放宽这种限制,使得可复用性更好。
9.4 Java中的多继承
1,.可以继承任意多个接口,并可以向上转型为每个接口,因为每一个接口都是一个独立的类型。
2. 使用接口的核心原因:为了能够向上转型为多个基类型(以及由此而带来的灵活性)。另一方面可以防止客户端程序员创建该类的对象。
3. 如果要创建不带任何方法定义和成员变量的基类,那么久应该选择接口而不是抽象类。
9.5 通过继承来扩展接口
1. 使用extends来扩展接口。
2. 基类和接口的方法名相同时,当只有返回类型不同时会报错。
9.6 适配接口
1. Scanner类的构造器接受的就是一个Readable接口,若要让Scanner可以作用于它,就让它成为Readable,并重写public int read(CharBuffer cb)方法。在read内部,将输入内容添加到CharBuffer参数中,或者在没有任何输入时返回-1。(例P182-183)。
9.7 接口中的域
1. 放入接口中的任何域都是static和final的(方法是public的),在Java SE5之前会由此来创建常量组工具,之后被enum替代。
9.8 嵌套接口
1. 接口可以嵌套在类或其他接口之中(应用格式为例:implements A.B)
2. 类中可以嵌套private类的接口,可以强制该接口中的方法定义不要添加任何类型信息(不允许向上转型),且其不能在定义它的类之外被实现。
3. 在接口中嵌套时,所有的接口元素必须是public的,不能声明为private。
9.9 接口与工厂
1. 理论上通过工厂方法设计模式,代码将完全与接口的实现分离,这就使得我们可以透明地将某个实现替换为另一个实现。(例P187)。工厂类用工厂接口返回一个实现接口,实现类用实现接口实现具体操作。
练习:
练习1:修改第8章练习9中的Rodent,使其成为一个抽象类。只要有可能们就将Rodent的方法声明为抽象方法。
略。(因为没去写过Rodent... 总之统一加个abstract即可)
练习2:创建一个不包含任何抽象方法的抽象类,并验证我们不能为该类创建任何实例。
略。
练习3:创建一个基类,让它包含抽象方法print(),并在导出类中覆盖该方法。覆盖后的方法版本可以打印导出类中定义的某个整型变量的值。在定义该变量之处,赋予它非零值。在基类的构造器中调用这个方法。现在,在main()方法中,创建一个导出类对象,然后调用它的print()方法。请解释发生的情形。
package Chapter9;
public class E3 {
public static void main(String[] args) {
E3B e = new E3B();
e.print();
}
}
abstract class E3A{
abstract void print();
E3A() {
System.out.print("E3A ");
print();
}
}
class E3B extends E3A {
private int a = 1;
@Override
void print() {
System.out.println(a);
}
E3B() {
System.out.print("E3B ");
print();
}
}
/*
E3A 0
E3B 1
1
*/
抽象类中可以调用抽象方法,因为调用时一定是有导出类实现,故直接用导出类重写的方法。且最开始未初始化完全时为默认值。
练习4:创建一个不包含任何方法的抽象类,从它那里导出一个类,并添加一个方法。创建一个静态方法,它可以接受指向基类的引用,将其向下转型到导出类,然后再调用该静态方法。在main()中,展现它的运行情况。然后,为基类中的方法加上abstract声明,这样就不再需要进行向下转型。
package Chapter9;
public class E4 {
public static void main(String[] args) {
func(new E4B());
}
public static void func(E4A e) {
((E4B)e).write();
}
}
abstract class E4A {
}
class E4B extends E4A {
void write() {
System.out.println("this is E4A");
}
}
/*
this is E4A
*/
练习5:在某个包内创建一个接口,内含三个方法,然后在另一个包中实现此接口。
略。
练习6:证明接口内所有方法都自动是public的。
略。
练习7:修改第8章中的练习9,使Rodent成为一个接口。
略。同练习1。
练习8:在polymorphism.Sandwich.java中,创建接口FastFood并添加加合适的方法,然后修改Sandwich以实现FastFood接口。
略。相当于接口实现练习。
练习9:重构Music5.java,将在Wind,Precussion和Stringed中的公共方法移入一个抽象类中。
略。 同上。
练习10:修改Music5.java,添加Playable接口。将play()的声明从Instrument中移动Playable中。通过将Playable包括在implements列表中,把Playable添加到导出类中。修改tune()使它接受Playable而不是Instrument作为参数。
略。同上。
练习11:创建一个类,它有一个方法用于接受一个String类型的参数,生成的结果是将该参数中每一对字符进行互换。对该类进行适配,使得它可以用于interfaceprocessor.Apply.process()。
package Chapter9;
public class E11 {
public static void main(String[] args) {
Apply.process(new StringProcessor(), new String("abc"));
}
}
interface Processor {
String name();
Object process(Object input);
}
class Apply {
public static void process(Processor p, Object s) {
System.out.println("Using Processor" + p.name());
System.out.println(p.process(s));
}
}
class StringProcessor implements Processor {
@Override
public String name() {
return getClass().getSimpleName();
}
@Override
public String process(Object input) {
return new StringBuffer((String)input).reverse().toString();
}
}
/*
cba
*/
练习12:在Adventure.java中,按照其他接口的样式,增加一个CanClimb接口。
略。照猫画虎。
练习13:创建一个接口,并从该接口继承两个接口,然后从后面两个接口多重继承第三个接口。
package Chapter9;
public class E13 {
public static void main(String[] args) {
E13D e = new E13D();
e.func();
}
}
interface E13A {
void func();
}
interface E13B1 extends E13A {
void func();
}
interface E13B2 extends E13A {
void func();
}
interface E13C extends E13B1, E13B2 {
void func();
}
class E13D implements E13C {
@Override
public void func() {
System.out.println("E13D.func()");
}
}
/*
E13D.func()
*/
练习14:创建三个接口,每个接口都包含两个方法。继承出一个接口,它组合了这三个接口并添加了一个新方法。创建一个实现了该新接口并继承了某个具体类的类。现在编写四个方法,每一个方法接受这四个接口之一作为参数。在main()方法中,创建这个类的对象,并将其传递给这四个方法。
package Chapter9;
public class E14 {
public static void main(String[] args) {
func1(new E14F());
func2(new E14F());
func3(new E14F());
func4(new E14F());
func5(new E14F());
}
public static void func1(E14A e) { e.funcA(); }
public static void func2(E14B e) { e.funcB(); }
public static void func3(E14C e) { e.funcC(); }
public static void func4(E14D e) { e.funcD(); }
public static void func5(E14E e) { e.funcE(); }
}
interface E14A {void funcA();}
interface E14B {void funcB();}
interface E14C {void funcC();}
interface E14D extends E14A, E14B, E14C {void funcD();}
class E14E {
void funcE() {System.out.println("E14E.funcE()");}
}
class E14F extends E14E implements E14D {
@Override
public void funcA() {
System.out.println("E14F.funcA()");
}
@Override
public void funcB() {
System.out.println("E14F.funcB()");
}
@Override
public void funcC() {
System.out.println("E14F.funcC()");
}
@Override
public void funcD() {
System.out.println("E14F.funcD()");
}
}
/*
E14F.funcA()
E14F.funcB()
E14F.funcC()
E14F.funcD()
E14E.funcE()
*/
练习15:将前一个练习修改为:创建一个抽象类,并将其继承到一个导出类中。
略。
练习16:创建一个类,它将生成一个char序列,适配这个类,使其可以成为Scanner对象的一种输入。
略。即实现Readable接口中的read()方法,和P182相同。
练习17:证明在接口中的域隐式地是static和final的。
略。
练习18:创建一个Cycle接口及其Unicycle、Bicycle和Tricycle实现。对每种类型的Cycle都创建相应的工厂,然后编写代码使用这些工厂。
略。工厂参考练习19。
练习19:使用工厂方法来创建一个框架,它可以执行抛硬币和掷骰子功能
package Chapter9;
import java.util.Random;
public class E19 {
public static void main(String[] args) {
playGame(new CoinFactory(), 3);
playGame(new DiceFactory(), 6);
}
public static void playGame(GameFactory factory, int num) {
Game game = factory.getGame(num);
game.play();
}
}
interface Game { void play(); }
interface GameFactory { Game getGame(int num); }
class CoinFactory implements GameFactory {
@Override
public Game getGame(int num) {
return new Coin(num);
}
}
class DiceFactory implements GameFactory {
@Override
public Game getGame(int num) {
return new Dice(num);
}
}
class Coin implements Game {
private int num;
Coin(int num) {
this.num = num;
}
@Override
public void play() {
Random r = new Random();
int cnt = 1;
while(this.num > 0) {
System.out.println("第"+ cnt++ +"次投硬币结果为"+(r.nextInt(2)==1?"正":"反"));
this.num--;
}
}
}
class Dice implements Game {
private int num;
Dice(int num) {
this.num = num;
}
@Override
public void play() {
Random r = new Random();
int cnt = 1;
while(this.num > 0) {
System.out.println("第"+ cnt++ +"次投色子结果为"+(r.nextInt(6)+1));
this.num--;
}
}
}
/*
第1次投硬币结果为反
第2次投硬币结果为反
第3次投硬币结果为反
第1次投色子结果为2
第2次投色子结果为2
第3次投色子结果为2
第4次投色子结果为4
第5次投色子结果为6
第6次投色子结果为4
*/