抽象類
如果自下而上在類的繼承層次結構中上移,位于上層的類更具有通用性,甚至可能更加抽象,從某種角度看,祖先類更加通用,人們只將它作為派生其他類的基類,而不作為想使用的特定的實體類,例如,考慮一下對 Employee 類層次的擴展,一名雇員是一個人,一名學生也是一個人,下面將 Person 類和 Student 類添加到類的層次結構中,下圖是這三個類
之間的關系層次圖,
為什么要花費精力進行這樣高層次的抽象呢?每個人都有一些諸如姓名這樣的屬性,學生與雇員都有姓名屬性,因此可以將 getName() 方法放置在位于繼承關系較高層次的通用基類中,現在,再增加一個 getDescription() 方法,它可以回傳對一個人的簡短描述,例如:
an employee with a salary of $5000000
a student majoring in computer science
在 Employee 類和 Student 類中實作 getDescription() 這個方法很容易,但是在 Person 類中應該提供什么內容呢?除了姓名之外,Person 類一無所知,當然,可以讓 Person::getDescription() 回傳一個空字串,然而,還有一個更好的方法, 就是使用 abstract 關鍵字,這樣就完全不需要實作這個方法了,
// no implementation required
public abstract String getDescription();
為了提高程式的清晰度,包含一個或多個抽象方法的類本身必須被宣告為抽象的,
public abstract class Person {
public abstract String getDescription();
}
除了抽象方法之外,抽象類還可以包含具體資料和具體方法,例如,Person 類還保存著姓名和一個回傳姓名的具體方法,
public abstract class Person {
private String name;
public Person(String name) {
this.name = name;
}
public abstract String getDescription();
public String getName() {
return name;
}
}
提示:許多程式員認為,在抽象類中不能包含具體方法,建議盡量將通用的域和方法(不管是否是抽象的)放在基類(不管是否是抽象類)中,
抽象方法充當著占位的角色,它們的具體實作在子類中,擴展抽象類可以有兩種選擇,
- 一種選擇是:在子類中定義抽象類的部分方法或不定義抽象類的方法,這樣就必須將子類也標記為抽象類;
- 另一種選擇是:在子類中定義抽象類全部的抽象方法,這樣一來,子類就不是抽象類了,例如,通過擴展 Person 抽象類,并實作 getDescription() 方法來定義 Student 類,由于在 Student 類中不再含有抽象方法,所以不必將 Student 類宣告為抽象的,
即使一個類不含抽象方法,也可以將該類宣告為抽象類,
抽象類不能被實體化,也就是說,如果一個類被宣告為 abstract,就不能創建這個類的物件,例如,運算式 new Person("Vince Vu") 是錯誤的,但可以創建一個具體子類的物件,
需要注意,可以定義一個抽象類的物件變數,但是它只能參考非抽象子類的物件,例如:Person p = new Student("Vince Vu", "Economics"); 這里的 p 是一個 Person 抽象類的物件變數,p 參考了一個 Student 非抽象子類的實體,
在 C++ 中,有一種在尾部用 =0 標記的抽象方法,被稱為純虛函式,例如:
// C++
class Person {
public:
virtual string getDescription() = 0;
};
在 C++ 中,一個類只要有一個純虛函式,這個類就是抽象類,在 C++ 中,沒有提供用于表示抽象類的特殊關鍵字,
介面
介面(interface)技術主要用來描述類具有什么功能,而并不給出每個功能的具體實作,
一個類可以實作(implement)—個或多個介面,并在需要介面的地方,隨時使用實作了相應介面的物件,
在下面的小節中,你會了解 Java 介面是什么以及如何使用介面,
介面概念
在 Java 程式設計語言中,介面不是類,而是對類的一組需求描述,這些類要遵從介面描述的統一格式進行定義,實作介面的類必須定義介面中宣告的所有方法,
在介面中還可以定義常量,然而,更為重要的是要知道介面不能提供哪些功能,介面絕不能含有實體域,在 Java8 之前, 也不能在介面中實作方法,(在 Java8 及之后,可以在介面中實作默認方法,)提供實體域和方法實作的任務應該由實作介面的那個類來完成,
介面中的方法都自動地被設定為 public ,介面中的域都自動地被設定為 public static final,因此,在介面中宣告方法時,不必提供關鍵字 public,
為了讓類實作一個介面,通常需要下面兩個步驟:
- 將類宣告為實作給定的介面,要將類宣告為實作某個介面,需要使用 implements 關鍵字
- 對介面中的所有方法進行定義,
介面的特性
介面不是類,尤其不能使用 new 運算子實體化一個介面:
x = new Comparable(...); // ERROR
然而, 盡管不能構造介面的物件,卻能宣告介面的變數:
Comparable x; // OK
介面變數必須參考實作了介面的類物件:
x = new Employee(...); // OK provided Employee implements Comparable
接下來,如同使用 instanceof 檢查一個物件是否屬于某個特定類一樣,也可以使用 instanceof 檢查一個物件是否實作了某個特定的介面:
if (anObject instanceof Comparable) { ... }
與可以建立類的繼承關系一樣,介面也可以被擴展,這里允許存在多條從具有較高通用性的介面到較高專用性的介面的鏈,例如,假設有一個被稱為 Moveable 的介面:
public interface Moveable {
void move(double x, double y);
}
然后,可以以它為基礎 擴展一個叫做 Powered 的介面:
public interface Powered extends Moveable {
double milesPerCallon();
}
雖然在介面中不能包含實體域或靜態方法,但卻可以包含常量,例如:
public interface Powered extends Moveable {
double milesPerCallonO;
double SPEED_LIHIT = 95; // a public static final constant
}
與介面中的方法都自動地被設定為 public —樣,介面中的域將被自動設為 public static final,
可以將介面方法標記為 public,將域標記為 public static final,有些程式員出于習慣或提高清晰度的考慮,愿意這樣做,但 Java 語言規范卻建議不要書寫這些多余的關鍵字,
可以為介面方法提供一個默認實作,必須用 default 修飾符標記這樣一個方法,
public interface Comparable<T> {
default int compareTo(T other) {
return 0;
}
// By default, all elements are the same
}
介面 & 抽象類
為什么 Java 程式設計語言還要不辭辛苦地引入介面概念?為什么不將 Comparable 直接設計成如下所示的抽象類,
// why not?
abstract class Comparable {
public abstract int compareTo(Object other);
}
然后,Employee 類再直接擴展這個抽象類,并提供 compareTo() 方法的實作:
// why not?
class Employee extends Comparable {
public int compareTo(Object other) { ... }
}
非常遺憾,使用抽象類表示通用屬性存在這樣一個問題:每個類只能擴展于一個類,假設 Employee 類已經擴展于一個類,例如 Person,它就不能再像下面這樣擴展第二個類了:
class Employee extends Person, Comparable // Error
但每個類可以像下面這樣實作多個介面:
class Employee extends Person implements Comparable // OK
有些程式設計語言允許一個類有多個父類,例如 C++,我們將此特性稱為多重繼承(multiple inheritance),而 Java 的設計者選擇了不支持多繼承,其主要原因是多繼承會讓語言本身變得非常復雜(如同 C++),效率也會降低(如同 Eiffel),
實際上,介面可以提供多重繼承的大多數好處,同時還能避免多重繼承的復雜性和低效性,
可以將介面看成是沒有實體域的抽象類,但是這兩個概念還是有一定區別的,介面 & 抽象類的區別:
- 它們可以包含的內容不同:
- 抽象類中可以包含資料域(實體域、static 域、final 域)、具體方法、抽象方法,
- 介面中不能包含實體域,但可以包含常量(static final 域)、默認方法,介面中的方法都自動地被設定為 public ,介面中的域都自動地被設定為 public static final
- 它們的用途不同:
- 抽象類的用途是:在子類繼承父類時,父類的一些方法實作是不明確的(父類對子類的實作一無所知),這時需要使父類是抽象類,在子類中提供方法的實作(抽象類和普通的類是十分相似的:普通類中有的,抽象類中也都可以有,只是抽象類中可以有抽象方法)
- 介面的用途是:介面主要用來描述類具有什么功能,而并不給出每個功能的具體實作,實作介面的類必須定義介面中宣告的所有方法,確保一個類(實作介面的類)實作一個或一組特定的方法,
- 在 Java 程式設計語言中,每個類只能夠擁有一個父類,但卻可以實作多個介面
參考資料
《Java核心技術卷一:基礎知識》(第10版)第 5 章:繼承 5.1.9 抽象類
《Java核心技術卷一:基礎知識》(第10版)第 6 章:介面、lambda 運算式與內部類 6.1 介面
本文來自博客園,作者:真正的飛魚,轉載請注明原文鏈接:https://www.cnblogs.com/feiyu2/p/17387040.html
轉載請註明出處,本文鏈接:https://www.uj5u.com/houduan/552084.html
標籤:其他
上一篇:【11個適合畢設的Python可視化大屏】用pyecharts開發拖拽式可視化資料大屏
下一篇:返回列表