假設我們編寫這樣一個方法:
public void runTwice(Person p) {
p.run();
p.run();
}
它傳入的參數類型是Person
,我們是無法知道傳入的參數實際類型究竟是Person
,還是Student
,還是Person
的其他子類,因此,也無法確定調用的是不是Person
類定義的run()
方法。
所以,多態的特性就是,運行期才能動態決定調用的子類方法。對某個類型調用某個方法,執行的實際方法可能是某個子類的覆寫方法。這種不確定性的方法調用,究竟有什麼作用?
我們還是來舉栗子。
假設我們定義一種收入,需要給它報稅,那麼先定義一個Income
類:
class Income {
protected double income;
public double getTax() {
return income * 0.1; // 稅率10%
}
}
對於工資收入,可以減去一個基數,那麼我們可以從Income
派生出SalaryIncome
,並覆寫getTax()
:
class Salary extends Income {
@Override
public double getTax() {
if (income <= 5000) {
return 0;
}
return (income - 5000) * 0.2;
}
}
如果你享受國務院特殊津貼,那麼按照規定,可以全部免稅:
class StateCouncilSpecialAllowance extends Income {
@Override
public double getTax() {
return 0;
}
}
現在,我們要編寫一個報稅的財務軟件,對於一個人的所有收入進行報稅,可以這麼寫:
public double totalTax(Income... incomes) {
double total = 0;
for (Income income: incomes) {
total = total + income.getTax();
}
return total;
}
來試一下:
public class Main {
public static void main(String[] args) {
// 給一個有普通收入、工資收入和享受國務院特殊津貼的小夥伴算稅:
Income[] incomes = new Income[] {
new Income(3000),
new Salary(7500),
new StateCouncilSpecialAllowance(15000)
};
System.out.println(totalTax(incomes));
}
public static double totalTax(Income... incomes) {
double total = 0;
for (Income income: incomes) {
total = total + income.getTax();
}
return total;
}
}
class Income {
protected double income;
public Income(double income) {
this.income = income;
}
public double getTax() {
return income * 0.1; // 稅率10%
}
}
class Salary extends Income {
public Salary(double income) {
super(income);
}
@Override
public double getTax() {
if (income <= 5000) {
return 0;
}
return (income - 5000) * 0.2;
}
}
class StateCouncilSpecialAllowance extends Income {
public StateCouncilSpecialAllowance(double income) {
super(income);
}
@Override
public double getTax() {
return 0;
}
}
觀察totalTax()
方法:利用多態,totalTax()
方法只需要和Income
打交道,它完全不需要知道Salary
和StateCouncilSpecialAllowance
的存在,就可以正確計算出總的稅。
如果我們要新增一種稿費收入,只需要從Income
派生,然後正確覆寫getTax()
方法就可以。把新的類型傳入totalTax()
,不需要修改任何代碼。
可見,多態具有一個非常強大的功能,就是允許添加更多類型的子類實現功能擴展,卻不需要修改基於父類的代碼。