多态之向上转型
1.什么是多态?
可以先举个生活中的例子:打印机,根据条件的不同可以打印出黑白纸张和彩色纸张。
Java中的多态就类似于生活中的打印机的例子,即同一事物,条件不同,产生不同的结果。
那什么叫同一事物呢?在Java中就是同一个对象,一个对象的实际类型是确定的。
如:
new Father();
new Son();
那什么叫条件不同呢?即该对象的引用,所以,引用类型就不确定了。
如:
Person p = new Son();
Student s = new Son();
......
这里也可以看出,指向的引用类型不同,即多态的前提条件是:必须有继承。
2.向上转型
父类的引用指向其一个子类的对象,即为向上转型。
向上转型为自动转换,不难理解,子类继承了父类,子类对象很容易就能向父类类型转换。
例:
父类:Father,一个name属性,一个show方法
package com.dh.polymorphic;
public class Father {
public String name = "father";
public void show(){
System.out.println("I am father.");
}
}
子类:Son,继承父类Father
package com.dh.polymorphic;
public class Son extends Father{
}
测试类:实例化子类对象,指向父类引用,输出属性和调用方法
package com.dh.polymorphic;
public class Application {
public static void main(String[] args) {
//子类对象指向父类引用
Father f = new Son();
System.out.println(f.name);
f.show();
}
}
现在我们可以进行一些测试了:
-
子类不做任何的添加,只继承父类:
结论:当子类没有书写与父类相同的属性,也没有重写父类方法时,调用的属性和方法都是父类的。
所以在这里并没有看出任何的多态,与子类可以说是毫无关系。
-
子类中书写与父类同名的属性,并且重写父类的方法,调用父类和子类共有的属性和方法,会是什么结果呢?
子类:Son
package com.dh.polymorphic; public class Son extends Father{ public String name = "son"; @Override public void show() { System.out.println("I am son."); } }
测试类:调用父类和子类共有的属性和方法
package com.dh.polymorphic; public class Application { public static void main(String[] args) { //子类对象指向父类引用 Father f = new Son(); System.out.println(f.name); f.show(); } }
通过结果得出结论:当父类引用指向子类对象时,调用的是父类和子类共有的,父类的属性和子类的方法。
解析:
在这里是不是看出有那么点不一样,有那么点多态的感觉了,多态是方法的多态,属性是没有多态的,而且方法是可以重写的,属性是不能被重写的,所以子类重写了父类的方法,调用的就是子类的方法,而属性不能重写,所以调用的就还是父类的属性。
所以,多态存在的条件又增加一个,多态存在的条件是有方法的重写。
-
调用子类独有的属性和方法,会怎么样呢?
子类:Son,定义与父类同名的属性,重写父类的show方法,并且自己添加age属性和study方法
package com.dh.polymorphic; public class Son extends Father{ public String name = "son"; //子类特有的属性 public int age = 18; @Override public void show() { System.out.println("I am son."); } //子类特有的方法 public void study(){ System.out.println("go to school."); } }
测试类:调用子类特有的属性和方法
结论:当父类引用指向子类对象时,不能使用子类独有的属性和方法。
3.向上转型的几种形式
(1)父类类型为参数
我们先总结一下向上转型多态存在的条件:
- 有继承关系;
- 有方法重写;
- 父类引用指向子类实例。
我们来实现文中开头所说的打印机的例子,来感受一下多态:
父类:
Printer类,里面有一个打印方法。(因为多态与属性无关,故在此不书写属性了)
package com.dh.polymorphic;
public class Printer {
public void print(){
}
}
子类1:
黑白打印机,BlackWhitePrinter类,重写父类的打印方法。
package com.dh.polymorphic;
public class BlackWhitePrinter extends Printer {
@Override
public void print() {
System.out.println("黑白打印");
}
}
子类2:
彩色打印机,ColorPrinter类,重写父类的打印方法。
package com.dh.polymorphic;
public class ColorPrinter extends Printer {
@Override
public void print() {
System.out.println("彩色打印");
}
}
测试类:
package com.dh.polymorphic;
public class PrinterTest {
//书写一个父类类型为参数的方法
public void tPrint(Printer printer){
//调用打印方法
printer.print();
}
public static void main(String[] args) {
//new一个测试类
PrinterTest printerTest = new PrinterTest();
//调用上述方法,传递参数
//传递子类1对象为参数
BlackWhitePrinter blackWhitePrinter = new BlackWhitePrinter();
printerTest.tPrint(blackWhitePrinter);//参数传递之后,Printer printer = blackWhitePrinter
//传递子类2对象为参数
ColorPrinter colorPrinter = new ColorPrinter();
printerTest.tPrint(colorPrinter);//参数传递之后,Printer printer = colorPrinter
}
}
结果:
分析:
上述两个参数传递,本质上都是父类引用指向子类对象的向上转型,所以调用打印方法,调用的都是子类重写的方法。
(2)父类类型为返回值
还是以打印机为例:
父类、两个子类的代码都不变
修改测试类:
package com.dh.polymorphic;
public class PrinterTest2 {
//书写一个方法,返回值为父类类型
//因为有多个子类,所以需要一个条件来判断到底是哪个子类,给方法添加一个参数
public Printer tPrinter(int num) {
//当num为1时,就是子类1
if (num == 1) {
return new BlackWhitePrinter();
} else if (num == 2) {//当num为2时,就是子类2
return new ColorPrinter();
}
//都不符合条件,就返回null(空对象,没有具体的指向实例)
return null;
}
public static void main(String[] args) {
//实例化一个测试类,调用方法进行测试
PrinterTest2 printerTest2 = new PrinterTest2();
//子类1
Printer printer = printerTest2.tPrinter(1); //本质为Printer printer = new BlackWhitePrinter();
printer.print(); //调用的为子类1重写的方法
//子类2
Printer printer1 = printerTest2.tPrinter(2); //本质为Printer printer = new ColorPrinter();
printer1.print(); //调用的为子类2重写的方法
}
}
结果:
分析:
在方法中,虽然定义的返回值是父类类型,但是返回的值都是子类的对象,所以也是父类类型引用指向子类类型的向上转型,调用打印方法调用的也是各自子类重写的方法。
总结
向上转型的条件:
- 具有继承关系;
- 有方法重写;
- 父类引用指向子类对象。
向上转型的实现:
- 在方法中直接转型;
- 父类类型当参数;
- 父类类型当返回值。
子类书写与父类同名的属性,重写父类的方法,向上转型,调用的是父类的属性,子类的方法。
本人水平有限,若有不足或错误,望补充和指出~