Inheritance
22 April 2020
OOP - Inheritance
定義
base class -> derived class
superclass -> subclass
ancestor class -> descendant class(可能中間繼承很多層class)
- 一個base class可以有多個derived class,base class只定義所有derived class都有的variable, method,而derived class就不用重新定義這些variable, method,直接跟base class繼承即可 因此derived class會擁有base class的variable, method,也可以call public method,而不用再derived class裡面也宣告。
- 能夠reuse code
- 繼承public/private (static) instance variable, static variable, public method
- 只有private method不會被繼承
- private data雖然會被繼承,但是不能直接使用或透過derived classs的method get/set來存取base class的private data,一定只有base class的method能修改存取這些被繼承的private data(詳情可見StackOverflow)
public class HourlyEmployee extends Employee
{...}
重點 - 繼承的class為同類別
- Derived的class屬於所有他的ancestor class的data type,因此沒有相容的問題,如果某method參數是ancestor class,是可以傳Derived class的object進去當參數的。
- private data雖然會被繼承,但是不能直接使用或透過derived classs的method get/set來存取base class的private data,一定只有base class的method能修改存取這些被繼承的private data(詳情可見StackOverflow)
- 目的是為了保護Base class的private data,如果child class能access base class得private data的話,攻擊者能直接產生一個子class來修改base class data。
Derived class要使用base class的private變數, 一定要用base class的accessor(private class getB不能直接return this.a)
class A {
private int a;
public A(int a) { this.a = a; }
public int getA() {return a;}
}
class B extends A {
public B(int b) { super(b); }
public int getB() {return getA();}
}
int result = new B(10).getA();
public class B extends A{
public B(B obj){
super(obj);
}
}
由此可知super是呼叫Base class A的constructor,但是傳derived class B的object
當作參數是OK的,因為derived class的object是屬於ancestor class的data type。
因此可以說class B和class A是同一種data type
public A{
private int t = 5;
}
public B extends A{
public int getT(){
return this.t; //error t是private屬於Class A, Class B雖然有,但不能修改存取
}
public void setT(int e){this.t = e;} //error 同上
}
overriding
base class有的method,在derived class宣告一個名字依樣的method,但內容調整就override了,因此該method就算base class有,仍會以derived class裡的method為主。
- method的signature要依樣(有相同method名, 相同參數)
- 兩個method壹定要是繼承關係(所以會屬於不同class)
- base class裡宣告final的method,不能再derived class被override
- 宣告final的class不能作為base class,因為內部的instance variable, method都不能改
covariant return
override但有不同的return值 一般來說,override的method只是做的事情不依樣,return的值要跟base class的method相同 但是,若該method是return class,derived class override的method可以return成別的class(要是derived class)
- 除非return type是一個class,不然override的metod不能改變return type
- derived class也算是base class但是多了一些限制而已,嚴格來說並沒有改變return type
- 可以原本base class的method是private,但是derived class是public,條件可以變寬鬆,但不能變嚴格(不能base class是public,derived class是private)
public class BaseClass{
public Employee get(int d){...}
}
public class derivedclass extends Baseclass{
public HourlyEmployee get(int d){...}
}
overload & override
在兩個method分別屬於繼承的class時 差別在於parameter一不依樣,parameter相同時就是override,parameter不同就是overload overload的話兩個method名字雖然依樣,但變成base class的method跟derived class的method都可以被derived class呼叫,沒把base class的蓋過去,因為overload,參數不同(signature不同)
class A{
public void setName(String a){...}
}
class B extends A{
public void setName(String a, String b){...}
}
B.setName(a,b) 兩個不同method,overload,沒把base class method蓋掉
B.setName(a)
Reference
Super
base class = 父類別(非ancestor) 在derived class construct的過程,會先呼叫base class的constructor才呼叫derived class的constructor,base class constructor幫忙initialize inherited的變數 因此假設在呼叫derived class constructor時,會呼叫base class的default constructor
如果希望是呼叫非default constructor derived class在constructor地方要呼叫super並放自己想要的參數,可能也是要放在第一行
public class B extends A{
B{
super("jimmy","chang");
...
}
}
- super指令一定要是derived class constructor的第一行
- super參數不接受instance variable(可以想成base class都還沒建立,哪來的instance variable,先有object才有instance vairable)
- 沒呼叫super的話 derived class constructor在呼叫前會先呼叫base class的default constructor
- super不能呼叫到一層以上的ancestor,頂多只能呼叫到父類別 super也可以當成是base class的object呼叫base class的method,可以用這方式call被derived class override的method
public class B extends A{
public int test(){
super.getInt(); // class A的method getInt,也就是說可以用這個方法來call
} // 被class B override的method
}
this constructor
在class內可以用this來呼叫自己class的constructor 跟super想法類似,但super是呼叫base class constructor,目標是方便,如果一個class有多個constructor,可以用這個來減少要寫的行數,等於利用其他寫好的constructor再加一些指令來當作新的constructor this是呼叫自己class constructor
- 限制跟super依樣,要放在constructor的第一行、不能有instance variable當參數
public Class A{
public A(){
this("jimmy",new Date(1129)); // this後面加constructor參數
} // constructor參數是看class A的定義
}
Create own library based on existed library
可以用繼承的方式繼承舊有library的功能在加上新功能,以StringTokenizer為例
import java.util.StringTokenizer
public class EnhancedStringTokenizer extends StringTokenizer{...}
見課本p.482 - 484
protected
- 宣告protected的method只能被自己和繼承的class呼叫(外人不能呼叫)
- protected的method,data還能被同個package底下的class呼叫(即使沒繼承關係) 符合上述兩種的class能直接呼叫或存取protected variable
Package access (Default access)
暱稱 default access, friendly access
一般宣告變數、method時, 我們會在前面放public/private/protected
但沒放的話會java會給他一個default的access type, 就是Default access,
這個type比protected稍微嚴格一些, 只有在同個package內的class能access這個變數或Method, 在此package外面的class(就算是derived class)也不能access
default package:同個資料夾下的其他class
- package access在同個package底下的class能隨意存取變數、method
- 在不同package下,就算是繼承關係的子class,class就不能存取該變數
重要
protected的變數derived class能直接修改存取,但是如果B,D兩class屬於不同package且在子class內new父class,不能直接存取父class的該protcted variable,因為算是不同物件了。
public B{protected int a}
public D extends B{
a = 5 -> ok
B b = new B();
b.a = 5; -> not ok,此a為新物件的a了,非D物件的a,但如果B,D同個package舊型
} -> 因為protect說同個package內的class能隨意存取protect變數
public C{
D test = new D();
test.a = 5 -> OK
}
object class
- 所有的class(包括self-designed)都源自於object class這個class
- object class在java.lang裡,java.lang每次都會自動import
- object class有一些所有class都繼承的method: toString, equal, clone,但對於self-designed的class可能會不正確,所以要override
equals
如上所述equals method要override,原本Object class的equal長這樣
public boolean equals(Object otherobject)
我們把它改成
- 先檢查傳進來的Object是不是跟現在這個class同個class type(不然不能equal比較),用object class define的final method getclass(不能override,有final)
public boolean equal(Object otherobj){
if(otherobj == null) return false;
// this.getClass,繼承自Object class
if(otherobj.getClass() != this.getClass()) return false;
A obj = (A)other obj; //將parameter Object type cast成現在這個class
... 做自己想要的比較
}
equals用equals(Object obj),參數用Object來overwrite,如果改成equals(A a)這種以class為參數會容易有問題,因為變overload,比較會得到自己不想要的結果(假設傳進來的是object參數,那他就會呼叫原本的equal而不是derived class的equal)
instanceof & getclass
getclass是Object這個大base class的final method,不能修改,會判斷這個object new時是呼叫哪個class,所以比較嚴謹,descendent之類的也會錯。
Object instanceof class
檢查object是不是class type(object可以是class的descendant但不能是ancestor)
因此對於繼承的類別判定, A instanceof B會對但B instanceof A會錯
getclass則是看這個class是從哪裡new出來的(?),所以繼承類別雙向的class判定都會隊,但instanceof只會對單向