接口和内部类为我们提供了一种将接口与实现分离的更加结构化的方法。
9.1 抽象类和抽象方法
java提供一种叫做抽象方法的机制,这种方法仅有声明而没有方法体。其声明所采用的语法:
abstract void f();
包含抽象方法的类叫做抽象类。如果一个类包含一个或者多个抽象方法,则该类被限定为抽象的(否则编译器会报错)。
我们也可能会创建没有抽象方法的抽象类。
抽象类和抽象方法非常有用,因为它们可以使类的抽象性明确起来,并告诉用户和编译器打算怎样来使用它们。抽象类还是很有用的重构工具,因为它们使得我们可以很容易地将公共方法沿着集成层次向上移动。
9.2 接口
接口被用来建立类与类之间的协议。
接口可以包含域,但是这些域隐式地是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]
*///:~
适配器设计模式:适配器中的代码将接受你所拥有的接口,并产生你所需要的接口。
//: 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中的多重继承
在导出类中,不强制要求必须有一个是抽象的或“具体的”(没有任何抽象方法的)基类。如果要从一个非接口的类继承,那么只能从一个类去继承。其余的基元素都必须是接口。需要将所有的接口名都置于implements关键字之后,用逗号将它们隔开。可以继承任意多个接口,并可以向上转型为每个接口,因为每个接口都是一个独立的类型。
一定要记住,前面的例子所展示的是使用接口的核心原因:为了能够向上转型为多个基类型(以及由此带来的灵活性)。然而,使用接口的第二个原因却是与使用抽象基类相同:防止客户端程序员创建该类的对象,并确保这仅仅是一个接口。
9.5 通过继承来扩展接口
一般情况下,可以将extends用于单一类,但是可以引用多个基类接口。
如果一个类同时继承了一个类和一个接口,而这个基类和接口都有一个相同的函数,那么在导出类中,只有一个相同的方法。
如果函数的签名不同,则两个方法可以同时存在。但是重载方法仅通过返回值类型来区分是分不开的。
9.6 适配接口
让方法接受接口类型,是一种让任何类都可以对该方法进行适配的方法。
9.7 接口中的域
在接口中定义的域不能是“空final”,但是可以被非常量表达式初始化。当然,这些域不是接口的一部分,它们的值被存储在该接口的静态存储区域内。
9.8 嵌套接口
接口可以嵌套在类或其他接口中。
实现一个private接口只是一种方式,它可以强制该接口中的方法定义不要添加任何类型信息(也就是不允许向上转型)。
若一个方法返回的是一个private接口,那么只有一种方式可以成功,那就是将返回值交给有权使用它的对象。
嵌套在另一个接口中的接口自动就是public的,而不能申明为private的。
当实现某个接口时,并不需要实现嵌套在其内部的任何接口。
//: interfaces/nesting/NestingInterfaces.java
package interfaces.nesting;
class A {
interface B {
void f();
}
public class BImp implements B {
public void f() {}
}
private class BImp2 implements B {
public void f() {}
}
public interface C {
void f();
}
class CImp implements C {
public void f() {}
}
private class CImp2 implements C {
public void f() {}
}
private interface D {
void f();
}
private class DImp implements D {
public void f() {}
}
public class DImp2 implements D {
public void f() {}
}
public D getD() { return new DImp2(); }
private D dRef;
public void receiveD(D d) {
dRef = d;
dRef.f();
}
}
interface E {
interface G {
void f();
}
// Redundant "public":
public interface H {
void f();
}
void g();
// Cannot be private within an interface:
//! private interface I {}
}
public class NestingInterfaces {
public class BImp implements A.B {
public void f() {}
}
class CImp implements A.C {
public void f() {}
}
// Cannot implement a private interface except
// within that interface's defining class:
//! class DImp implements A.D {
//! public void f() {}
//! }
class EImp implements E {
public void g() {}
}
class EGImp implements E.G {
public void f() {}
}
class EImp2 implements E {
public void g() {}
class EG implements E.G {
public void f() {}
}
}
public static void main(String[] args) {
A a = new A();
// Can't access A.D:
//! A.D ad = a.getD();
// Doesn't return anything but A.D:
//! A.DImp2 di2 = a.getD();
// Cannot access a member of the interface:
//! a.getD().f();
// Only another A can do anything with getD():
A a2 = new A();
a2.receiveD(a.getD());
}
} ///:~
9.9 接口与工厂
接口是实现多重继承的途径,而生成遵循某个接口的对象的经典方式就是工厂方法设计模式。这与直接使用构造器不同,我们在工厂对象上调用的是创建方法,而该工厂对象将生成接口的某个具体实现对象。理论上,通过这种方式,我们的代码将完全与接口的实现分离,这就使得我们可以透明地将某个实现替换为另一个实现。
//: interfaces/Factories.java
import static net.mindview.util.Print.*;
interface Service {
void method1();
void method2();
}
interface ServiceFactory {
Service getService();
}
class Implementation1 implements Service {
Implementation1() {} // Package access
public void method1() {print("Implementation1 method1");}
public void method2() {print("Implementation1 method2");}
}
class Implementation1Factory implements ServiceFactory {
public Service getService() {
return new Implementation1();
}
}
class Implementation2 implements Service {
Implementation2() {} // Package access
public void method1() {print("Implementation2 method1");}
public void method2() {print("Implementation2 method2");}
}
class Implementation2Factory implements ServiceFactory {
public Service getService() {
return new Implementation2();
}
}
public class Factories {
public static void serviceConsumer(ServiceFactory fact) {
Service s = fact.getService();
s.method1();
s.method2();
}
public static void main(String[] args) {
serviceConsumer(new Implementation1Factory());
// Implementations are completely interchangeable:
serviceConsumer(new Implementation2Factory());
}
} /* Output:
Implementation1 method1
Implementation1 method2
Implementation2 method1
Implementation2 method2
*///:~