第五章 接口
? 抽象类
? 多态
? 接口
Animal
makeNoise()
eat()
sleep()
roam()
Canine
roam()
Wolf
makeNoise()
eat()
Dog
makeNoise()
eat()
Feline
roam()
Lion
makeNoise()
eat()
Cat
makeNoise()
eat()
回顾 Animal的类结构,
1 通过继承最大限度地减少了重复代

2 通过覆盖,以及多态的特征保证每
个子类的独特性。
3 在一使用 Animal的程序里,
(包括 Animal作为形参或数
组声明里),
任何 Animal的子类型(现有或以
后添加的)都可以作为实参被传
递,并在运行时得到类型识别。
抽象类
过去在程序里说,
Dog myDog =new Dog() ;
Animal myDog= new Dog() ;
1
Dog 对象
Dog引用
两者的类型是相同的
1
Dog 对象
Animal引用
两者的类型是不同的
现在在程序里说,
下面这句有些不自然,
Animal anim=new Animal() ; 1
Animal 对象
Animal引用
两者的类型相同,但是一个
实际的 Animal对象可以是什
么呢?
需要一个 Animal类,用于继承和多态
不需要实例化 Animal类,不需要一个 Animal对象
需要一个防止类被实例化的方法,
把该类标记为,abstract”。
abstact class Animal {
public void sleep() { }
}
abstact class Canine extends Animal {
public void roam() { }
}
编译器确保抽象类不会被实例化,
abstact class Canine extends Animal {
public void roam() { }
}
public class MakeCanine {
public void go() {
Canine c;
c=new Dog() ;
c=new Canine() ;
c.roam();
}
}
尽管是抽象类,仍然允许声明该类型的引用。
error,不允许实例化一个抽象的类。
Animal
Feline Canine
Lion Cat Wolf Dog
abstract
abstract abstract
concrete concrete concrete concrete
Animal族中抽
象和具体的类
抽象的类位于继承
树的上部:通过被
扩展 而得到应用的
价值。
抽象的方法
抽象的方法意味着该方法必须被子类覆盖;它是没有体的
public abstract void eat() ; 没有方法体!以
分号结束。
抽象的方法必须位于抽象类里;但是,抽象类可以拥
有非抽象的方法。
你必须实现所有的抽象方法,
在继承树上的第一个具体的
类必须实现所有的抽象方法。
即为抽象方法提供一个体。
Animal
eat()
roam()
roam()
makeNoise()
Canine
Dog
eat()
makeNoise()
abstract
concrete
多态
任务 1:设计一个存放 Dog对象的列表类,
1 使用一个数组来存储对象,数组最大不超过 5。
2 支持“添加”操作。当元素总数超过 5时如果继续
添加,该方法什么也不做。
MyDogList
Dog[] dogs;
int nextIndex;
void add(Dog d);
//MyDogList类
public class MyDogList {
private Dog[] dogs=new Dog[5];
private int nextIndex=0;
public void add(Dog d) {
if(nextIndex < dogs.length) {
dogs[nextIndex]=d;
System.out.println(“Dog added at, + nextIndex);
nextIndex++;
}
}
}
任务 2:设计一个存放 Cat对象的列表类,其他要求不
变。
有几种方案,
1) 再设计一个 MyCatList
2) 把两个类合在一起,设计一个 MyCatDogList,
这样需要两个数组,两套方法。
3)设计一个通用的 AnimalList类,它能够接受所
有的 Animal子类。
public class MyAnimalList {
private Animal[] animals=new Animal[5];
private int nextIndex=0;
public void add(Animal a) {
if(nextIndex < animals.length) {
animals[nextIndex]=d;
System.out.println(“animals added at, + nextIndex);
nextIndex++;
}
}
}
//MYAnimalList
Animal是抽象类,这样
有没有问题?
没有关系,这儿不是
Animal对象,而是 Animal
类型的数组的对象。
MyAnimalList list=new MyAnimalList();
Dog d=new Dog();
Cat c=new Cat();
list.add(d);
list.add(c);
测试
接口
假如要为一家宠物店设计程序,其中的宠物类部分和以前
的 Animal类有重叠。为了避免重复的设计,尽量利用以
前的 Animal类。
Pet
beFriendly();
playWith();
…,
eat();
sleep();
makeNoise();
roam();
在宠物的“合同”里,
应该既有新增的宠物的
行为,也有以前定义的
属于 Animal的行为。
Animal
Feline Canine
Lion Cat Wolf Dog
把所有的 pet行为
放入根类用于继承
方案 1,
把 pet的行为放入 Animal类里
优点,
所有的 Animal子类都会继承
这些行为,
? 不需要改动子类了。
? Animal类可以作为宠物店程序
里的多态类型使用。
缺点,
? 某些不是宠物的动物将表
现出宠物的行为。这会为使用
者带来潜在的危险。
? 对根类的这种修改也将传
播到将来任何新添的子类中。
dangeous
Animal
Feline Canine
Lion Cat Wolf Dog
把所有的 pet行为
放入根类,但不实
现它们,而是标记
为 abtract
方案 2,
和方案 1有所区别,所有的方
法在 Animal里被标记为抽象的。
优点,
? 方案 1的优点被继承下来。
? 非宠物的动物可以在实现抽
象方法时选择什么都不做,从而
避免出现错误的行为。
缺点,
? 所有的子类必须添加并提
供 pet行为的实现。
? 和刚才一样,关于 Animal
的“合同”的修改将影响到所
有后代,即使它们可以选择什
么都不做。
A pet,but do nothing
Animal
Feline Canine
Lion Cat Wolf Dog
只把 pet行为放在
是宠物的子类里。
方案 3,
把 pet的行为放入特定子类里
优点,
没有继承,非 pet的动物不会表
现出错误的行为了。
缺点,
? 没有多态了,无法通过
Animal类型的参数来调用 pet
的方法,只能把形参定义为
Dog或 Cat。
? 关于宠物的“合同”被取
消了,尽管潜在的需求还在。
没有证据说明 Cat和 Dog拥有
共同的属于 pet的行为,即它
们的相应方法可以随意定义。
编译器无法检查它们的对错。
no-pet
总结前面的方案,需求是,
1 把 pet行为添加到确是宠物的类里
2 所有宠物的类满足一个“合同”。
3 同样,要能够利用多态,这样每个宠
物都能保证自己的方法被调用,而不
需要为每个 pet有关的方法设计各自
的参数、返回值或者数组类型。
Animal
Feline Canine
Lion Cat Wolf Dog
Pet
看上去需要两个
superclass Pet类作为抽象父类,放入所有的 pet方法。
非 pet的动物不
受任何影响 Cat和 Dog同时扩
展 Animal和 Pet
多重继承可以吗?
C
A B
Base
int i
play()
C调用哪个 play()?
play() play()
C
A B
play() play()
C怎样使用变
量 i?
Java的解决方案,interface
1 interface里每个方法都是抽象的。
Pet
abstract void
beFriendly();
absract void play()
因此,这里的所有方法都必须在
它的“子类”里实现(即覆盖)。
2 interface的定义和实现,
public interface Pet {…}
DEFINE an interface,
关键字是,interface”,
而不是,class”
IMPLEMENT an interface,
public class Dog extends Canine implements Pet {…}
关键字,implements”
放在接口名,pet”之
前。注意实现接口与
扩展类同时出现。
3 Pet接口的定义和实现
定义时只需要方法头。所有的方法自动是
public的和 abstract的。
public interface Pet {
public abstract void beFriendly();
public abstract void play();
}
这两个关键
字是可选的
public class Dog extends Canine implements Pet {
public void beFriendly() { …};
public void play() {…}
public void roam() {…} // 这些是正常的方法

}
在 {}里实现
要实现 pet必须实
现所有的 Pet方法。 但是在实现时必须
出现 public
X=new Comparable(…);//error
//正确的方法
Comparable X;
X=new Employee(…);
永远不要用 new来实
例化一个接口。
4 和抽象类相似,接口不可以被实例化,它只能指
向实现它的类的对象。
所有实现了该接口的类的对象都可
以接受。
5 利用接口进行常数分组
interface Week{
int MONDAY=1,
TUESDAY=2,
WEDNESDAY=3,
THURSDAY=4,
FRIDAY=5,
SATURDAY=6,
SUNDAY=7;
}
所有的变量自动成为 static&
final的。
class CheckWeek implements Week{

public void printDay(int day)
{
if(day==MONDAY)
prt(“today is Monday”);
…,
}
在实现该接口的类里直接
使用
5 (续)
//if not implemented
//Inside main()
….,
int today=Week.MONDAY;

如果没有实现该接口,使
用访问符”,”来使用。
6 一个类可以实现多个接口
public class Dog extends Animal implements Pet,
Serializable,Runnable
使用“,”分隔
对象可以存储状态到
文件 允许多线程
class,subclass,abstract class 以及 interface的选择,
? 不存在任何的实际或假设的继承关系时,
仅仅建立一个 class就可以了。
?需要建立已有类的更特定的类型,增加
或者修改某些行为时,建立一个 subclass
实现扩展。
?仅需要为一组子类建立一个模板,并且
至少能为子类们提供一些公共的代码。把
它标记为 abstract,同时也保证了没人能
建立该类的对象。
?要忽略已有的继承树层次,让某些类也
可以使用或定义某些行为或属性集合,定
义一个 interface。
interface I1{
void showMe();
}
//implements I1
class C1 implements I1{
public void showMe(){
System.out.println("this is C1");
}
}
//extends c1,also implements I1
class C2 extends C1 implements I1{
public void showMe(){
System.out.println("this is C2");
}
}
Polymorphism的体现,把接口变量作为多态参数。
//inside main class

public static void showInterface(I1 i){
i.showMe();
}
//pass object to interface
public static void main(String[] args)
{
C2 x=new C2();
showInterface(x);
}
对象 x作为实参传递给了一个接口类型,
随后 C2的 showMe()方法被调用。
过程类似于 I1是根类的情形。
//inside main class

public static void showInterface(I1 i){
i.showMe();
}
//pass object to interface
public static void main(String[] args)
{
C1 x=new C2();
showInterface(x);
}
I1
C1
C2
showMe(new C2())
接口永远是虚的
I do nothing
showMe
showMe
Object in heap
is C2,Not me
Yes,show me
C2首先 upcast
为 C1,随后被
I1接受。