文章目录

定义和作用

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

多态的作用
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 类型
【达内课程】面向对象之多态_面向对象
运行程序:
【达内课程】面向对象之多态_多态_02

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;
}
}
}

运行程序:
【达内课程】面向对象之多态_android_03

类型转换

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

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

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

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

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

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

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