java對象類型轉換和多態(tài)性(實例講解)
對象類型轉換
分為向上轉型和向下轉型(強制對象轉型)。 向上轉型是子對象向父對象轉型的過程,例如貓類轉換為動物類;向下轉型是強制轉型實現的,是父對象強制轉換為子對象。 這和基礎數據類型的轉換是類似的,byte在需要時會自動轉換為int(向上轉型),int可以強制轉型為byte(向下轉型)。
對于對象轉型來說, 向上轉型后子對象獨有的成員將不可訪問 。 意思是,在需要一只動物時,可以把貓當作一只動物傳遞,因為貓繼承自動物,貓具有動物的所有屬性。但向上轉型后,貓不再是貓,而是被當作動物看待,它自己獨有的屬性和方法就不可見了。換句話說,向上轉型后,只能識別父對象中的內容。
可以通過"引用變量 instanceof 類名"的方式來判斷引用變量 所指向的對象 是否屬于某個類,也就是說"對象是不是某類",例如聲明一個貓類對象的引用"Cat c",然后"c instanceof Animal"表述的意思是"對象c是一種動物嗎?"對于instanceof返回true的對象,都可以轉換為類對象,只不過有些可能需要強制轉換。
向上轉型可以自動進行,這本就是符合邏輯的,狗類繼承自動物類,它本身就是一只動物,因此在需要動物類的時候,丟一只狗過去就會自動向上轉型成動物類。但這時狗已經不是狗,而是動物,所以狗獨有的成員不再可見。
強制轉換的方式和基礎數據類型強制轉換一樣,都是在待轉換對象前加上目標類型,例如將動物a強制轉換為狗d: Dog d = (Dog)a 。
下面是一個對象類型轉換的示例,很好地分析了能否轉型、轉型后能否訪問某些成員等等。
class Animal { String name; Animal(String name) {this.name = name;} } class Cat extends Animal { String eyecolor; Cat(String name,String color) {super(name); this.eyecolor = color;} } class Dog extends Animal { String furcolor; Dog(String name,String color) {super(name); this.furcolor = color;} } public class OCast { public static void main(String [] args) { Animal a = new Animal("animal"); Cat c = new Cat("cat","blue"); Dog d = new Dog("dog","black"); System.out.println( a instanceof Animal);//return true System.out.println( c instanceof Animal);//return true System.out.println( d instanceof Animal);//return true System.out.println( a instanceof Cat); //return false System.out.println(a.name); //return animal a = new Dog("yellowdog","yellow"); //object Dog upcast to Animal System.out.println(a.name); //return yellowdog System.out.println(a instanceof Animal); //return true System.out.println(a instanceof Dog); //return true //System.out.println(a.furcolor); //error! because a was regarded as Animal Dog d1 = (Dog)a; // because "a instanceof Dog" is true,so force cast Animal a to Dog System.out.println(d1.furcolor); //return yellow } }
對于上面的 a = new Dog("yellowdog",yellow) ,a是Animal類型,但此時 它指向的是Dog對象。也就是說它是Dog,所以也是Animal類 ,所以 a instanceof Animal); 和 a instanceof Dog; 都是true,這是它的"指針"決定的。 但因為它的類型是Animal類型,類型決定了能存儲什么樣的數據,對于已經存在的但不符合類型的數據都是不可見的,所以Animal類型決定了它只能看到Dog對象中的Animal部分 。
如下圖:
既然可以向上轉型,配合instanceof的邏輯判斷,就能實現很好的擴展性。例如,動物類的sing(Animal a)方法需要的是一個動物類,可以給它一只狗d,這時會向上轉型(就像需要double類型卻給了一個int數據一樣),雖然轉型了,但狗d的實際引用仍然是Dog對象,于是 if (a instanceof Dog) 判斷為真,則調用能體現狗sing()方法特殊性的語句。如果傳遞一只貓,if判斷一下并調用能體現貓sing()方法特殊性的語句。這樣,任何時候想添加一只動物,都只需要增加一條if語句就可以了。
見下面的示例:
class Animal { String name; Animal(String name) { this.name = name; } } class Cat extends Animal {Cat(String name) {super(name);}} class Dog extends Animal {Dog(String name) {super(name);}} public class TestCast { public static void main(String [] args) { TestCast t = new TestCast(); Animal a = new Animal("animal"); Animal c = new Cat("cat"); Animal d = new Dog("dog"); t.sing(a);t.sing(c);t.sing(d); } void sing(Animal a) { if ( a instanceof Cat) { Cat cat = (Cat)a; System.out.println("cat is singing"); } else if(a instanceof Dog) { Dog dog = (Dog)a; System.out.println("dog is singing"); } else { System.out.println("not an instance of animal"); } } }
如果沒有對象轉型,那么Dog里要定義一次sing(),Cat里也要定義一次sing()。要增加一個動物類,動物類里也還要定義一次sing()?,F在就方便多了,直接在sing()方法內部修改if語句就可以了。
注意,上面的sing()方法不屬于Animal或其他子類的方法,而是獨立定義在其他類里進行調用的。
多態(tài)
向上轉型雖然在一定程度上提高了可擴展性,但提高的程度并不太高。以向上轉型為基礎,java的多態(tài)實現的擴展性更好更方便。
多態(tài)也叫動態(tài)綁定或后期綁定,它是執(zhí)行期間進行的綁定,而非編譯期間的綁定(這是靜態(tài)綁定或稱為前期綁定)。
多態(tài)的原理是:當向上轉型后,調用一個被重寫的方法時,本該調用的是父類方法,但實際上卻會動態(tài)地調用子類重寫后的方法。實際上,編譯期間綁定的確實是父類方法,只不過在執(zhí)行期間動態(tài)轉調子類對應方法。
例如,Animal類的sing()方法,Cat和Dog類都重寫了sing()方法。當需要一個Animal對象時,傳遞了一個Cat類,那么將調用Cat的sing()方法。動態(tài)綁定的邏輯正如下面的代碼類似:
void sing(Animal a) { if ( a instanceof Cat) { Cat cat = (Cat)a; System.out.println("cat is singing"); } else if(a instanceof Dog) { Dog dog = (Dog)a; System.out.println("dog is singing"); } else { System.out.println("not an instance of animal"); } }
以下是一個多態(tài)的例子
class Animal { private String name; Animal(String name) {this.name = name;} public void sing(){System.out.println("animal sing...");} } class Cat extends Animal { private String eyeColor; Cat(String n,String c) {super(n); eyeColor = c;} public void sing() {System.out.println("cat sing...");} } class Dog extends Animal { private String furColor; Dog(String n,String c) {super(n); furColor = c;} public void sing() {System.out.println("dog sing...");} } class Lady { private String name; private Animal pet; Lady(String name,Animal pet) {this.name = name; this.pet = pet;} public void myPetSing(){pet.sing();} } public class DuoTai { public static void main(String args[]){ Cat c = new Cat("catname","blue"); Dog d = new Dog("dogname","black"); Lady l1 = new Lady("l1",c); Lady l2 = new Lady("l2",d); l1.myPetSing(); l2.myPetSing(); } }
編譯后的執(zhí)行結果為:
cat sing... dog sing...
在上面的示例中,Lady類的構造方法和她調用sing()方法的代碼為:
Lady(String name,Animal pet) {this.name = name; this.pet = pet;} public void myPetSing(){pet.sing();}
如果構造出Lady對象的pet是Cat對象c,這個c首先會向上轉型為Animal類,也就是說Lady的pet屬性雖然指向的是"Cat c"對象,但它只能看見其中的父對象Animal部分。那么 myPetSing(pet.sing();) 方法自然會調用Animal類的sing()方法。 以上過程是編譯器所認為的過程,也是靜態(tài)綁定或前期綁定的過程。
但編譯完成后,雖然pet屬性只能看見Animal部分,但實際在執(zhí)行時pet.sing()卻換轉換為執(zhí)行c.sing()。就相當于做了一次對象類型強制轉換 Cat petx = (Cat)pet 。 這是動態(tài)綁定或后期綁定的過程,也稱為多態(tài)。
實際上,對象在被new出來后,它所涉及到的方法都放在code segment內存區(qū)中的一個方法列表中,這個列表中包含了子類、父類的方法,只不過有些時候不可見的方法無法去調用。當執(zhí)行程序時,內部的機制可以從方法列表中搜索出最符合環(huán)境的方法并執(zhí)行它。
實現多態(tài)的技術的關鍵點在于:
(1). 定義一個父類引用f,并將其指向子類對象,即進行向上轉型 ;
(2). 重寫父類的方法,并使用父類引用f去引用這個方法。這樣就可以面向父類進行編程 。
正如上面的示例中,將pet定義為Animal類而非具體的子類,并在方法中調用pet.sing()。如此依賴,就無需考慮pet到底是Cat/Dog,在進行功能擴展添加Bird類時,完全不用再修改Lady類的這段代碼。
再例如,父類Animal,子類Dog,方法sing()。
class Animal {public void sing(A);} class Dog extends Animal {public void sing(B);} public class Test { Animal a = new Dog(); //父類引用變量a指向子對象Dog,此時將向上轉型 a.sing(); //使用父類引用變量a引用被重寫的方法sing(),執(zhí)行時將動態(tài)綁定到Dog的sing() }
以上這篇java對象類型轉換和多態(tài)性(實例講解)就是小編分享給大家的全部內容了,希望能給大家一個參考,也希望大家多多支持腳本之家。
相關文章
Lombok中@EqualsAndHashCode注解的使用及說明
這篇文章主要介紹了Lombok中@EqualsAndHashCode注解的使用及說明,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教2023-03-03SpringBoot中@ComponentScan的使用詳解
這篇文章主要介紹了SpringBoot中@ComponentScan的使用詳解,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教2021-11-11