誰能解釋一下以下代碼的輸出,這里涉及的Java原理是什么?
class Mammal {
void eat(Mammal m) {
System.out.println("Mammal eats food");
}
}
class Cattle extends Mammal{
void eat(Cattle c){
System.out.println("Cattle eats hay");
}
}
class Horse extends Cattle {
void eat(Horse h) {
System.out.println("Horse eats hay");
}
}
public class Test {
public static void main(String[] args) {
Mammal h = new Horse();
Cattle c = new Horse();
c.eat(h);
}
}
它產生以下輸出:
Mammal eats food
我想知道我們是如何得出上述結果的。
uj5u.com熱心網友回復:
多載與覆寫
這不是一個有效的方法覆寫,因為所有方法簽名(方法名稱 引數)都是不同的:
void eat(Mammal m)
void eat(Cattle c)
void eat(Horse h)
這稱為方法多載(請參閱),并且類Horse將具有3不同的方法,而不是一個。即它自己的多載版本eat()和2繼承版本。
編譯器會將方法呼叫映射c.eat(h)到最具體的方法,即eat(Mammal m),因為變數h的型別是Mammal。
為了呼叫帶有簽名的方法,eat(Horse h)您需要強制h轉換為 type Horse。請注意,這種轉換將被視為所謂的窄化轉換,它永遠不會自動發生,因為無法保證這種型別轉換會成功,因此編譯器不會為您執行此操作。
注釋掉這個方法void eat(Mammal m),你會看到編譯錯誤——編譯器不執行縮小轉換,它只能幫助你擴大轉換,因為它們保證成功,因此是安全的。
如果您手動進行型別轉換會發生什么:
強制h輸入型別Horse:
c.eat((Horse) h);
輸出:
Cattle eats hay // because `c` is of type `Cattle` method `eat(Cattle c)` gets invoked
因為變數c是型別Cattle,它只知道方法eat(Cattle c)而不是eat(Horse h)。而在幕后,編譯器會將型別擴大到.hCattle
強制c輸入h型別Horse:
((Horse) c).eat((Horse) h);
輸出:
Horse eats hay // now `eat(Horse h)` is the most specific method
覆寫規則
方法覆寫的規則符合Liskov 替換原則。
使用指向基類的指標或參考的函式必須能夠在不知情的情況下使用派生類的物件。
子類應該以這樣的方式宣告它的行為,以便它可以在任何需要它的父類的地方使用:
方法簽名必須完全匹配。即方法名稱應該與引數的型別相同。并且引數應該以相同的順序宣告。
重寫方法的訪問修飾符可以相同或更寬,但不能更嚴格。即
protected父類中的方法可以被覆寫,public也可以保留protected,我們做不到private。在原始型別的情況下,被覆寫方法的回傳型別應該完全相同。但是如果父方法宣告回傳參考型別,則可以回傳其子型別。即如果父回傳
Number一個被覆寫的方法可以Integer作為回傳型別提供。如果父方法宣告拋出任何已檢查的例外,則允許被覆寫的方法宣告相同的例外或其子型別,可以實作為安全的(或根本不拋出例外)。不允許使被覆寫的方法的安全性低于父方法宣告的方法,即拋出父方法未宣告的已檢查例外。請注意,對于運行時例外(未選中)沒有任何限制,即使父方法未指定被覆寫的方法,也可以自由宣告它們。
這將是方法覆寫的有效示例:
static class Mammal{
void eat(Mammal m){
System.out.println("Mammal eats food");
}
}
public class Cattle extends Mammal{
@Override
void eat(Mammal c) {
System.out.println("Cattle eats hay");
}
}
public class Horse extends Cattle{
@Override
public void eat(Mammal h) throws RuntimeException {
System.out.println("Horse eats hay");
}
}
main()
public static void main(String[] args) {
Mammal h = new Horse();
Cattle c = new Horse();
c.eat(h);
}
輸出:
Horse eats hay
uj5u.com熱心網友回復:
在您的示例中,發生方法多載(方法名稱相同但傳遞的引數型別不同)。
當您呼叫c.eat(h)時,編譯器會知道您要使用該void eat(Mammal m)方法,因為您的h參考具有型別Mammal。
如果您將物件參考更改為Horse或Cattle類似:
Horse h = new Horse();
輸出將是:
Cattle eats hay
發生這種情況是因為編譯器將使用最具體的方法,在這種情況下void eat(Cattle c),基于物件參考型別Horse。
您可能還對使用運行時多型性的方法覆寫感興趣。
轉載請註明出處,本文鏈接:https://www.uj5u.com/qiye/486318.html
