iT邦幫忙

0

java 中的多型問題 A a = new B() ;

(1) Animal a =new Dog();
(2) Dog b =new Dog();

關於多型,想請問當Dog是Animal的子類別,那麼(1)跟(2)的差別在哪裡?

當要使用Dog的叫聲方法時,a跟b都會使用子類別Dog改寫的方法,
那麼都寫成(2)不就好了嗎,想請問特別寫成(1)的意義在哪裡?
(寫成(1)的意義是否為不想給a使用子類別Dog有而父類別Animal沒有的方法? 謝謝

tw_hsu iT邦新手 4 級 ‧ 2022-01-20 10:52:58 檢舉
雖然DesignPattern是後來的概念,但多型這東西出現時,設計者就已經有這種概念了,只是不具體。

你會覺得這是個「意義不明」的功能
因為你執著於「new」這個動作
但其實在Java中,要產生一個物件的實體,並不是只有「new」這個方法而已
(賣個關子,讓你自己研究。)

想像一下:只要有這個物件的class名稱(字串),就可以隨意決定Animal a最後實際上會是什麼樣的class,而這個class名稱不一定來自本機端,可能是來自網路傳輸。遠端的操作者可以彈性的要求電腦將Animal a設定為Dog/Cat/Rat...更極端一點,這個class檔案不需要存在本機端,遠端操作者可以「傳輸」檔案過來然後要求本機端接受後執行。

但先不要講那麼天馬行空的事情,講務實一點保守一點
「Animal a = ...」
這行程式碼/這個物件X寫下時,可能Animal子類別只有Dog/Cat,但後來開始逐步增加Rat/Pig/Horse....
可是根本不需要在乎這些,因為它的設計只在乎「新的物件有沒有Animal屬性」
所以如果要擴充Animal a = ...後面的「可能性」,難道一定要修改這個物件X嗎?
DesignPattern中有一種Pattern是建議大家使用這樣的概念...

int sign = .... (網路訊號?文件檔?都可能)
AnimalProvider p = new AnimalProvider();
Animal a = p.getAnimal(sign);

這樣一來,上面的問題就可以專責由AnimalProvider解決,而且不需要是同一個工程師(甚至不需要是同一段時間、同一個團隊)。
kawa0710 iT邦新手 2 級 ‧ 2022-01-21 09:18:15 檢舉
換一個角度從【繼承】看,先寫了Animal類別「規範」子類別的程式寫法及「減少」子類別的程式碼數量。早期開發的程式碼都是用Animal類別,後面開發的要用到Dog類別但又要丟給早期開發的程式碼使用時,Dog就往上轉型成Animal。
所以Dog的程式碼不是不給用,而是早期開發的程式碼只用Animal類別,故不需要用到Dog類別新增的方法和屬性
良葛格 iT邦新手 2 級 ‧ 2022-01-24 09:12:11 檢舉
(...這邊的討論無法使用 markdown,回覆於下方的「回答」…)
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中
0
aaaron
iT邦新手 5 級 ‧ 2022-01-19 03:03:20

多型可以增加程式的彈性,日後維護性也比較高。

舉個例子,如果貓狗都會一個動作叫做show,而show裡面是先後執行走、跑、跳的行為,所以參數可以利用父類別型態來接收不同子類別的物件,來達到多型的目的,未來如果show這個動作想要修改,只需要更改一個地方,不需要改每個動物的show,所以有高內聚低耦合的特性,日後的可維護性高。

calss A{
 public void show(Animal a){
     a.walk();
     a.run();
     a.jump();
 }
}

主程式:
A a1 = new A();
Dog d = new Dog();
Cat c = new Cat();
a1.show(d);
a1.show(c);
tw_hsu iT邦新手 4 級 ‧ 2022-01-20 10:52:07 檢舉

雖然DesignPattern是後來的概念,但多型這東西出現時,設計者就已經有這種概念了,只是不具體。

你會覺得這是個「意義不明」的功能
因為你執著於「new」這個動作
但其實在Java中,要產生一個物件的實體,並不是只有「new」這個方法而已
(賣個關子,讓你自己研究。)

想像一下:只要有這個物件的class名稱(字串),就可以隨意決定Animal a最後實際上會是什麼樣的class,而這個class名稱不一定來自本機端,可能是來自網路傳輸。遠端的操作者可以彈性的要求電腦將Animal a設定為Dog/Cat/Rat...更極端一點,這個class檔案不需要存在本機端,遠端操作者可以「傳輸」檔案過來然後要求本機端接受後執行。

但先不要講那麼天馬行空的事情,講務實一點保守一點
「Animal a = ...」
這行程式碼/這個物件X寫下時,可能Animal子類別只有Dog/Cat,但後來開始逐步增加Rat/Pig/Horse....
可是根本不需要在乎這些,因為它的設計只在乎「新的物件有沒有Animal屬性」
所以如果要擴充Animal a = ...後面的「可能性」,難道一定要修改這個物件X嗎?
DesignPattern中有一種Pattern是建議大家使用這樣的概念...

int sign = .... (網路訊號?文件檔?都可能)
AnimalProvider p = new AnimalProvider();
Animal a = p.getAnimal(sign);

這樣一來,上面的問題就可以專責由AnimalProvider解決,而且不需要是同一個工程師(甚至不需要是同一段時間、同一個團隊)。

2
Samuel
iT邦好手 1 級 ‧ 2022-01-19 09:15:18

試著假想今天寫個類別叫做Hr的 Class裡面定義一個招聘專精日文的method
然後要傳入一種資料型別叫做applicant

如果今天你設計的方式是分成
東吳大學日文的硬聘者就一個東吳applicant
輔仁大學日文的硬聘者就一個輔仁applicant
.....
XX學校日文的硬聘者就一個XX applicant
此時你Hr Class中定義的招聘專精日文的method
會依賴過於具體的硬聘者種類的Class資料型別

這時將應聘日文專業applicant獨立抽離出來成抽象父類或者interface都可以
主要在當中定義抽象的可能考取某些日文相關檢定證照之類的

Hr的 Class裡面定義一個招聘專精日文的method
也就不用重寫只是因為參數不同的一堆重複方法
傳入那個抽象概念的資料型別即可
因為你只在乎求職者有考到某些檢定也不care他怎麼考取到的
你只care資格有到

多形可以用來讓物件導向設計程式達到
Liskov Substitution Prnciple(LSP)
通常子類是可以去替換掉其父親類 而不影響架構的。
只要是父類(Base Class)出現的地方子類(Sub class)就可以出現,並且還能替換成子類
也不會有任何錯誤異常發生。
通常會需要用這技巧是在於使用者(接手開發的人)根本不需要知道
是父類還是子類,反之則不可。

Dependency Inversion Principle(DIP)
高層模組不該依賴低層模組(Class....)。
-->換言之,外部在調用的區塊(可能是你的main或多個程式檔案、其他的方法、函數當中)
不該頻繁修改。

兩者(高低層模組)皆應該依賴其抽象,抽象不該依賴細節,細節應該依賴抽象。
在白話一點說,就是實例化的Class之間不發生依賴關係,其依賴關係
透過Interface或Abstract來產生。

Ref:
https://coolmandiary.blogspot.com/2017/05/day01solid.html

https://coolmandiary.blogspot.com/2018/04/java.html

2
石頭
iT邦高手 1 級 ‧ 2022-01-19 13:03:29

簡單來說 多型是為了多態

寫法二不是不行只是比起寫法一少了彈性

多態在大型 Open Source 專案都可以看到,因為這讓專案日後更有機會擴充

建議你可以去看看物件導向設計原則 SOLID

這是我之前使用多態來優化程式碼的回答給你參考 C# 優化使用過多的IF Else代碼

在原本很複雜的邏輯,可以運用多態 + 合理封裝讓可讀性跟維護性大大提升

0
良葛格
iT邦新手 2 級 ‧ 2022-01-24 09:17:15

如果你的 doIt 做的事一模一樣,那麼以下的寫法只要一個 doIt

	class Animal {
		void doAnimal() {
		}
	}

	class Dog extends Animal {
		void doDog() {
		}
	}

	class Cat extends Animal {
		void doCat() {
		}
	}

	public class Main {
		public static void doIt(Animal animal) {
			animal.doAnimal();
			// 其他程式碼
		} 
		
		public static void main(String[] args) {
			doIt(new Dog());
			doIt(new Cat());
		}
	}

以下的寫法會導致程式流程重複:

	class Animal {
		void doAnimal() {
		}
	}

	class Dog extends Animal {
		void doDog() {
		}
	}

	class Cat extends Animal {
		void doCat() {
		}
	}

	public class Main {
		public static void doIt(Dog dog) {
			dog.doAnimal();
			// 其他程式碼
		} 
		
		public static void doIt(Cat cat) {
			cat.doAnimal();
			// 其他程式碼
		} 

		public static void main(String[] args) {
			doIt(new Dog());
			doIt(new Cat());
		}
	}

我要發表回答

立即登入回答