在面向?qū)ο缶幊痰乃枷胫校涌谑且粋€(gè)非常重要的概念。按書上介紹的,使用接口,可以實(shí)現(xiàn)運(yùn)行時(shí)多態(tài)、易維護(hù)、易拓展等等優(yōu)點(diǎn)。擁有多年編程經(jīng)驗(yàn)的人應(yīng)該能理解這些話的含義,對(duì)于一個(gè)初學(xué)編程的萌新來(lái)說(shuō),看完這段話完全不知所云。那今天我用《英雄聯(lián)盟》為背景,詳細(xì)的分析一下接口在面向?qū)ο缶幊讨械淖饔茫约笆褂媒涌诘膬?yōu)勢(shì)。
這次使用java作為編寫demo的語(yǔ)言,主要原因有兩個(gè):
- java是最流行的編程語(yǔ)言,基本上學(xué)過(guò)編程的都會(huì)java語(yǔ)言;
- java是一門對(duì)面向?qū)ο筇匦灾С直容^好的語(yǔ)言;
還記得我剛開始學(xué)習(xí)java的時(shí)候,就很不理解接口的作用,感覺(jué)接口有點(diǎn)多余。
例如我定義了一個(gè)接口,但是我在實(shí)現(xiàn)這個(gè)接口的類中還要寫接口的實(shí)現(xiàn)方法,那我不如直接就在這個(gè)類中寫實(shí)現(xiàn)方法豈不是更便捷,還省去了定義接口
相信不止我一個(gè)人有過(guò)這樣的疑惑吧。
后來(lái)隨著寫代碼,看閱讀別人的代碼,逐漸開始理解接口的作用了,慢慢覺(jué)得接口是一個(gè)非常方便和牛逼的東西。
教材上,網(wǎng)上解釋接口的例子大多數(shù)使用定義一個(gè)Animal接口,然后Dog實(shí)現(xiàn)了這個(gè)接口,Cat實(shí)現(xiàn)了這個(gè)接口;還有一種用USB接口舉例。大多數(shù)人看完還是一臉懵逼。
現(xiàn)在用一種新的方式——《英雄聯(lián)盟》為背景介紹一下。
說(shuō)了這么半天,開始進(jìn)入正題吧。
先圈兩個(gè)重點(diǎn):
- Java之所以要有接口,是因?yàn)閖ava不支持多繼承,使用接口,可以間接的實(shí)現(xiàn)多繼承的一些特性;像C++就不存在接口這個(gè)東西,因?yàn)镃++支持多繼承
- 在面向?qū)ο蟮母拍钪校宇悾ㄅ缮悾┛梢宰詣?dòng)轉(zhuǎn)換為父類(基類)類型;也就是說(shuō),A類實(shí)現(xiàn)了接口B,那么A的實(shí)例化對(duì)象可以自動(dòng)轉(zhuǎn)換為B類型
public class Main {
public static void main(String[] args) {
B a = new A();
}
}
interface B {
}
class A implements B {
}
這樣的代碼是正確的。
開始demo部分,我們定義一個(gè)Skill接口,里面有 Q、W、E、R 四個(gè)方法,代表英雄的四個(gè)技能。為了簡(jiǎn)單,被動(dòng)技能和召喚師技能就不寫了。
然后從五個(gè)位置上單、打野、中單、ADC、輔助中各挑選一個(gè)英雄,作為例子。上路中我最喜歡的是銳雯,打野我玩的最多,糾結(jié)了半天選了盲僧。中單里必須選亞索,ADC里選擇了暴走蘿莉,輔助里選擇了錘石。
先上代碼再解釋:
//技能接口
interface Skill {
void Q();
void W();
void E();
void R();
}
//放逐之刃-銳雯
class RuiWen implements Skill {
public RuiWen() {
System.out.println("斷劍重鑄之日,騎士歸來(lái)之時(shí)");
}
@Override
public void Q() {
System.out.println("折翼之舞");
}
@Override
public void W() {
System.out.println("震魂怒吼");
}
@Override
public void E() {
System.out.println("勇往直前");
}
@Override
public void R() {
System.out.println("放逐之鋒");
}
}
//盲僧-李青
class LiQing implements Skill {
public LiQing() {
System.out.println("我用雙手成就你的夢(mèng)想");
}
@Override
public void Q() {
System.out.println("天音波/回音擊");
}
@Override
public void W() {
System.out.println("金鐘罩/鐵布衫");
}
@Override
public void E() {
System.out.println("天雷破/摧筋斷骨");
}
@Override
public void R() {
System.out.println("猛龍擺尾");
}
}
//疾風(fēng)劍豪-亞索
class YaSuo implements Skill {
public YaSuo() {
System.out.println("死亡如風(fēng),常伴吾生");
}
@Override
public void Q() {
System.out.println("斬鋼閃");
}
@Override
public void W() {
System.out.println("風(fēng)之障壁");
}
@Override
public void E() {
System.out.println("踏前斬");
}
@Override
public void R() {
System.out.println("狂風(fēng)絕息斬");
}
}
//暴走蘿莉-金克斯
class JinKeSi implements Skill {
public JinKeSi() {
System.out.println("規(guī)則就是用來(lái)打破的");
}
@Override
public void Q() {
System.out.println("槍炮交響曲!");
}
@Override
public void W() {
System.out.println("震蕩電磁波!");
}
@Override
public void E() {
System.out.println("嚼火者手雷!");
}
@Override
public void R() {
System.out.println("超究極死神飛彈!");
}
}
//魂鎖典獄長(zhǎng)-錘石
class ChuiShi implements Skill {
public ChiShi() {
System.out.println("我們要怎樣進(jìn)行這令人愉悅的折磨呢");
}
@Override
public void Q() {
System.out.println("死亡判決");
}
@Override
public void W() {
System.out.println("魂引之燈");
}
@Override
public void E() {
System.out.println("厄運(yùn)鐘擺");
}
@Override
public void R() {
System.out.println("幽冥監(jiān)牢");
}
}
代碼有點(diǎn)多,但是很簡(jiǎn)單,寫了5類,對(duì)應(yīng)5個(gè)英雄。每個(gè)類的構(gòu)造方法中,打印了這個(gè)英雄在排位中被選中時(shí)的臺(tái)詞。每個(gè)類都實(shí)現(xiàn)了skill這個(gè)接口,并重寫了QWER這4個(gè)方法,在方法中打印了這個(gè)英雄技能的名稱。
在main方法中初始化這5個(gè)英雄,并調(diào)用每個(gè)英雄的QWER這四個(gè)技能,代碼:
public class Main {
public static void main(String[] args) {
//初始化銳雯釋,放技能
Skill ruiWen = new RuiWen();
ruiWen.Q();
ruiWen.W();
ruiWen.E();
ruiWen.R();
//初始化李青,釋放技能
Skill liQing = new LiQing();
liQing.Q();
liQing.W();
liQing.E();
liQing.R();
//初始化亞索,釋放技能
Skill yaSuo = new YaSuo();
yaSuo.Q();
yaSuo.W();
yaSuo.E();
yaSuo.R();
//初始化金克斯,釋放技能
Skill jinKeSi = new JinKeSi();
jinKeSi.Q();
jinKeSi.W();
jinKeSi.E();
jinKeSi.R();
//初始化錘石,釋放技能
Skill chuiShi = new ChuiShi();
chuiShi.Q();
chuiShi.W();
chuiShi.E();
chuiShi.R();
}
}
注意一點(diǎn):
我們?cè)趯?shí)例化這5個(gè)英雄時(shí),這5個(gè)英雄都是Skill類型的
看一下運(yùn)行結(jié)果:
可以看到,這5個(gè)英雄依次被實(shí)例化,并釋放了QWER這4個(gè)技能。
可能到這有的同學(xué)沒(méi)看懂,這和接口有什么關(guān)系?接口帶來(lái)了哪些好處?
簡(jiǎn)單分析一下:
- 接口這個(gè)概念,其實(shí)就是定義了一種規(guī)范。在 Skill 這個(gè)接口中,定義了Q、W、E、R這四個(gè)方法,只要是實(shí)現(xiàn)了這個(gè)接口的類,一定會(huì)有這四個(gè)方法。
- 接口可以看做是實(shí)現(xiàn)多繼承的一種方式(這樣說(shuō)可能不嚴(yán)謹(jǐn))。java中沒(méi)有多繼承這種機(jī)制,失去了一些靈活性。但是去掉多繼承后,語(yǔ)法簡(jiǎn)單了很多,像C++中,因?yàn)橛卸嗬^承,又引入了虛繼承的概念。說(shuō)多了,回到正題。一個(gè)類實(shí)現(xiàn)一個(gè)接口后,可以看做是這個(gè)接口的子類,所以,我們?cè)趯?shí)例化英雄時(shí)(
new Ruiwen()
等),可以直接實(shí)例化為 Skill 類型的。
結(jié)合這兩點(diǎn),所以我們每一個(gè)Skill類型的對(duì)象,都可以調(diào)用 Q、W、E、R 這四個(gè)方法。
有人會(huì)提出疑問(wèn),我在每個(gè)類中都定義 Q、W、E、R 這四個(gè)方法不就行了。但是如何保證每個(gè)類里都有這四個(gè)方法呢?通過(guò)接口約束,可以保證,所有實(shí)現(xiàn)這個(gè)接口的類中,一定有這四個(gè)方法。
再通過(guò)下面這個(gè)用法,看一下接口怎樣實(shí)現(xiàn)多態(tài)的:
import java.util.Scanner;
public class Main {
public static void main(String[] args) {
Skill hero;
Scanner scanner = new Scanner(System.in);
switch (scanner.nextInt()) {
case 1:
hero = new RuiWen();
break;
case 2:
hero = new LiQing();
break;
case 3:
hero = new YaSuo();
break;
case 4:
hero = new JinKeSi();
break;
case 5:
hero = new ChuiShi();
break;
default:
hero = new RuiWen();
}
hero.Q();
hero.W();
hero.E();
hero.R();
}
}
簡(jiǎn)單看一下代碼,定義了一個(gè)Skill類型的變量hreo。通過(guò)輸入不同的值,來(lái)判斷實(shí)例化哪一個(gè)英雄。最后調(diào)用英雄的 Q、W、E、R 方法。
先輸入 1 看一下,輸入 1 應(yīng)該是實(shí)例化銳雯這個(gè)英雄
沒(méi)有問(wèn)題,輸入1成功實(shí)例化了銳雯這個(gè)英雄,并調(diào)用了銳雯的四個(gè)技能。
再換一個(gè)輸入值看一下:
這次輸入了 2 ,實(shí)例化了李青這個(gè)英雄,并調(diào)用了李青的四個(gè)技能。
簡(jiǎn)單說(shuō)一下使用了接口后的優(yōu)勢(shì):
- 使用接口后,實(shí)現(xiàn)了運(yùn)行時(shí)多態(tài),也就是 hero 具體是哪個(gè)類的對(duì)象,在編譯階段我們是不知道的,只有當(dāng)程序運(yùn)行時(shí),通過(guò)我們輸入的值才能確定 hero 是哪個(gè)類的對(duì)象。
- 使用了接口后,所有實(shí)現(xiàn)了 Skill 接口的類,都可以實(shí)例化為 Skill 類型的對(duì)象。如果不是這樣,那有多少個(gè)英雄(類)就要定義多少個(gè)變量。現(xiàn)在英雄聯(lián)盟有145個(gè)英雄,那就要定義145個(gè)變量,這。。。。
總結(jié):
- 接口的作用是定義了定義了一些規(guī)范(也就是定義了一些方法),所有實(shí)現(xiàn)了這個(gè)接口的類,必須要遵守這些規(guī)范(類中一定有這些方法)
- 一個(gè)類實(shí)現(xiàn)了一個(gè)接口, 可以看做 是這個(gè)接口的子類,注意是可以看做。子類類型可以自動(dòng)轉(zhuǎn)換為父類類型,所以任何出現(xiàn)接口的地方,都可以使用實(shí)現(xiàn)這個(gè)接口的類的對(duì)象代替。最常見(jiàn)的就是方法中傳參,定義一個(gè)接口類型的變量,傳入一個(gè)實(shí)現(xiàn)了接口的對(duì)象。
最后:文章是下班后半夜寫的,加上自身能力有限,文中如有不正確的地方,歡迎評(píng)論區(qū)探討,共同提高。