1. 程式人生 > >Java開發筆記(五十八)簡單接口及其實現

Java開發筆記(五十八)簡單接口及其實現

multi 舉例 動物 三種 behavior 不出 ant wim run方法

前面介紹了抽象方法及抽象類的用法,看似解決了不確定行為的方法定義,既然叫喚動作允許聲明為抽象方法,那麽飛翔、遊泳也能聲明為抽象方法,並且雞類涵蓋的物種不夠多,最好把這些行為動作擴展到鳥類這個群體,於是整個鳥類的成員方法都可以如法炮制了。可是這種做法也帶來了一些弊端,包括但不限於:
1、能飛的動物不僅僅是鳥類,還有昆蟲、蝙蝠等其它動物也能飛,難不成昆蟲類、哺乳動物類也要自行聲明飛翔方法?這麽做顯然產生了重復的方法定義。不然的話,要是把飛翔方法挪到更底層的動物類,一大群動物為了不淪為抽象類都得重寫飛翔方法,比如鱷魚、大象等根本不會飛的動物也要裝模作樣撲騰幾下,實在是滑天下之大稽。
2、除了幾種常見的鳥類為大眾所熟知之外,大部分鳥類其實人們一時半刻叫不出它們的名字,倘若在路上偶遇一只鳥兒,難道因為不認識它就沒法描述它的模樣了嗎?(如果鳥類是個抽象類,外部是不能創建鳥類實例的)
3、就算給整個動物類都添加了叫喚、飛翔、遊泳這些抽象方法,並且費盡九牛二虎之力把所有派生而來的子類都實現了這三個抽象方法,也不意味著萬事大吉。譬如青蛙擅長跳躍這個動作,哪天程序員突發奇想要給抽象的動物類補充跳躍方法,從而支持青蛙的跳躍行為,隨之而來的代價便是讓動物類的所有子類都重寫跳躍方法,這樣也太傷筋動骨了。
綜上所述,抽象類解決不了層出不窮的問題,遠非什麽靈丹妙藥,只能用於處理符合條件的特定要求。若想真正有效應對這些刁鉆古怪的挑戰,還得指望新的抽象技術,在Java編程中這就是接口。接口不從屬於類,而是與類平級,類通過關鍵字class標識,而接口通過關鍵字interface來標識。由於接口是作為類的輔助角色出現,因此它在結構上與類比較相似,不過也有不少不同之處,舉例如下:
1、凡是類都有構造方法,即便是抽象類也支持定義構造方法,但接口不允許定義構造方法,因為接口只用於聲明某些行為動作,本身並非一個實體。
2、在Java8以前,接口內部的所有方法都必須是抽象方法,具體的方法內部代碼有賴於該接口的實現類來補充。因為有這個強制規定,所以接口內部方法的abstract前綴可加可不加,即使不加abstract,編譯器也會默認把該方法當作抽象方法。
3、至於接口內部的屬性,則默認為終態屬性,即添加了final前綴的成員屬性。當然這個final前綴也是可加可不加的,即使不加final,編譯器仍會默認把該屬性當作終態屬性。
按照上述的接口規定,再來編寫一個定義了動物行為的接口代碼,其中主要包括飛翔方法、遊泳方法、奔跑方法等,詳細的接口定義代碼示例如下:

//定義一個接口。接口主要聲明一些特定的行為方法
public interface Behavior {

	// 聲明了一個抽象的飛翔方法。註意,接口內部的方法默認都是抽象方法,所以可以不用添加abstract前綴
	public void fly();
	//abstract public void fly(); // 這裏的abstract可加可不加

	// 聲明了一個抽象的遊泳方法
	public void swim();

	// 聲明了一個抽象的奔跑方法
	public void run();

	// 接口內部的屬性默認都是終態屬性,所以可以不用添加final前綴
	public String TAG = "動物世界";
	//public final String TAG = "動物世界"; // 這裏的final可加可不加
	
	// 接口不允許定義構造方法。在Java8以前,接口內部的所有方法都必須是抽象方法
}

接著定義一個鵝類,它不但繼承自Bird鳥類,而且實現了新的行為接口Behavior。註意子類繼承父類的格式為“extends 父類名”,實現某個接口的格式則為“implements 接口名”,同時該類還要重寫接口裏的所有抽象方法。於是實現了行為接口的鵝類代碼如下所示:

//定義一個實現了接口Behavior的鵝類。註意鵝類需要實現Behavior接口的所有抽象方法
public class Goose extends Bird implements Behavior {

	public Goose(String name, int sexType) {
		super(name, sexType);
	}

	// 實現了接口的fly方法
	@Override
	public void fly() {
		System.out.println("鵝飛不高,也飛不遠。");
	}

	// 實現了接口的swim方法
	@Override
	public void swim() {
		System.out.println("鵝,鵝,鵝,曲項向天歌。白毛浮綠水,紅掌撥清波。");
	}

	// 實現了接口的run方法
	@Override
	public void run() {
		System.out.println("檻外蕭聲輕蕩漾,沙間鵝步滿蹣跚。");
	}
}

對於外部來說,這個鵝類跟一般的類沒啥區別,鵝類所實現的接口方法,在外部看來都是鵝類的成員方法,原來怎麽調用現在依然怎麽調用。下面是外部使用鵝類的代碼例子:

	// 演示簡單接口的實現類用法
	private static void testSimple() {
		Goose goose = new Goose("家鵝", 0);
		goose.fly(); // 實現了接口的fly方法
		goose.swim(); // 實現了接口的swim方法
		goose.run(); // 實現了接口的run方法
	}

接口與類相比還有一個重大區別,在Java體系之中,每個類最多只能繼承一個父類,不能同時繼承多個類,也就是不允許多重繼承。而接口不存在這方面的限制,某個類可以只實現一個接口,也可以同時實現兩個接口、三個接口等等,待實現的接口名稱之間以逗號分隔。例如除了飛翔、遊泳、奔跑這三種動作之外,有些動物還擅長跳躍,比如青蛙、袋鼠等等,倘若在現有的Behavior接口中增加跳躍方法jump,那麽包括Goose在內所有實現了Behavior的類都要重寫jump方法,顯然改造量巨大。現在借助接口的多重實現特性,完全可以另外定義新的行為接口Behavior2,在新接口中聲明跳躍方法,那麽只有實現Behavior2接口的類才需要重寫jump方法。按此思路單獨定義的新接口Behavior2代碼見下:

//定義另一個行為接口
public interface Behavior2 {

	// 聲明了一個抽象的跳躍方法
	public void jump();
}

然後編寫Frog蛙類的定義代碼,這個蛙類同時實現了接口Behavior和Behavior2,這樣它要重寫Behavior的三個方法,以及Behavior2的跳躍方法。下面是一個蛙類代碼的簡單例子:

//定義一個實現了接口Behavior和Behavior2的蛙類。類只能繼承一個,但接口可以實現多個
public class Frog implements Behavior, Behavior2 {

	// 實現了Behavior2接口的jump方法
	@Override
	public void jump() {
		System.out.println("青蛙跳躍的技能叫做“蛙跳”");
	}

	// 實現了Behavior接口的fly方法
	@Override
	public void fly() {
	}

	// 實現了Behavior接口的swim方法
	@Override
	public void swim() {
		System.out.println("青蛙遊泳的技能叫做“蛙泳”");
	}

	// 實現了Behavior接口的run方法
	@Override
	public void run() {
	}
}

由於新增的jump方法屬於新接口Behavior2,不屬於原接口Behavior,因此實現了Behavior接口的鵝類代碼無需任何修改,只有實現Behavior2的蛙類代碼才需額外處理。當然這個特殊處理也僅限於蛙類的定義,對於外部而言,蛙類Frog仍是一個普通的類,外部調用它並沒有什麽兩樣,具體的調用代碼示例如下:

	// 演示某個類同時實現了多個接口
	private static void testMultiple() {
		Frog frog = new Frog();
		frog.swim(); // 實現了Behavior接口的swim方法
		frog.jump(); // 實現了Behavior2接口的run方法
	}

  

更多Java技術文章參見《Java開發筆記(序)章節目錄》

Java開發筆記(五十八)簡單接口及其實現