文章目录
定义和作用
多态的定义
面向对象的多态性,即“一个接口,多个方法”。多态性体现在父类中定义的属性和方法被子类继承后,可以具有不同的属性或表现方式。
多态的作用
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 类型
运行程序:
instanceof 关键字
它的作用是测试它左边的对象是否是它右边的类的实例,返回boolean类型的数据。一般用于判断一个对象是否为某个类的实例,是返回true,否返回false。
例子2:Point3D 类
我们使用上一章的 Point3D 类举例(传送门:【达内课程】面向对象之继承与重写)
我们有一个 Point 类,可以求二维坐标中一个点到原点的距离。我们增加了一个 Point3D 类继承了 Point 类。除了可以计算二维坐标中一个点到原点的距离,还可以计算三维坐标中一个点到原点的距离
修改 Point3D 类中 distance(Point p) 方法,判断传入的 Point 类型是 二维坐标还是三维坐标,类型不同,求距离方法不同。
public class Point3D extends Point {
......
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 {
public void draw(TextView view) {
super.draw(view);
view.setText("-");
}
public void length(TextView view) {
view.append("\n线很长很长...");
}
}
Circle
public class Circle extends Shape {
public void draw(TextView view) {
super.draw(view);
view.setText("o");
}
}
Square
public class Square extends Shape {
public void draw(TextView view) {
super.draw(view);
view.setText("口");
}
}
xml
<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;
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;