多态之向上转型

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重写的方法
    }
}

结果:

分析:

在方法中,虽然定义的返回值是父类类型,但是返回的值都是子类的对象,所以也是父类类型引用指向子类类型的向上转型,调用打印方法调用的也是各自子类重写的方法。


总结

向上转型的条件:

  • 具有继承关系;
  • 有方法重写;
  • 父类引用指向子类对象。

向上转型的实现:

  • 在方法中直接转型;
  • 父类类型当参数;
  • 父类类型当返回值。

子类书写与父类同名的属性,重写父类的方法,向上转型,调用的是父类的属性,子类的方法。


本人水平有限,若有不足或错误,望补充和指出~

posted @ 2021-01-22 13:55  deng-hui  阅读(931)  评论(0编辑  收藏  举报