為何要繼承
底下程式碼中, 宣告了一個Pikachu的類別, 裏面有等級(level), 身高(h), 体重(w) 三個物件變數. 試想, 如果你是任天堂這間公司裏的程式設計師, 老闆叫你要創造出一千多種的神奇寶貝, 而每種神奇寶貝都同樣會有這三個物件變數. 那麼每創造一種神奇寶貝後, 這三個變數不就都要重複寫一次嗎。
class Pikachu { int leve; double w, h; } class Eve { int leve; double w, h; }
所以是不是有什麼方法, 可以不用重複寫個上千次呢? 此時繼承就可以解決這個問題了. 如下程式碼
class Pokemon { private int leve; public double w, h; } class Pikachu : Pokemon { } class Eve:Pokemon { }
上述的Pokemon宣告了所有類別共通的變數, 然後使用 “:” 繼承給Pikachu 及 Eve, 而在Pikachu及Eve類別中什麼都不用要寫, 就會有level, w, h三個變數了。
繼承後的權限
子類別繼承後, 父類別的public屬性及方法全都會過繼到子類別, 因此在 Main(0中可以直接使用 p1.w.
而private變數呢, 一般人是說沒有繼承給子類別. 但這種說法好像是錯的. 實際上好像是有繼承給子類別, 只是在子類別中無法使用. 這就好比父親給你一個金庫, 但他卻忘了把鑰匙留給你, 所以這個金庫就無法打開.
因為父類別若有private變數, 而此變數需要在子類別中存取的話, 那在父類別就一定要把鑰匙交待下去, 也就是在父類別中要有 public 方法來進行存取.
static void Main(string[] args)
{
Pikachu p1=new Pikachu();
p1.w = 10.3f;
p1.level = 10; //Error
}
物件變數覆蓋
為什麼上面說private變數好像有繼承給子類別呢. 假設在子類別有一個變數, 其名稱與父類別的變數名稱一模一樣, 那子類別的變數就會蓋掉父類別的變數.
在下面的代碼中, p.level=100, 則是使用子類別自己的變數,
而p.setLevel(5), 卻是改到了父類別原先的level. 因為推估, private 變數一樣會下降到子類別中, 在子類別保存了一份來自父類別的資訊.
static void Main(string[] args)
{
Pikachu p = new Pikachu();
p.setLevel(6);
p.level = 100;
Console.WriteLine("p.getLevel()={0}",p.getLevel(), p.level);
Console.WriteLine("p.level={0}", p.level);
}
class Pokemon
{
private int level;//物件變數
public double w, h;//物件變數
public static int count;//類別變數
public void setLevel()
{
this.level = 1;
}
public void setLevel(int level)
{
if (level > 10) this.level = level;
else this.level = level ;
}
public int getLevel()
{
return level;
}
}
class Pikachu : Pokemon
{
public int level;
}
方法覆蓋
當子類別的方法與父類別的方法同樣名稱時, 此子類別的方法會覆蓋掉父類別的方法, 然後父類別方法會屍骨無存.
在下面代碼中, 父類別的 level為 private, 所以只有父類別的 setLevel()方法才可以存取. 但子類別又把父親留下來的鑰題給蓋了上去, 且在子類別又無法存取 level 變數, 那不就死定了嗎.
此時只好委託父類別的方法設定. 但父類別不是屍骨無存了嗎. 那只好採用觀落音法 base.setLevel();
class Pokemon { private int level;//物件變數 public double w, h;//物件變數 public static int count;//類別變數 public void setLevel(int level) { if (level > 10) this.level = level; else this.level = level ; } public int getLevel() { return level; } } class Pikachu : Pokemon { public void setLevel(int level) { base.setLevel(level * 10); } }
建構子覆蓋
建構子其實沒有覆蓋的問題, 因為子類別的建構子名稱一定跟父類別不一樣.
但請看如下代碼, 在Main()中new 出一隻Pikachu()後,會印出Pokemon出生, 然後才會印出皮卡丘出來.
生出皮卡丘跟Pokemon有啥關係呢?, 原來編譯器會在Pikachu()建構子後面自動加上 :base(), 如下
public Pikachu():base(), 所以會先執行父類別建構子, 再執行子類別建構子
那麼, Pokemon類別因為沒有父類別, 那他的建構子會不會也自動加base()呢? 答案是~~會的. 那加上之後, 是要執行什麼東西啊?
原來 class Pokemon雖沒有繼承任何父類別, 那麼編譯器就會自動加為如下
class Pokemon : Object
也就是說, 所有類別, 都是繼承Object而來的. Object是所有類別的父類別, 只有Object沒有父類別
class Pokemon { public Pokemon() { Console.WriteLine("Pokemon出生"); } } class Pikachu : Pokemon { public Pikachu() { Console.WriteLine("皮卡丘出生"); } }