9 接口
接口和内部类为我们提供了一种将接口和实现分离的更加结构化的方法。在学习接口之前,必须学习抽象类,它是普通的类和接口之间的中庸之道。
9.1 抽象类和抽象方法
随着继承层次中一个个新子类的定义,类变得越来越具体,而父类则更一般,更通用。类的设计应该保证父类和子类能够共享特征。有时将一个父类设计得非常抽象,以至于它没有具体的实例,这样的类叫做抽象类,或者抽象基类。
- 用abstract关键字来修饰一个类时,这个类叫做抽象类;
- 用abstract来修饰一个方法时,该方法叫做抽象方法。( 抽象方法:只有方法的声明,没有方法的实现。以分号结束:
abstract int abstractMethod( int a );
) - 含有抽象方法的类必须被声明为抽象类。(否则,编译器会报错。)
- 抽象类不能被实例化。抽象类是用来被继承的,抽象类的子类必须重写父类的抽象方法,并提供方法体。若没有重写全部的抽象方法,仍为抽象类。
- 不能用abstract修饰属性、私有方法、构造器、静态方法、final的方法。即private,static,final。
(即使不能被实例化,但是还是有构造器)抽象方法所在的类一定是抽象类.抽象类中可以没有抽象方法。也可以同时有抽象方法,也可以有实体方法。
例如:
abstract class Main{
private int i;
public abstract void getInform();
public void print(){
System.out.println("Main");
}
}
class Subclass extends Main{
public void getInform(){
System.out.println("Subclass");
}
}
9.2 接口
interface关键字使抽象的概念更加向前迈进。abstract关键字允许人们在类中创建一个或多个没有任何定义的方法——提供了接口部分,但是没有提供任何具体的实现部分,这些实现由此类的继承者们实现。interface这个关键字产生了一个完全抽象的类,他根本就没有提供任何具体实现,它允许创建者确定方法名,参数列表和返回类型,但是没有任何方法体。接口只提供了形式,并且接口提供了多重继承,总的来说就是:
- 有时必须从几个类中派生出一个子类,继承它们所有的属性和方法。但是,Java不支持多重继承。有了接口,就可以得到多重继承的效果。
- 接口(interface)是抽象方法和常量值的定义的集合
- 从本质上讲,接口是一种特殊的抽象类,这种抽象类中只包含常量和方法的定义,而没有变量和方法的实现。
- 一个类可以实现多个接口,接口也可以继承其它接口:
class A impelments InterfaceB,InterfaceC{}
接口的特点:
- 用关键字interface来定义。
- 接口中的所有成员变量都默认是由public static final修饰的。
- 接口中的所有方法都默认是由public abstract修饰的。
- 没有构造器并且采用多继承机制。
例如:
public interface Runner {
int ID = 1;
void start();
public void run();
void stop();
}
等价于
public interface Runner {
public static final int ID = 1;
public abstract void start();
public abstract void run();
public abstract void stop();
}
在接口中public static final 或 public abstract 都是默认,可以缺省默认。
实现接口的类中必须提供接口中所有方法的具体实现内容,方可实例化。否则,仍为抽象类。接口的主要用途就是被实现类实现。(面向接口编程)。与继承关系类似,接口与实现类之间存在多态性
定义Java类的语法格式:先写extends,后写implements
< modifier> class < name> [extends < superclass>]
[implements < interface> [,< interface>]* ] {
< declarations>*
}
即class CC extends DD implements AA{ }
Java中类的继承是单继承的,接口的实现可以是多实现的。接口和接口之间仍为继承关系,接口之间的继承可以多继承,接口与具体实现类之间也存在多态。
一个类可以实现多个无关的接口
interface Runner { public void run();}
interface Swimmer {public double swim();}
class Creator{public int eat(){…}}
class Man extends Creator implements Runner ,Swimmer{
public void run() {……}
public double swim() {……}
public int eat() {……}
}
与继承关系类似,接口与实现类之间存在多态性
public class Test{
public static void main(String args[]){
Test t = new Test();
Man m = new Man();
t.m1(m);
t.m2(m);
t.m3(m);
}
public String m1(Runner f) { f.run(); }
public void m2(Swimmer s) {s.swim();}
public void m3(Creator a) {a.eat();}
}
9.3 完全解耦
只要一个方法操作的是类而非接口,你就只能使用这个类及其子类。如果你想要将这个方法应用于不在此继承结构中的某个类,就无法使用。接口可以在很大程度上放宽了这种限制,可以编写复用性更好的代码。
9.4 通过继承来扩展接口
通过继承,可以很容易的在接口中添加新的方法声明,还可以通过继承在新接口中组合数个接口。
//: interfaces/HorrorShow.java
// Extending an interface with inheritance.
interface Monster {
void menace();
}
interface DangerousMonster extends Monster {
void destroy();
}
interface Lethal {
void kill();
}
class DragonZilla implements DangerousMonster {
public void menace() {}
public void destroy() {}
}
interface Vampire extends DangerousMonster, Lethal {
void drinkBlood();
}
class VeryBadVampire implements Vampire {
public void menace() {}
public void destroy() {}
public void kill() {}
public void drinkBlood() {}
}
public class HorrorShow {
static void u(Monster b) { b.menace(); }
static void v(DangerousMonster d) {
d.menace();
d.destroy();
}
static void w(Lethal l) { l.kill(); }
public static void main(String[] args) {
DangerousMonster barney = new DragonZilla();
u(barney);
v(barney);
Vampire vlad = new VeryBadVampire();
u(vlad);
v(vlad);
w(vlad);
}
} ///:~
9.5.1 组合接口时名字冲突
实现多继承时,可能会碰到一个小陷阱,例如class A implements B,C{}
其中接口B和接口C的接口方法相同,则不会有什么问题,但是它们的名称或返回类型不同,就会出现问题。例如:
public class Main implements Subclass,Subbclass{
public static void main(String[] args) {
Main m=new Main();
m.getInform();
}
void getinfor(){
System.out.println("Main");
}
@Override
public void getInform() {
// TODO Auto-generated method stub
System.out.println("Subclass");
}
@Override
public void getInform(int i) {
// TODO Auto-generated method stub
}
}
interface Subclass{
void getInform();
String getInform(int i);
}
interface Subbclass{
void getInform(int i);
String getInform();
}
这时候困难就来了,覆盖,实现和重载搅和在一起了,重载类型仅通过返回类型是区分不开的。所以在组合的不同接口中使用相同的方法名通常会造成代码可读性的混乱,需要尽量避免。
9.6 适配接口
在接口中允许同一个接口具有不同的具体实现。在简单的情况中,它的体现形式通常是一个接受接口类型的方法,而该接口的实现和向该方法传递的对象则取决于方法的使用者。一种常见的用法就是策略设计模式与适配器模式。关于java的设计模式详细见《Thinking in Patterns》
9.7 嵌套接口
接口可以嵌套在类或者其他接口中。例如:
//: 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());
}
} ///:~
接口是实现多重继承的途径,而生成遵循某个接口的对象的典型方式就是工厂方法设计模式。