Chapter 13 翻譯 (Abstact Classes and Interfaces)
抽象類同接口
詞彙
- geometric 幾何的
- subject 話題
概念
- 一個 superclass(超類) 定義相關 subclasses(子類) 的共同行爲
- 一個 interface(接口) 定義多個類的共同行爲(包括不相關的類)
13.2 Abstract Classes 1493
- abstract class (抽象類) 不能用去創建對象
- abstract class 可以包括 abstract method (抽象方法)
- abstract method (抽象方法) 會被 concrte(真實的) 子類所實現
- perimeter 週長
- are referred to as
- denote
- modifier
- appropriate
13.2.2
- is worth noting 是值得注意的
13.51
13.1 Introduction p1492
父類定義相關子類的共同行爲。接口可以用來多個類的共同行爲(包括不相關類)。
你可以使用 java.util.Arrays.sort
方法去排序一個數字數組或者字符數組。你可以依然用 sort
方法去排序一個幾何數組?爲了寫出這樣的代碼,你必須知道接口。一個接口定義類的行共同爲。在討論接口前,我們引入一個相關主題:抽象類。
13.2 Abstract classes
一個抽象類不能去創建對象。一個抽象類可以包含抽象方法,抽象方法會實現於實子類 concrete subclasses
。
在 inheritance hierarchy 繼承體系
中,隨着新類的出現,類變得越來越具體,並且變得越來越實。如果你從子類返到父類,類會越來越通用越來越不具體。類的設計應該確保父類包含子類的共同功能。有時候,一個父類係好抽象,抽象到不可以建立具體實例。噉嘅類 被人叫作 is referred to as
抽象類 abstract class
。
在 charpter 11 里,GeometricObject
定義成一個父類,Circle
同 Rectangle
的父類。GeometricObject
方法將幾何對象的共同特徵造成模型(model 將...做成模型
)。兩者 Circle
同 Recangle
包含咗 getArea()
同 getPerimeter()
方法來計算圓同方型嘅面積和 週長 perimeter
。
因爲你可以計算所有幾何圖形的面積同週長,最好定義 getArea()
同 getPerimeter()
方法在幾何對象類之中。但是這些方法不能被實現在幾何對象類中,因爲這些實現要基於特定的幾何對象類型。這樣的方法被稱作抽象類,並且用 abstract
修改器表示。當你定義了多個抽象方法於 GeometricObject
後,個類變成抽象類。
抽象類用 abstract
修改器標識。在 UML 圖上,抽象類同抽象方法的名字都會是斜體。
GeometricObject
|__ getArea(): double
GeometricObject.java
1 | public abstract class GeometricObject { |
抽象類就好似普通的類,但係不可以用 new 來創建抽象類的實例。一個抽象方法只有定義,沒有實現。實現是它的子類的事情。一個類包含抽象方法,就必須定義成抽象類啦!
abstract class
的構建器被定義成 protected
,因爲只會它的子類會使用它。當你創建一個實子類的實例,是包含了它的父類構造器。
GeometricObject
抽象類定義了幾何對象的共同功能,並且提供了適當的構造器。因爲你不知道如何去計算幾何對象的面積和週長,所以 getArea()
同 getPerimeter()
定義成抽象。這些方法會在它的子類實現。 Circle
同 Rectangle
的實現都一樣,除了他們延伸 GeometricObject
類不一樣。
Circle.java
1 | public class Circle extends GeometricObject { |
Rectangle.java
1 | public class Rectangle extends GeometricObject { |
13.2.1 Why Abstract Methods?
你可以奇怪有什麼好處,在 GeometricObject
類裡去定義 getArea()
同 getPerimeter()
抽象方法。下面的例子顯示了定義 GeometricObject
類的好處,包括 equalArea
方法檢查是否兩者面積相等,包括 displayGeometricObject
方法去顯示它們。
TestGeometricObject.java
1 |
|
方法 getArea()
同 getPerimeter()
定義在 GeometricOjbect
類度,這些方法會被 Circle
同 Rectangle
類所覆蓋。下面 statement表達式
:
1 | GeometricObject geoObject1 = new Circle(5); |
創建一個新的圓形同新的長方形,之後 指向到 assign to
變量 geoObject1
同 geoObject2
。這兩個變量都是 GeometricObject
類。
當 調用invoke
equalArea(geoObject1, geoObject2)
, getArea()
方法定義在 Recrangle
類中,個類係用於 object2.Area()
裡,因爲 goeObject2
係一個長方形。`
同樣,當提及 displayGeometricObject(geoObject1)
,Circle
的 getArea()
同 getPerimeter
就會被使用。 displayGeometricObject(geoObject2)
就係 Rectangle
相應的類。JVM 動態地決定哪一個方法被調用,具體係基於真實的對象調用了變一個方法。
Note 注意
,如果 getArea
方法沒有被定義在 GeometricObject
類,你就不能定義比較兩個幾何對象是否有相同的面積的 equalArea
方法。你已經見到定義個抽象方法係“共同”類的好處。
13.2.2 Interesting Points about Abstract classes
如下幾點關於抽象類 is worth noting 是值得注意的
:
- 抽象方法不能包含在非抽象類度。如果一個子類屬於抽象父類,未能實現所有的抽象方法,那麼子類必須成抽象。換句話說,在一個非抽象子類,個類延伸於抽象父類,所有的抽象方法必須被實現。也要注意
抽象方法不能係靜態
。 - 使用
new
操作符不能初始化一個抽象類,但係你仍然可以定義佢嘅構造器,某某被調用在子類的構造器上。例如,GeometricObject
的構造器會被調用在Circle
同Rectangle
類裡面。 一個類包含抽象方法,一定要係抽象。但係可以定義一個抽象類不包含任何抽象方法。
這個抽象類可以作爲定義後面子類的基礎類。- 一個子類可以覆蓋來自父類類的方法,子類定義個方法成抽象。這樣是非常不正常,但是當實現父類的方法成爲無效,就幾有用。這種情況,子類必須定義成抽象。—— 實體方法被覆蓋成抽象
- 一個子類可以是抽象,
even if 即使
父類是真實方法。例如,object
類是實體方法,但是它的子類GeometricObject
是抽象。 - 你不能使用
new
操作符從抽象類創建一個實例,但是一個抽象類可以作爲一個抽象類。因此,如下的語句,創建一個數組,數組的元素是GeometricObject
類。1
GeometricObject[] objects = new GeometricObject[10];
- 你可以創建一個實例,
GeometricObject
的實例 (Circle),並且assign a to b 將a配給b
將 Circle 的引配到數組上。1
object[0] = new Circle();
13.2 Check Point p1506
1.
13.3 Case Study: the Abstract Number
Class
Number
是一个抽象的父类,是数组容器类 BigInteger
和 BigDecimal
的抽象父类。
Section 10.7
引入了數字 wrapper 封裝器/包裝紙
類。 Section 10.9
引入了 BigInteger
和 BigDecimal
類。這些類有共同的方法 byteValue()
, shortValue()
, intValue()
, longValue()
, floatValue()
和 doubleValue()
用於返回 byte
, short
, int
, long
, float
, 和 double
值,返回的這些值都是來自這些類。這些共同方法是定義入 Number
類,這個類是數字封裝類 BigInteger
和 BigDecimal
的父類。
1 | doubleValue() <-- Double |
個 Number
類是一個抽象父類, Double
, Float
, Long
, Integer
, Short
, Byte
, BigInteger
和 BigDecimal
的父類。
因为 intValue()
, longValue()
, floatValue
和 doubleValue()
方法不能被实现在 Number
类里,所以這些方法在 Number
類裡面定義成抽象方法。 Number
類因此是抽象方法。byteValue()
同 shortValue()
方法由 intValue()
方法實現:
1 | public byte byteValue() { |
Number
定義成數字類的父類,可以定義多個方法去 perform執行
數字的多個共有操作。下面畀出一個程式去找一列 Number
對象中最大的數字。
1 | import java.util.ArrayList; |
个程序创建了一个数组,一个数组有多个 Number
对象。个数组加上一个 Integer
对象, Double
对象,一个 BigInteger
对象,和一个 BigDecimal
对象到个数组。
调用了 getLargestNumber
方法,个方法返回了最大的数子。个 getLargestNumber
方法返回了 null
,如果个列表是 null
或者个列表的大细是0。去找出列表最大的数,个数字通过调用 doubleValue()
方法去比较。个 doubleValue
方法是定义在 Number
类里面,并且在 Number
的实现子类里实现。如果一个数是一个 Integer
对象,那么 Integer
的 doubleValue
会被调用。如果数是 BigDecimal
对象,就调用 BigDecimal
的 doubleValue()
。
如果 doubleValue()
方法没有被定义在 Number
类里,你就不可以在多个不同类型的数字中找到最大的数。
13.4 Case Study: Calendar
and GregorianCalendar
GregorianCalendar
是一个抽象类 Calendar
的 实体子类。
java.util.Date
的实例代表一个特定的实例,实例是以时间的形式,带有 millisecond 毫秒
的精度。 java.util.Calendar
是一个抽象的基础类,提取日历信息的基础类,例如:月,日,小时,分,同秒。 Calendar
的子类能够实现特定的日历系统,例如 Gregorian calendar,阴历,犹太历。目前,Gregorian 日历的 java.util.GregorianCalendar
是在java被支持的。 add
方法 在 Calendar
类中是抽象的,因为它的实现是基于特定实体日历系统。
13.5 Interface 接口
一个接口像一个类构造器,用于定义多个对象的共同操作。
『在多个方面 in many ways』,一个接口类似抽象类,但是 意图intent
是去规定多个相关或不相关类的共同行为。例如,使用多个接口,去规定对象是否可以comparable 比较
、 可食用的 edible [ˈedəbl]
、 cloneable 复制
。
distinguish sth from sth
比较一个接口和类,Java 使用这样的语法去定义接口:
1 |
|
Ex.
1 | public interface Edible { |
一个接口在 Java 中就当作「特殊的类」。每个接口编译成独立的二进制文件,和一般类一样。用接口 『more or less 几乎』 同用抽象类差唔多。例如你用接口当引用變量的数据类。『如同一樣 as with…』抽象類,你不能利用 new
從一個接口創建一個實例。
可以使用 Edible
接口去規定一個對象是否可以食。對象『執行Implement』接口類使用 implements
關鍵字。例如,「雞」類同「水果」類都會執行「可食用」接口。類與接口的關係『be know as 畀認爲係』『接口繼成Interface Inheritance』。因爲接口繼成同類繼成基本都一樣,都統一『refer to指的是』繼成。
接口定義了多個對象的共同行爲。
注意:在接口裡,數據場的『首飾器Modifier』public static void
和方法的首飾器 public abstract
都可以忽略。
下面係相等:
1 | public interface T { |
等於
1 | public interface T { |
雖然 public
首飾器可以在接口裡忽略,但是當那個方法在子類實現,那個方法依然要是 public
。
注意:默認方法 Default Method
Java 8 引入默認接口方法,通過使用 default
關鍵字。一個默認方法提供一個默認實現。一個類執行一個接口,可以使用方法的默認實現,或者通過新執行去覆蓋默認。這個功能使你可以添加新方法到已經存在的接口,利用默認實現,不用重寫存在的多個類執行接口的代碼。
Java 8 又允許『公開靜態方法Public Static Method』存在在接口。公開靜態方法在接口中使用和普通方法一樣目的。
1 | public interface A { |
Check Point
- 不能使用
new A()
来创建接口。 - 可以用接口来创建参变量
A x;
- 接口正確?不正確?
正確不正確1
2
3interface A {
void print();
}1
2
3
4// interface 裡面的方法是抽象,不帶具體實現{};
interface A {
void print() {};
}1
2
3abstract interface A {
abstract void print() {};
}1
2
3
4// 不能直接 print() 而不表明 void
abstract interface A {
print();
}1
2
3interface A {
default void print() { }
}1
2
3
4
5interface A {
static int get() {
return 0;
}
} - 解釋:
- All methods defined in an interface are public. 「所有」接口方法是公共
- When a class implements the interface, the method must be declared public. 實現方法必須是公共
- public 可見性不能忽略
13.6 The Comparable
Interface
这个 Comparable
接口定义了用来比较多个对象的方法 compareTo
。
假设你希望设计一个通用的方法去找相同类的两个对象中更大的一个。例如两个学生、两个日期、两个圆、两个长方形。所以,两个对象必须是可比较,两个对象的共同行为必须是可比较的。 Java 提供了 Comparable
接口去做呢件事。
1 | java.lang.Comparable |
1 | // 比较多个对象的接口,这个定义在 java.lang 里面 |
compareTo
方法决定了一个有对象 o
的对象的顺序,小于 o
会返回负,等于系0,大于系正数。
Comparable
接口是一个通用接口。當實現這個接口時候,通用类 E
係會被實體類去替代。好多 Java 庫的類都實現了 Comparable
去定義對象中的順序。類如 Byte
, Short
, Integer
, Long
, Float
, Double
, Character
, BigInteger
, BigDecimal
, Calendar
, String
同 Date
都實現了 Comparable
接口。
例如 Integer
, BigInteger
, String
, Date
類在 API 中定義了如下:
1 | // Integer |
1 | // BigInteger |
1 | // String |
1 | // String |
你可以使用 compareTo
方法去比較兩個數字,兩個字符等等。
Ex:
1 | System.out.println(new BigInteger("2312313").compareTo(new BigInteger("5"))); |
s
是一個 String
對象,下面都是事實:
1 | s instanceof String |
因爲所有的 Comparable
對象都有 compareTo
方法,所以 java.util.Arrays.sort(Object [])
方法使用 compareTo
方法去比較同排序數組的對象。放落去的 object 對象都是 Comparable
接口的實例。
下面是排序字符串數組的例子:
1 | // SortComparableObjects.java |
1 | GZ SZ ZH |
不能用 sort
方法去排序多個長方形的數組。因爲 Rectange
沒有實現 Comparable
。但是,你可以定義一個新的長方形類,個長方形類係實現了 Comparable
。這個新類的實例是可以比較。
這裏建一個新類 ComparableRectangle
:
1 | // ComparableRectangle.java |
ComparableRectangle
延伸 Rectangle
並且實施咗 Comparable
。關鍵字 implements
說明了 ComparableRectangle
繼承 Comparable
接口的所以常量和方法。 compareTo
方法比較了兩個長方形的面積。 ComparableRectangle
的實例也是 Rectangle
, GeometricObject
, Object
和 Comparable
的實例。
注意:
接口和它的方法的名字都是 斜體 。
java.lang.Comparable
compareTo(o: ComparableRectangle): int
用虛線空心箭頭去指向接口
ComparableRectangle
延伸 Rectangle
和 實施了 Comparable
你可以使用 sort
方法去排序多個 ComparableRectangle
對象的數組:
1 |
|
- 一个接口提供通用编程的另外一种形式。
- 如果不使用接口,可能会有些困难去使用通用排序
sort
方法去排序那些对象。 - 因为多「重继承係」係好必要去繼承
Comparable
同另外一些類,例如Rectangle
。
object
類包含了equal
方法,這個方法是被子類所繼承,子類能夠覆蓋。- 這樣就能夠比較是否多個對象是相等的。
- 假設
Object
類包含了compareTo
方法,『as由於』compareTo
定義在Comparable
接口裡,所以sort
方法能夠被使用,來比較一組對象。 - 是否一個
compareTo
方法應該被包含在Object
類裡,仍然在爭論中。 - 因爲
compareTo
方法在Object
類中沒有定義,如果多個對象都繼承Comparable
接口,那麼這個Comparable
接口使多個對象能夠相互比較。 compareTo
應該同equals
『一樣 be consistent with』。- 如果
o1.equals(o2)
係真,那麼o1.compareTo(o2) == 0
。 - 如果兩個有相同面積,你應該覆蓋
ComparableRectangle
的equals
方法返回true
。
Check Point
- 如果一個類 實施implement 咗
Comparable
, 那麼這個類的實例可以有compareTo
方法。True! public int compareTo(String o)
is forcompareTo
mehtod in theString
cl- 如果他們的共同類,或者共同父類,都有
compareTo
,先可以比較,否則 error。 - 要 implements 咗
Comparable
接口先可以定義compareTo
方法。1
2
3
4
5
6
7
8
9// 實施 Comparable<類名>
public class ComparableRectangle extends Rectangle
implements Comparable<ComparableRectangle> {
public int compareTo(ComparableRectangle o) {
// ...
}
} - A:
Person
類沒有 implementComparable
接口,不可以 sort 。
13.7 The Cloneable
Interface
Cloneable
接口 規定了 那個 對象 是 可以 複製的。- 創建 一個 對象 的 副本 是 『desirable想有的』。
- 你需要使用
clone
方法 並且 明白Cloneable
接口。 - 雖然 一個 接口 包含 常量 同 抽象方法,但是
Cloneable
接口 是 一個特殊例子。 Cloneable
接口定義在java.lang.Cloneable
。
1 | package java.lang; |
- 個接口是空的。
- 接口無內容,『被叫做是個 is referred to as a』
marker interface
標記接口。 - 一個 標記接口 用來『表示denoet』類『擁有 possess [pəˈzes]』特定的並且想有的屬性。
- 一個 類 實施咗
Cloneable
接口 就標記成 可複製。 Clone()
方法已經定義在Object
類中,這個類的對象可以被複製,通過Clone()
方法做到。
- Java 許多類,例如
Date
,就實施了Cloneable
。 - 因此,這些類都是可 複製的。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26import java.util.*;
public class DateClone {
public static void main(String[] args) {
Calendar calendar = new GregorianCalendar(2013, 2, 1);
Calendar calendar1 = calendar;
Calendar calendar2 = (Calendar)calendar.clone();
System.out.println("calendar = calendar1 is " +
(calendar == calendar1));
System.out.println("calendar = calendar2 is " +
(calendar == calendar2));
System.out.println("calendar.equals(calendar2) is " +
calendar.equals(calendar2));
}
}
/*
⇒ java DateClone
calendar = calendar1 is true
calendar = calendar2 is false
calendar.equals(calendar2) is true
*/ - 『前面的preceding』代碼,
Calendar calendar1 = calendar;
複製了calendar
的地址到calendar1
。 - 所以
calendar
同calendar1
都指向相同的Calendar
對象。 Calendar calendar2 = (Calendar)calendar.clone();
創建新的對象,個對象是原來calendar
的副本,並且將新的副本的地址分配給calendar2
。- 所以
calendar2
和calendar
是不同的對象,雖然有着相同的內容。
1 | // ArrayTest.java |
- 在上面的代码
ArrayList<Double> list2 = (ArrayList<Double>)list1.clone();
中list1
的副本地址去到list2
。list1
和list2
是不同的对象,虽然内容相同。ArrayList<Double> list3 = list1
表明 3 同 1 都是指向相同的对象。- 所以 1 同 3 都会同时小咗 1.5
1 | // List2.java |
- 数组的
clone()
的返回類是同複製前一樣。 list1.clone()
的返回類是int[]
,因爲list1
是int[]
。
- 去定義一個自定義的類,這個類實施了
Cloneable
接口,這個類必須要『覆蓋override』Object
類的clone()
方法。
1 | // House.java |
House
類實施了clone
方法,個clone
方法原來定義在Object
類中。clone
方法的 頭部 定義在Object
類中是這樣1
protected native Object clone() throws CloneNotSupportedException;
- 個關鍵字
native
說明了這個方法不是用 Java 寫的,而是用 JVM 去實施的。 - 個關鍵詞
protected
限制了這個方法,只可以被相同Package 包
或者 子類 去訪問。 House
類必須覆蓋某些方法,並且改變「修改器modifier」的可見度,這樣那個方法先可以用在任何包中。- 因爲
clone
方法實現在Object
類裡,去做對象複製,所以House
的clone
方法簡單就用super.clone()
就可以喇。 - 個
clone
方法定義在object
類裡,如果個對象不是「可複製的cloneable」,就會跳出CloneNotSupportedException
。 - 因爲我們已經做了「錯誤」捕捉在原生,所以無必要在這裡做。
House
類實現了Comparable
接口的compareTo
方法。個方法可以比較兩個對象。- 你現在可以創建一個對象
House
,並且創建副本:1
2House house1 = new House(1, 1750.50);
House house2 = (House)house1.clone(); house1
同house2
是不同兩個對象,是有着一樣的內容。Object
類的Clonel
方法复制每个域到目标对象。- 如果個域係「原始的primitive」類,那麼值就會被複製。
- area(double) 的值會從 house1 複製到 house2 。
- 如果個域是一個對象,那麼域的地址會被複製。
- 例如
whenBuilt
是屬於Date
,它的地址會複製到house2
。 - 儘管
house1==house2
是錯的,但是house1.whenBuilt == house2.whenBuilt
是真的! - 這就叫『淺Shallow』複製。不是深複製。如果個域是對象,只會複製地址,不是常量。
- 去執行「深度複製」
- 要改下
clone
方法。1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20// House.java
public class House implements Cloneable, Comparable<House> {
....
public Object clone() {
try {
House houseClone = (House)super.clone();
// clone 目標對象是深度複製,對象裡的對象是淺複製
houseClone.whenBuilt =
(java.util.Date)(WhenBuilt.clone());
return houseClone;
}
catch
return null;
}
}
}
用下面的代碼複製 House
對象:
1 | House house1 = new House(1, 750.50); |
house1.whenBuilt == house2.whenBuilt
就是 false
喇。
- 關於
clone
方法 和Cloneable
接口有多個問題 - 1st, 爲什麼
Object
的clone
方法是被「保護的protected」,而不是共用「public」。- 不是每個對象都可以被複製。
- 如果子類生成的對象可以複製,Java 設計師專登強制子類要去「覆蓋Override」。
- 2nd, 爲什麼
clone
方法不定義在Cloneable
接口中?- Java 提供一個原生的方法去執行淺複製。
- 因爲接口的方法是「抽象 abstract」,所以原生方法不能在接口中實現。
- 因此,設計師決定定義並且實現個
clone
方法在Object
中。
- 3rd, 爲什麼
Object
類不實現Cloneable
接口?- 同1st。
- 4th, 如果
House
類不實現Cloneable
,會發生什麼?house1.clone()
會返回null
。- 因爲
super.clone()
會掟出CloneNotSupportedException
。
- 5th, 你可以在
House
類 實現clone
方法,但是又唔用 clone 方法。1
2
3
4
5
6public Object clone() {
House houseClone = new House(id, area);
houseClone.whenBuilt = new Date();
houseClone.getWhenBuilt().setTime(whenBuilt.getTime());
return houseClone;
} - 在這個例子中,
house
類不需要實現Cloneable
接口。 - 你必須確保所有數據都要正確複製。
- 使用
Object
類中clone()
方法『減輕你從relieve you from』手動複製收據的工作。 Object
的clone
方法會自動地執行淺複製所以收據域。
Check Point
- 如果一个类不实施
java.lang.Cloneable
,類能不能用super.clone()
。- 不能用
super.clone()
Date
類實施了Cloneable
- 不能用
- 如果不在類裡面覆蓋
clone()
,因爲java.lang.Object
是受保護的,直接使用係個語法錯誤。 - 如果不實施
java.lang.Cloneable
係一個run time error,意思就係用時候會返回 null 。
- 如果不在類裡面覆蓋
==
號驗證兩個是否指向一個對象xx.equals.aa
驗證內容是否相同
=
指向同一個對象list.clone()
意味着全新的對象
- 要用
xxx.clone()
1)要實施java.lang.Cloneable
2)寫override clone()
13.8 Interface vs. Abstract Classes
一個類,可以實施多個接口,只可以延伸一個父類。
一個接口同抽象類「幾乎more or less」相同地使用。
但是接口同抽象類的定義不一樣
X 變量 構建器Constructor 方法 抽象類 冇限制 Constructor 純粹加入給子類,不能初始化 冇限制 接口 所以變量必須public static final 沒有constructor,不能初始化 public abstract instance, public default, and public static
- Java 只允許類延伸的單獨繼承
- 但是允許多重接口多重延伸
1 | public class NewClass extends BaseClass |
- 一個接口可以通過
extends
繼承另一個接口。 - 這個接口叫做
子接口subinterface
1 | public interface NewInterface extends Interface1, Interface2, Interface3 { |
- 一個類實施
NewInterface
必須實施來自NewInterface
,Interface1
,Interface2
,Interface3
的抽象方法。 - 一個接口可以延伸多個接口,但係不能延伸多個類
- 一個類係可以延伸多個類(父類)和實施多個接口。
- 所以的類共享同一個根,叫
Object
類 - 但是,沒有多個接口的唯一根
- 一個接口類的變量可以「參考 Reference」任何實施了這個接口的類所生成的實例。
- 如果一個類實施了一個接口,個接口就如同這個類的父類
- 你能把接口當成數據類,接口可以「投射 cast」接口的變量到它的子類。
- 例如
c
是最上層class2
的實例,那麼也同時是Object
,class1
,Interface1_2
interface1_1
,interface1
, …. 的實例。
命名規則
類的名字是名詞。接口的名字一般是形容詞或者名詞。
設計說明
- 抽象類 和 接口 都可以用去規定多個對象的共同行爲。
- 如何決定使用 接口 和 類。
- 通常嚟講,「是一個关系 is-a relationship」清楚地描述父子关系
- 父子关系用多个类来展示
- Gregorian calendar 是一个 calendar
java.util.GregorianCalendar
同java.util.Calendar
的关系通过类继承来「展示 model」。- 一个弱「是一关系」,说明一个对象「possess拥有」一个特定的属性
- 一个弱「是一关系」,可以通过使用接口来表示。
String
类实施了Comparable
接口,所以所有的字符串系可比较的。
接口更推荐使用,而不是抽象类
因为接口可以定义不相关类的共同父类
接口比一般类更灵活
想想 Animal
類, howToEat
方法定義在 Animal
類中如下。
1 | abstract class Animal { |
動物的子類如下:
1 | class Chicken extends Animal { |
1 | class Duck extends Animal { |
「inheritance hierarchy 繼承 層次體系」、「polymorphism多形態性」使你可以有個 Animal
變量指向 Chicken
對象和 Duck
對象的地址。
1 | public static void main(String[] args) { |
基於具體的對象,個對象提及的方法,JVM判斷使用來自哪個對象的 howToEat
。
你可以定義 Animal
的子類。
但是,有個限制。子類必須其它動物。
另外一個問題出現,如果一個動物是不能食,是不能延伸 Animal
類。
接口不會這些問題。
接口有更多靈活性,因爲你不需要成個接口內容連結到特定類。
你可以定義接口 howToEat()
方法,讓它服務多類。
例如:「Edible interface 可食接口」與 「Chicken Class 雞類」
1 | // DesignDemo |
定義一個類,類「represent 代表」可食對象
簡單將類實施 Edible
接口
類現在是 Edible
類的子類, Edible
對象可以用 howToEat
方法。
Check Point
- 接口
是
編譯成另外獨立的二進制代碼 - 接口
不能
有靜態方法 - 接口
可以
延伸多個接口 - 接口
不能
延伸抽象類 - 接口
可以有
默認方法
- 接口
13.9 Case Study: The Rational有理由的
Class
這個部分顯示了如何設計一個有理類,個類能表達同處理有理數。
一個有理數有「numerator分子」和「denominator分母」,寫成 a/b
有理數分母不能爲0,但係0作爲分子就可以。
整數 1 等於 i/1。
有理數用在準確的計算當中。
意味着, 1/3 = 0.3333333
就不能准确地表达,以 double
或者 float
浮点格式都不能。
去获得准确的结果,我哋必须使用有理数。
Java 提供浮點数据类,但是沒有有理數。
這個部分顯示了如何設計一個類,去表達有理數。
因爲有理數同 Integer
和 float-point
有許多共同特點,
並且 Number
是數字包裹器的根類,比較合適就是定義 Rational
作爲 Number
的子類。
因爲有理數是可以比較的,所以 Rational
類應該實施 Comparable
接口。
1 | java.lang.Number <-----------------| Rational | |
doubleValue()
定義在 java.lang.Number
裡面,並且在 Rational
中重寫。
如果字符串「is concatenated with 接上」一個對象,而且通過 +
號,這個對象的字符串表達式 toString()
就會被用上。
所以 r1 + " + " + r2 + " = " r1.add(r2)
等同於 r1.toString() + " + " + r2.toString() + " = " r1.add(r2).toString()
p1580
- is intended for 是专供
- particular 专门的
p1581
- immutable 不可改变的
- limitation 局限 # 区别limit
- overflow 溢jat出
check point
#2 #3
1 | Rational r1 = new Rational(-2, 6); // Rational 有實現 compareTo方法 |
#4
1 | if ((this.subtract((Rational)(other))).getNumerator() == 0) |
其實就係
1 | return ((this.subtract((Rational)(other))).getNumerator() == 0); |
用 conditional operator 簡化:
1 | if (this.subtract(o).getNumerator() > 0) |
5
- revise 修正
13.10 Class-Design Guidelines 類設計說明
1585
- sound 可靠的 adj
- cohesion 結合 凝聚性
- entity 實體
- coherent 有調理的
- separate 獨立的 adj
- synchronize 同步v
p1587 - as is the case 一直都係
- encapsulate date field 封裝 數據 域
p1588 - achieve 實現
- clarity 明確性
- contract 合約
- incorporate 包含
- impose 推行
p1589
- intuitively 直观地 [ɪnˈtjuːɪtɪvli]
- derive 取得
- depend on 基於
1590
- is dependent on 依賴於
錯誤
1 |
|
應該
1 | private class SomeThing { |
1591
- integral 必要的
- mistakenly 錯誤地
- overlook 忽略v
- is independent of 獨立於
1592
- aggregation 集合、總量
- possesse 有
1595
- construct 概念、觀念
- in many ways 在好多方面