1. 多态的运行
调用成员变量时:编译看左边,运行看《左》边
调用成员方法时:编译看左边,运行看《右》边
代码示例:
Fu f = new Zi(); //编译看左边的父类中有没有name这个属性,没有就报错 //在实际运行的时候,把父类name属性的值打印出来 System.out.println(f.name); //编译看左边的父类中有没有show这个方法,没有就报错 //在实际运行的时候,运行的是子类中的show方法 f.show();
2. 多态的优点(使用场景)
1. 能简化代码。
比如将一个父类或者接口作为方法参数类型,这样这个函数就能接收所有子类的对象作为参数,就不需要再重载这个方法,去分别接收所有子类作为参数类型了。
同理,可以将父类或者接口作为数组,List等的参数类型,这样这些数组或者列表就可以加入所以子类对象作为元素了,减少了代码的复杂度。
1. 代码可重用性和灵活性
场景: 假设你正在开发一个图形界面应用程序,需要处理不同类型的图形元素,如圆形、矩形和三角形。
不使用多态:
- 你可能需要为每种图形类型编写单独的代码来处理它们的绘制、移动等操作。
使用多态:
- 你可以创建一个名为
Shape
的基类,并定义共通的方法,如draw()
和move()
。 - 圆形、矩形和三角形都继承自
Shape
类,并实现这些方法。 - 你只需编写一次处理
Shape
对象的代码,就可以处理任何具体的形状,例如在一个列表中存储Shape
对象并遍历它们,调用draw()
方法,无论列表中是圆形、矩形还是三角形。
// 基类 Shape
abstract class Shape {
abstract void draw();
abstract void move();
}
// 圆形
class Circle extends Shape {
void draw() {
System.out.println("Drawing a Circle");
}
void move() {
System.out.println("Moving a Circle");
}
}
// 矩形
class Rectangle extends Shape {
void draw() {
System.out.println("Drawing a Rectangle");
}
void move() {
System.out.println("Moving a Rectangle");
}
}
// 使用多态
public class GraphicsApp {
public static void main(String[] args) {
Shape[] shapes = new Shape[]{new Circle(), new Rectangle()};
for (Shape shape : shapes) {
shape.draw();
shape.move();
}
}
}
此例子中,就直接声明了Shape类型的数组,里面可以用于存不同实现的子类对象。
2. 便于代码维护和扩展
场景: 设计一个支付系统,支持不同类型的支付方式,如信用卡支付、电子钱包支付、银行转账等。
不使用多态:
- 对于每一种支付方式,你可能需要写一个完全独立的代码段来处理支付逻辑。
使用多态:
- 创建一个名为
PaymentMethod
的接口或抽象类,并定义一个processPayment
方法。 - 对于每种支付方式,创建一个类(如
CreditCard
、EWallet
、BankTransfer
),实现PaymentMethod
接口。 - 现在,你的支付逻辑可以针对
PaymentMethod
接口编程,而无需关心具体的支付方式。 - 当添加新的支付方式时,只需添加一个新的
PaymentMethod
实现,无需修改现有的支付逻辑。
接下来是支付的例子,接口代表不同的支付方法。
// 支付方法接口
interface PaymentMethod {
void processPayment(double amount);
}
// 信用卡支付
class CreditCard implements PaymentMethod {
public void processPayment(double amount) {
System.out.println("Processing credit card payment of " + amount);
}
}
// 电子钱包支付
class EWallet implements PaymentMethod {
public void processPayment(double amount) {
System.out.println("Processing e-wallet payment of " + amount);
}
}
// 支付处理
public class PaymentProcessor {
public void processPayment(PaymentMethod method, double amount) {
method.processPayment(amount);
}
public static void main(String[] args) {
PaymentProcessor processor = new PaymentProcessor();
processor.processPayment(new CreditCard(), 100.0);
processor.processPayment(new EWallet(), 55.5);
}
}
此例子中,processPayment方法就直接将PaymentMethod当作参数类型就可以,无需令重载方法去接收CreditCard类型或者EWallet类型。
3. 实现设计模式和高级编程概念
场景: 使用观察者模式来实现一个事件监听系统,例如在用户界面中响应不同类型的事件(如点击、滑动等)。
不使用多态:
- 对于每种事件类型,可能需要单独处理监听器的注册和响应。
使用多态:
- 创建一个
EventListener
接口,定义一个handleEvent
方法。 - 为每种事件类型(如
ClickEvent
、SwipeEvent
)创建特定的监听器类,实现EventListener
接口。 - 在事件分发系统中,你可以创建一个
EventListener
列表,无论监听器的具体类型如何,都能通过调用handleEvent
来通知它们。 - 这样,添加新的事件类型或监听器时,系统的其它部分不需要任何修改。
// 事件监听器接口
interface EventListener {
void handleEvent();
}
// 点击事件监听器
class ClickListener implements EventListener {
public void handleEvent() {
System.out.println("Click event handled");
}
}
// 滑动事件监听器
class SwipeListener implements EventListener {
public void handleEvent() {
System.out.println("Swipe event handled");
}
}
// 事件分发器
class EventDispatcher {
private List<EventListener> listeners = new ArrayList<>();
void registerListener(EventListener listener) {
listeners.add(listener);
}
void dispatchEvent() {
for (EventListener listener : listeners) {
listener.handleEvent();
}
}
public static void main(String[] args) {
EventDispatcher dispatcher = new EventDispatcher();
dispatcher.registerListener(new ClickListener());
dispatcher.registerListener(new SwipeListener());
dispatcher.dispatchEvent();
}
}
3. 缺点
我们已经知道多态编译阶段是看左边父类类型的,如果子类有些独有的功能,此时多态的写法就无法访问子类独有功能了。(具体看文档)
改进:使用时基于instance of进行类型判断,然后进行一个向下转型。转到子类型,去使用子类的方法。
需求:根据需求完成代码:
1.定义狗类
属性:
年龄,颜色
行为:
eat(String something)(something表示吃的东西)
看家lookHome方法(无参数)
2.定义猫类
属性:
年龄,颜色
行为:
eat(String something)方法(something表示吃的东西)
逮老鼠catchMouse方法(无参数)
3.定义Person类//饲养员
属性:
姓名,年龄
行为:
keepPet(Dog dog,String something)方法
功能:喂养宠物狗,something表示喂养的东西
行为:
keepPet(Cat cat,String something)方法
功能:喂养宠物猫,something表示喂养的东西
生成空参有参构造,set和get方法
4.定义测试类(完成以下打印效果):
keepPet(Dog dog,String somethind)方法打印内容如下:
年龄为30岁的老王养了一只黑颜色的2岁的狗
2岁的黑颜色的狗两只前腿死死的抱住骨头猛吃
keepPet(Cat cat,String somethind)方法打印内容如下:
年龄为25岁的老李养了一只灰颜色的3岁的猫
3岁的灰颜色的猫眯着眼睛侧着头吃鱼
5.思考:
1.Dog和Cat都是Animal的子类,以上案例中针对不同的动物,定义了不同的keepPet方法,过于繁琐,能否简化,并体会简化后的好处?
2.Dog和Cat虽然都是Animal的子类,但是都有其特有方法,能否想办法在keepPet中调用特有方法?
//动物类(父类)
public class Animal {
private int age;
private String color;
public Animal() {
}
public Animal(int age, String color) {
this.age = age;
this.color = color;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
public String getColor() {
return color;
}
public void setColor(String color) {
this.color = color;
}
public void eat(String something){
System.out.println("动物在吃" + something);
}
}
//猫类(子类)
public class Cat extends Animal {
public Cat() {
}
public Cat(int age, String color) {
super(age, color);
}
@Override
public void eat(String something) {
System.out.println(getAge() + "岁的" + getColor() + "颜色的猫眯着眼睛侧着头吃" + something);
}
public void catchMouse(){
System.out.println("猫抓老鼠");
}
}
//狗类(子类)
public class Dog extends Animal {
public Dog() {
}
public Dog(int age, String color) {
super(age, color);
}
//行为
//eat(String something)(something表示吃的东西)
//看家lookHome方法(无参数)
@Override
public void eat(String something) {
System.out.println(getAge() + "岁的" + getColor() + "颜色的狗两只前腿死死的抱住" + something + "猛吃");
}
public void lookHome(){
System.out.println("狗在看家");
}
}
//饲养员类
public class Person {
private String name;
private int age;
public Person() {
}
public Person(String name, int age) {
this.name = name;
this.age = age;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
//饲养狗
/* public void keepPet(Dog dog, String something) {
System.out.println("年龄为" + age + "岁的" + name + "养了一只" + dog.getColor() + "颜色的" + dog.getAge() + "岁的狗");
dog.eat(something);
}
//饲养猫
public void keepPet(Cat cat, String something) {
System.out.println("年龄为" + age + "岁的" + name + "养了一只" + cat.getColor() + "颜色的" + cat.getAge() + "岁的猫");
cat.eat(something);
}*/
//想要一个方法,能接收所有的动物,包括猫,包括狗
//方法的形参:可以写这些类的父类 Animal
public void keepPet(Animal a, String something) {
if(a instanceof Dog d){
System.out.println("年龄为" + age + "岁的" + name + "养了一只" + a.getColor() + "颜色的" + a.getAge() + "岁的狗");
d.eat(something);
}else if(a instanceof Cat c){
System.out.println("年龄为" + age + "岁的" + name + "养了一只" + c.getColor() + "颜色的" + c.getAge() + "岁的猫");
c.eat(something);
}else{
System.out.println("没有这种动物");
}
}
}
//测试类
public class Test {
public static void main(String[] args) {
//创建对象并调用方法
/* Person p1 = new Person("老王",30);
Dog d = new Dog(2,"黑");
p1.keepPet(d,"骨头");
Person p2 = new Person("老李",25);
Cat c = new Cat(3,"灰");
p2.keepPet(c,"鱼");*/
//创建饲养员的对象
Person p = new Person("老王",30);
Dog d = new Dog(2,"黑");
Cat c = new Cat(3,"灰");
p.keepPet(d,"骨头");
p.keepPet(c,"鱼");
}
}