JAVA面向对象之多态

262 阅读5分钟

这是我参与8月更文挑战的第9天,活动详情查看:8月更文挑战

文章目录

定义和作用

多态的定义
面向对象的多态性,即“一个接口,多个方法”。多态性体现在父类中定义的属性和方法被子类继承后,可以具有不同的属性或表现方式。

多态的作用
1、可以直接把不同子类当父类看(多种子类型都可以转换成一致的父类型),屏蔽子类间的差异,提高代码的通用率/复用率
2、父类引用可以调用不同子类的功能,提高了代码的扩充性和可维护性

如何实现多态

java实现多态有三个必要条件:继承、重写、向上转型。

继承:在多态中必须存在有继承关系的子类和父类。
重写:子类对父类中某些方法进行重新定义,在调用这些方法时就会调用子类的方法。
向上转型:在多态中需要将子类的引用赋给父类对象,只有这样该引用才能够具备技能调用父类的方法和子类的方法

例子1:Human 类

我们使用上一章的 Human 类举例(传送门:【达内课程】面向对象之继承与重写

人类范围包含学生和员工
人类:name、gender、age
|——学生:除了拥有人类的属性,还特有属性 school
|——员工:除了拥有人类属性,还拥有特有属性 company

我们有三个类 Human、Student、Employee。每个类有自己的 toString() 方法用来输出自己的属性。

类的代码都不变,这里只改了 MainActivity 代码:

public class MainActivity extends AppCompatActivity {
    ......
    
    private void f1() {
        Human human = new Human("美伢", "女", 18);
        //textView.setText(human.tostring());

        f(human);
    }

    private void f2() {
        Student student = new Student();
        student.name = "小A";
        student.gender = "男";
        student.age = 10;
        student.school = "北大";
        //textView.setText(student.tostring());
        //学生类型向上转型成Human类型,传递到f()方法
        f(student);
    }

    private void f3() {
        Employee employee = new Employee();
        employee.name = "小B";
        employee.gender = "女";
        employee.age = 22;
        employee.company = "冀春";
        //textView.setText(employee.tostring());

        f(employee);
    }

    /*
     * 处理父类型对象
     * 所有子类型对象都可以转成父类型,传递到这个方法进行处理
     * 如果传递的是父类型,直接调用父类型的tostring();方法
     * 如果传递的是子类型,则调用子类型的tostring();方法
     * */
    void f(Human human) {
        textView.append("\n" + human.tostring());
    }
}

如果运行 f(2); 时,加断点,可以看到传到 f(Human human); 方法中的是Student 类型
Human断点
运行程序:
在这里插入图片描述

instanceof 关键字

它的作用是测试它左边的对象是否是它右边的类的实例,返回boolean类型的数据。一般用于判断一个对象是否为某个类的实例,是返回true,否返回false。

例子2:Point3D 类

我们使用上一章的 Point3D 类举例(传送门:【达内课程】面向对象之继承与重写

我们有一个 Point 类,可以求二维坐标中一个点到原点的距离。我们增加了一个 Point3D 类继承了 Point 类。除了可以计算二维坐标中一个点到原点的距离,还可以计算三维坐标中一个点到原点的距离

修改 Point3D 类中 distance(Point p) 方法,判断传入的 Point 类型是 二维坐标还是三维坐标,类型不同,求距离方法不同。

public class Point3D extends Point {
    ......
    @Override
    public double distance(Point p) {
        int dx = x - p.x;
        int dy = y - p.y;
        int dz;

        //当前点是三维点
        //与参数点p求距离,p也可以是二维点也可以是三维点
        //需要判断p是二维点还是三维点
        if (p instanceof Point3D) {
            //向上转型后,只能调用父类定义的通用成员
            //不能调用子类特有成员
            //可以先向下转回成子类型再调用
            dz = z - ((Point3D) p).z;
        } else {
            dz = z;
        }
        return Math.sqrt(dx * dx + dy * dy + dz * dz);
    }
}

例子3:绘制图形

我们有图形类 Shape(图形),它的子类有 Line(线)、Circle(圆)、Square(方):

Shape:draw(画图)、clean(清除)
|-Line:有自己的 draw 方法。有单独的 length 方法
|-Circle:有自己的 draw 方法
|-Square:有自己的 draw 方法

Share

public class Shape {
    public void draw(TextView view) {
        //无意义代码,会在子类中重写
        view.setText("图形");
    }

    public void clean(TextView view) {
        view.setText("");
    }
}

Line

public class Line extends Shape {
    @Override
    public void draw(TextView view) {
        super.draw(view);
        view.setText("-");
    }

    public void length(TextView view) {
        view.append("\n线很长很长...");
    }
}

Circle

public class Circle extends Shape {
    @Override
    public void draw(TextView view) {
        super.draw(view);
        view.setText("o");
    }
}

Square

public class Square extends Shape {
    @Override
    public void draw(TextView view) {
        super.draw(view);
        view.setText("口");
    }
}

xml

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical">

    <Button
        android:id="@+id/button1"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:onClick="doClick"
        android:text="Shape" />

    <Button
        android:id="@+id/button2"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:onClick="doClick"
        android:text="Line" />

    <Button
        android:id="@+id/button3"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:onClick="doClick"
        android:text="Circle" />

    <Button
        android:id="@+id/button4"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:onClick="doClick"
        android:text="Squre" />

    <Button
        android:id="@+id/button5"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:onClick="doClick"
        android:text="擦除" />

	<TextView
        android:id="@+id/text"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:textSize="20sp"
        android:textColor="#222222"
        android:gravity="center"/>
</LinearLayout>

MainActivity

public class MainActivity extends AppCompatActivity {
    Button button1;
    Button button2;
    Button button3;
    Button button4;
    Button button5;
    TextView textView;
    private Shape currentShape;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        textView = findViewById(R.id.text);
        button1 = findViewById(R.id.button1);
        button2 = findViewById(R.id.button2);
        button3 = findViewById(R.id.button3);
        button4 = findViewById(R.id.button4);
        button5 = findViewById(R.id.button5);
    }

    public void doClick(View view) {
        switch (view.getId()) {
            case R.id.button1:
                f(new Shape());
                break;
            case R.id.button2:
                f(new Line());
                break;
            case R.id.button3:
                f(new Circle());
                break;
            case R.id.button4:
                f(new Square());
                break;
            case R.id.button5:
                f1();
                break;
        }
    }


    private void f(Shape shape) {
        //参数对象,保存到成员变量
        currentShape = shape;
        shape.draw(textView);
        //向上转型后只能访问父类定义的通用成员
        //不能访问子类特有成员
        //shape.length();
        if (shape instanceof Line) {
            Line line = (Line) shape;
            line.length(textView);
        }
    }

    private void f1() {
        if (currentShape != null) {
            currentShape.clean(textView);
            currentShape = null;
        }
    }
}

运行程序:
在这里插入图片描述

类型转换

其中两个例子的注释中的都写了向上转型。这里解释一下,向上转型即子类对象转为父类型。格式如下:

父类 父类对象 = 子类实例 ;(自动转换)

就像上边绘制图形例子中的 f() 方法参数虽然是 Shape ,但我们可以直接传入子类实例 new Line()new Circle()

向上转型特点如下:
1、向上转型后,只能调用父类定义的通用成员
2、不能调用子类特有成员,但可以先向下转回成子类型再调用

向下转型即已经转为父类型的子类对象再转回子类型。格式如下:

子类 子类对象 = (子类)父类实例 ;(强制转换)

就像上边绘制图形例子中 f() 方法里,参数是 Shape,但我们要想调用子类特有的方法需要先强转到子类类型:Line line = (Line) shape;