*Overloading
method 的執行規則
- 基礎對應(找相同或是相容)
- 如果找不到相同的型別, 就往上找相同的型別
- byte → char / short → int → long → float → double
- Autoboxing / AutoUnboxing
- 如果找不到基礎對應, 就會嘗試Autoboxing 的方式
- Autoboxing 是一對一的對應, 也就是 int 只會找 Integer 不會找其他
- Varargs
TestOverloading.java
public
class TestOverloading {
//透過註解不同的方法來觀察
overloading
配合Test.java
//註解觀察
順序 首先觀察基礎對應 int
→ long → float → double
//再來觀察
Autoboxing
註解 Integer
//觀察
Varargs
註解 int
or Integer
public
void oops(byte a){
System.out.println("oops(byte
a)");
}
public
void oops(short a){
System.out.println("oops(short
a)");
}
public
void oops(int a){
System.out.println("oops(int
a)");
}
public
void oops(long a){
System.out.println("oops(long
a)");
}
public
void oops(float a){
System.out.println("oops(float
a)");
}
public
void oops(double a){
System.out.println("oops(double
a)");
}
public
void oops(Integer a){
System.out.println("oops(Integer
a)");
}
public
void oops(Long a){
System.out.println("oops(Long
a)");
}
public
void oops(int... a){
System.out.println("oops(int...
a)");
}
public
void oops(Integer... a){
System.out.println("oops(Integer...
a)");
}
}
Test.java
public
class Test {
public
static void main(String[] args) {
//
short
a = 25;
char
b = 'a';
//char
與 short
雖然位元數一樣,
但是還是要 Casting
(互轉型別)
//
a = (short) b;
//
b = (char) a;
TestOverloading
to = new TestOverloading();
//搭配上面的TestOverloading
來了解順序
to.oops(100);
//如果傳的是
大寫的Integer,
如果找不到,
會找小寫的int
並找向上相容
→ 再找 Varargs
( 但是int
或 Integer
只能有一個)
to.oops(new
Integer(100));
//
如果傳入的是
char
在Overloading
的時候會往上相容,
所以會呼叫 int
to.oops(b);
}
}
Notes:
- char 與 short 雖然位元數一樣, 但是還是要 casting (轉型), 但是在Overloading 的時候會往上相容, 所以會呼叫 int
*Chapter
10 多型
Notes:
- 現實生活中利用多型來簡化生活用語, 例如用"東西"來代表所有的事物
- 多型是為了要有高度的擴充性以及讓程式精簡
- 物件導向語言藉由繼承與多型來改善傳統語言的缺點
*多型(Polymorphism)
- 擁有不同型態的能力
- 事實上物件只有一個型態
- Java 藉由參考變數參考不同型態的物件來達成多型的目的, 其中參考變數的型別須為物件型別的父類別或是更上層的型別
- Complier 會將參考的物件是為參考變數所宣告的型別來使用
Notes:
- 宣告一個父類別的物件, 但是指向子類別, 稱之為多型
- 例如: Manager 為 Employee 的子類別
- Employee d = new Manager(); //多型的宣告
- //Compiler 會將參考的型別視為被宣告的型別來使用
*虛擬方法呼叫(Virtual
Method Invocation)
- 如果使用多型的狀況, 如果子類別有override 方法, 就會產生虛擬方法呼叫VMI, 也就是會呼叫子類別的方法
-
- Compile-time 型別, 編譯時期方法名稱必須是宣告變數型別的成員, 也就是父類別要有相對的方法
- runtime 型別: 在執行時期物件的型別, 所以會採用子類別的實作, 稱為動態繫結(Dynamic Binding)
Lab:
多型
Animal.java
public
class Animal {
//建立一個父類別來實現多型,
定義 speak
and walk
//如果子類別沒有
override
方法就會出現預設的方法
public
void speak(){
System.out.println("speak");
}
//
public
void walk(){
System.out.append("Walking");
}
}
AnimalUN.java
public
class AnimalUN {
public
static void main(String[] args) {
//用來測試多型
Animal
d = new Dog(); //多型,
建立一個父物件類別但是指向子物件
d.speak();
//如果子類別沒有
override
方法,
就會出現 speak,
如果有就會出現
dog
speak
//
Animal
c = new Cat();
c.speak();
}
}
Dog.java
public
class Dog extends Animal {
public
void speak(){
System.out.println("Dog
speak");
}
//
public
void walk(){
System.out.append("Dog
walking");
}
}
Cat.java
package
mod10;
public
class Cat extends Animal {
//使用override
方式來執行子類別的方法
public
void speak(){
System.out.println("Cat
speak");
}
//
public
void walk(){
System.out.append("Cat
Walking");
}
}
*異質集合
- 帶有不同類別型別的物件集合稱為異質性集合(heterogeneous collections)
*多型參數的傳遞
- 宣告一個接受父類別型別參數的方法, 然後傳遞子類別的物件給該方法
Lab:
多型參數
Human.java
package
mod10;
public
class Human extends Animal{
//
public
void speak(){
System.out.println("Human
speak");
}
//
public
void walk(){
System.out.append("Human
Walking");
}
}
修改剛剛的
AnimalUN.java
AnimalUN.java
public
class AnimalUN {
public
static void main(String[] args) {
//用來測試多型
Animal
d = new Dog(); //多型,
建立一個父物件類別但是指向子物件
d.speak();
//如果子類別沒有
override
方法,
就會出現 speak,
如果有就會出現
dog
speak
System.out.println("--------------------");
//
Animal
c = new Cat();
c.speak();
System.out.println("--------------------");
AnimalUN
un = new AnimalUN();
//多型物件的傳遞,
c 為 Cat
類別,
為Animal
的子類別,
但speech
方法內為Animal
(父類別)
un.speech(c);
//多型物件的傳遞
un.chat(c,
d);
//
Human
h = new Human();
un.speech(h);
}
//這邊使用的是
Animal
public
void speech(Animal a){
a.speak();
}
//這邊使用的是
Animal
public
void chat(Animal a, Animal b){
a.speak();
b.speak();
}
//
use VarArgs 來擴充
public
void mutiChat(Animal ... as){
}
}
*多型的目的
- 簡化程式的開發
- 高度的擴充性
*Overriding
三大規則
- 方法簽章要跟父類別被Overriden的方法一模一樣
- 存取控制不能比被Overriden的方法還小(比父類別還開放)
- Override 方法時不能使用更封閉的存取修飾子
- private 的方法不會被override
- 不能丟出Overriden方法未宣告丟出的Exception
*instanceof
運算子
- instanceof 運算子可以用來判斷物件的真實身份
*物件轉型
- 轉型的目的為了要恢復該物件的完整功能
- 使用instanceof 來測試一個物件的型別
- 藉由轉型(casting)來恢復物件的完整功能
- 根據下列的指導方針來檢查轉型的適當性
- 在類別階層中, 向上(upward)轉型(其實就是多型)(子類別轉成父類別)會自動發生
- 向下(downward)轉型(父類別轉成子類別)則需要符合類別階層的定義, 並經由compiler的檢查
- 物件的型別需要runtime時期透過instanceof 進行檢查, 否則會發生錯誤.(ClassCastException)
*instanceof
- 可以用來做相容型別的測試
- 藉由判別回傳 true 或是 false, 來判斷是否為相容型別
- Employee ← Manager ← Director
- Employee e = new Employee();
- Manager m = new Manager();
- Director d = new Director();
- ( e instanceof Employee ) → true
- ( e instanceof Manager ) → false
- ( e instanceof Director ) → false
- ( m instanceof Employee ) → true
- ( m instanceof Manager ) → true
- ( m instanceof Director ) → false
*抽象類別
- 一個方法如果沒有提供實作的方法, 該方法須宣告為抽象方法(abstract method)
- 一個類別如果包含一個以上的抽象方法 ,該類別需宣告為抽象類別(abstract class)
- 有別於具體類別(concrete class)(實作完整的類別), 抽象類別不能夠建立實體物件, 但仍可當作變數型別的宣告
Lab:
抽象類別
修改剛剛的Animal.java
//如果方法內有一個抽象方法,
就必須宣告為抽象類別
public
abstract class Animal {
//建立一個父類別來實現多型,
定義 speak
and walk
//如果子類別沒有
override
方法就會出現預設的方法
//將
speak方法改為抽象,
注意要連同{
} 一起移除(抽象方法內不可以有實作,
也就是敘述 {}
), 並加上 ;
public
abstract void speak();
//
public
void walk(){
System.out.append("Walking");
}
}
Notes:
- 為何要使用抽象類別, 因為要提供多型還有VMI
- 在 UML 圖形內, 斜體字代表抽象
- 使用抽象類別,如果子類別沒有辦法提供實作, 那子類別就要宣告為抽象類別
*介面(Interface)
- Interface 等同於class
- Interface 內都是抽象類別, 沒有定義任何的實作.
- Interface 可以當成一種型別
- Interface 不會受到單一繼承的限制
- 一個公開介面(public interface)是介於用戶端程式(client code)和實作這個介面的類別之前的契約.
- Java interface 是這種契約的正式宣告, 其中所有的方法都沒有提供實作
- interface 所有的方法必定是 public abstract, 所有的屬性必定是 public static final
- 不相關的類別可以實作相同的interface
- 一個類別可以實作多個不相關的interface
Lab:
Interface
將上面的程式改成
interface
Animal.java
//如果方法內有一個抽象方法,
就必須宣告為抽象類別
//interface
將 void
class 改成 interface
//一旦宣告為
interface,
那裏面的方法都是抽象的方法
public
interface Animal {
//建立一個父類別來實現多型,
定義 speak
and walk
//如果子類別沒有
override
方法就會出現預設的方法
//將
speak方法改為抽象,
注意要連同{
} 一起移除(抽象方法內不可以有實作,
也就是敘述 {}
), 並加上 ;
//如果是
interface
的話,
那 abstract
關鍵字就可以省略
public
void speak();
//
public
void walk();
}
Dog.java
public
class Dog implements Animal {
//使用override
方式來執行子類別的方法
public
void speak(){
System.out.println("Dog
speak");
}
//
public
void walk(){
System.out.append("Dog
walking");
}
}
Cat.java
//
如果Animal
是 interface
那就要使用 implements
關鍵字,
非 extends
關鍵字
public
abstract class Cat implements Animal {
//
public
void speak(){
System.out.println("Cat
speak");
}
//這邊
Cat
沒有實作 walk(),
等等就會要用 Exception,
因為interface
子類別都要實作
}
AnimalUN.java
public
class AnimalUN {
public
static void main(String[] args) {
//用來測試多型
Animal
d = new Dog(); //多型,
建立一個父物件類別但是指向子物件
d.speak();
//如果子類別沒有
override
方法,
就會出現 speak,
如果有就會出現
dog
speak
System.out.println("--------------------");
//因為
Cat
的 walk
方法沒有實作,
所以要有 throw
Excption
Animal
c = new Cat() {
@Override
public
void walk() {
throw
new UnsupportedOperationException("Not supported yet.");
}
};
c.speak();
System.out.println("--------------------");
AnimalUN
un = new AnimalUN();
//多型物件的傳遞,
c 為 Cat
類別,
為Animal
的子類別,
但speech
方法內為Animal
(父類別)
un.speech(c);
//多型物件的傳遞
un.chat(c,
d);
//
Human
h = new Human();
un.speech(h);
}
//這邊使用的是
Animal
public
void speech(Animal a){
a.speak();
}
//這邊使用的是
Animal
public
void chat(Animal a, Animal b){
a.speak();
b.speak();
}
//
use VarArgs 來擴充
public
void mutiChat(Animal ... as){
}
}
*介面的使用
- 用來規範一個或多個類別應實作的方法
- 訂定物件的程式介面而不需要揭露類別本體的實作
- 在完全不相干的類別之間尋求相似性, 而不需要勉強建立類別間的繼承關係
- 藉由一個實作多重介面的類別可以模擬多重繼承的效果
沒有留言:
張貼留言