你好!讓我們談談Java中的抽像類。
為什麼類被稱為“抽象”?
您可能還記得抽像是什麼——我們之前討論過 :) 如果您忘記了,不用擔心。請記住, OOP 原則說,在設計類和創建對象時,您應該只表示實體的主要屬性並丟棄次要屬性。例如,如果我們正在設計一個SchoolTeacher
班級,身高可能不是教師的必要屬性。事實上,這個特徵對於老師來說並不重要。但是如果我們要創建一個BasketballPlayer
類,那麼高度將是最重要的特徵之一。好吧,一個抽像類是一組未來類的最抽象、“粗糙的工件”。工件不能直接使用——太“粗糙”了。但它定義了未來類(抽像類的後代)將具有的某些特徵狀態和行為。
Java中抽像類的例子
考慮一個簡單的汽車示例:
public abstract class Car {
private String model;
private String color;
private int maxSpeed;
public abstract void gas();
public abstract void brake();
public String getModel() {
return model;
}
public void setModel(String model) {
this.model = model;
}
public String getColor() {
return color;
}
public void setColor(String color) {
this.color = color;
}
public int getMaxSpeed() {
return maxSpeed;
}
public void setMaxSpeed(int maxSpeed) {
this.maxSpeed = maxSpeed;
}
}
這就是最簡單的抽像類的樣子。如您所見,沒什麼特別的:) 為什麼我們需要這個?首先,它提供了我們所需實體的最抽象描述——汽車。abstract關鍵字在這裡意味著什麼。在現實世界中,沒有“只是一輛車”這樣的東西。有卡車、賽車、轎車、轎跑車和 SUV。我們的抽像類只是一個“藍圖”,我們稍後將使用它來創建特定的汽車類。
public class Sedan extends Car {
@Override
public void gas() {
System.out.println("The sedan is accelerating!");
}
@Override
public void brake() {
System.out.println("The sedan is slowing down!");
}
}
在許多方面,這與我們在繼承課程中談到的內容相似。只有在那種情況下,我們才有一個Car
方法不是抽象的類。但是這樣的解決方案有幾個缺點,這些缺點在抽像類中是固定的。首先,不能創建抽像類的實例:
public class Main {
public static void main(String[] args) {
Car car = new Car(); // Error! The Car class is abstract!
}
}
Java 的創建者是故意製作這個“功能”的。再次記住:抽像類只是未來“常規”類的藍圖。你不需要藍圖的副本,對吧?同樣,沒有必要創建抽像類的實例 :) 如果類Car
不是抽象的,那麼我們可以輕鬆地創建它的實例:
public class Car {
private String model;
private String color;
private int maxSpeed;
public void go() {
// ...some logic
}
public void brake() {
// ...some logic
}
}
public class Main {
public static void main(String[] args) {
Car car = new Car(); // This is okay. The car is created.
}
}
目前,我們的程序中有某種無法理解的汽車。它不是卡車,不是賽車,也不是轎車,但並不清楚它是什麼。是現實中不存在的“只是一輛車”。同樣的例子也可以用在動物身上。想像一下,如果您的程序有Animal
對象(“只是動物”)。目前尚不清楚它是什麼種類,屬於哪個家族,或者俱有什麼特徵。在節目中看到一個會很奇怪。自然界中沒有“只是動物”。只有狗、貓、狐狸、鼴鼠等。抽像類將我們從“只是對象”中拯救出來。它們為我們提供了基準狀態和行為。例如,所有汽車都必須有型號、顏色和最高時速,而且他們還必須能夠使用油門和剎車。就是這樣。這是一個通用的抽象藍圖,您稍後將使用它來設計您需要的類。注意:抽像類中的兩個方法也是抽象的,也就是說它們根本沒有實現。原因是一樣的:抽像類不會為“只是汽車”創建“默認行為”。它們只是表明每輛車必須能夠做什麼。也就是說,如果您確實需要默認行為,則可以在抽像類中實現方法。Java 不禁止這樣做:
public abstract class Car {
private String model;
private String color;
private int maxSpeed;
public void gas() {
System.out.println("Accelerating!");
}
public abstract void brake();
// Getters and setters
}
public class Sedan extends Car {
@Override
public void brake() {
System.out.println("The sedan is slowing down!");
}
}
public class Main {
public static void main(String[] args) {
Sedan sedan = new Sedan();
sedan.gas();
}
}
控制台輸出
Accelerating!
如您所見,我們在抽像類中實現了一個方法,但沒有實現另一個。這樣一來,我們Sedan
類的行為就分為兩部分:如果我們調用它的gas()
方法,行為是從抽象父類“拉上來”的Car
,我們brake()
在Sedan
類中實現方法。這是超級方便和靈活的。但是我們的類現在不那麼抽象了,是嗎?畢竟,它實際上實現了一半的方法。事實是——這是一個非常重要的特徵——即使一個類的方法是抽象的,它也是抽象的. 兩種方法中的一種,或千種方法中的一種——都沒關係。我們甚至可以實現所有方法,不留下任何抽象。結果將是一個沒有任何抽象方法的抽像類。原則上這是可能的——編譯器不會產生任何錯誤——但最好不要這樣做,因為它剝奪了抽像這個詞的意義。你的程序員夥伴看到這個也會很驚訝:/也就是說,如果一個方法被標記為抽象的,每個後代類都必須實現它或被聲明為抽象的。否則編譯器會報錯. 當然,每個類只能繼承一個抽像類,所以抽像類和普通類在繼承上沒有區別。我們繼承抽像類還是普通類並不重要——父類只能有一個。
為什麼Java沒有多類繼承
我們已經說過 Java 中沒有多重繼承,但我們並沒有真正深究為什麼。讓我們現在嘗試這樣做。事實上,如果 Java 確實具有多重繼承,那麼子類將無法決定選擇哪種行為。假設我們有兩個類:Toaster
和NuclearBomb
:
public class Toaster {
public void on() {
System.out.println("The toaster is on. We're toasting!");
}
public void off() {
System.out.println("The toaster is off!");
}
}
public class NuclearBomb {
public void on() {
System.out.println("Boom!");
}
}
如您所見,兩個類都有一個on()
方法。對於烤麵包機,該方法開始烤麵包,但對於核彈,它會引發爆炸。呃哦:/現在想像一下你已經決定(不要問我為什麼!)在兩者之間創造一些東西。這是你的課MysteriousDevice
:當然,這段代碼是行不通的。我們僅將其作為“可能發生的情況”的示例進行介紹:
public class MysteriousDevice extends Toster, NuclearBomb {
public static void main(String[] args) {
MysteriousDevice mysteriousDevice = new MysteriousDevice();
mysteriousDevice.on(); // And what should happen here? Will we get toast or a nuclear apocalypse?
}
}
讓我們看看我們有什麼。這個神秘裝置同時源自烤麵包機和核彈。兩者都有一個on()
方法。on()
因此,如果我們調用一個對象,則不清楚應該執行哪個實現MysteriousDevice
。對像不會理解。最重要的是,NuclearBomb 沒有off()
方法,所以如果我們沒有猜對,就不可能關閉設備。這種“誤解”,在不清楚應該執行哪個行為時,正是 Java 的創造者拒絕多重繼承的原因。也就是說,您將了解到 Java 類可以實現許多接口。
GO TO FULL VERSION