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

override & overload

overload

override

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

  1. 宣告protected的method只能被自己和繼承的class呼叫(外人不能呼叫)
  2. 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

  1. package access在同個package底下的class能隨意存取變數、method
  2. 在不同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)

我們把它改成

  1. 先檢查傳進來的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只會對單向