问题背景
在review代码的时候,发现了一个关于Java继承和多态的组合问题。
问题比较少,但是个人觉得是一个很好的问题,可以加深对继承和多态的理解。
问题如下:
在一个service中发现,一个方法里面调用了两个不同的函数,两个函数返回的对象不一样。A对象继承了B对象。
问题分析
这里其实就产生了一个问题,在调用A对象的方法时,调用的是A对象的方法,但是在调用B对象的方法时,调用的是B对象的方法。
那么为什么不调用一个呢?
查看代码才发现,因为特殊情况,需要返回A对象(B对象没有的值),所以就实现了两个接口。
那么就产生了思考:为什么不能直接调用一个接口,减少代码量呢?
*
多态
多态是指程序中定义的引用变量所指向的具体类型和通过该引用变量发出的方法调用在编程时并不确定,而是在程序运行期间才确定,即一个引用变量到底会指向哪个类的实例对象,该引用变量在程序运行期间才确定,这就是多态的概念。
Java语言中的多态(Polymorphism)是面向对象编程的三大基本特性之一,它表示同一个行为具有多个不同表现形式或形态的能力。
在Java中,多态通常通过方法的重写(Override)和方法的重载(Overload)以及接口和父类引用子类对象来实现。
多态的基本概念
-
方法的重写(Override):子类可以重写父类中的方法,使得当使用父类类型的引用指向子类对象时,调用的是子类重写后的方法。
-
方法的重载(Overload):在同一个类中,可以有多个同名但参数列表不同的方法,这称为方法的重载。虽然重载不是严格意义上的多态,但它与多态在概念上有一定的联系。
-
接口和父类引用子类对象:Java中,父类类型的引用可以指向子类对象,调用的是实际对象(子类对象)的方法。这是多态性最典型的表现。
多态的示例
下面是一个通过重写父类方法和使用父类引用指向子类对象来实现多态的示例:
import com.alibaba.fastjson.JSON;
import lombok.Data;
import lombok.EqualsAndHashCode;
import java.io.Serial;
import java.io.Serializable;
/* 父类*/
@Data
class Animal implements Serializable {@Serialprivate static final long serialVersionUID = 1L;private String name;private String color;void makeSound() {System.out.println("The animal makes a sound");}
}/* 子类1:狗*/
@EqualsAndHashCode(callSuper=true)@Data
class Dog extends Animal implements Serializable {@Serialprivate static final long serialVersionUID = 1L;//品种 private String breed; private String gender;@Overridevoid makeSound() {System.out.println("The dog barks");}
}/* 子类2:猫*/
@EqualsAndHashCode(callSuper=true)@Data
class Cat extends Animal implements Serializable {@Serialprivate static final long serialVersionUID = 1L;//是否有胡须private String hasWhiskers;//生命次数private int livesLeft;@Overridevoid makeSound() {System.out.println("The cat meows");}
}// 主类
public class Main {public static void main(String[] args) {// 创建Animal类型的数组,可以存放Animal及其子类的对象Animal[] animals = new Animal[2];// 创建Dog和Cat对象,并赋值给Animal类型的数组animals[0] = new Dog();animals[1] = new Cat();// 遍历数组,调用每个对象的makeSound方法for (Animal animal : animals) {animal.makeSound(); // 多态的体现:调用的是实际对象的方法}}
}
在这个例子中,我们有一个Animal
父类,以及两个子类Dog
和Cat
。这两个子类都重写了父类的makeSound
方法。在Main
类的main
方法中,我们创建了一个Animal
类型的数组,并分别将Dog
和Cat
对象赋值给数组的元素。当我们遍历数组并调用每个元素的makeSound
方法时,实际调用的是每个对象自己的makeSound
方法实现,这就是多态的体现。
运行上述代码,输出将会是:
The dog barks
The cat meows
每个对象都按照它们自己的类型来执行相应的行为,尽管它们都是通过Animal
类型的引用来调用的。这就是Java中多态的一个简单示例。通过多态,我们可以提高代码的灵活性和可维护性,使得程序能够更容易地扩展和适应新的需求。
解决方法
到了这里,大家应该明白了吧,其实我们可以反过来用,在接口的返回对象的时候,定义返回类型为Animal。然后在实际使用中在程序运行期间才确定具体的Animal对象(Cat或者Dog),然后直接引用属性就OK了。 后续就算这个接口返回的更多的类型,只要是继承的都可以返回父类的方式进行解决(如果改动太大,还是另外写会更好一些)
public class Main {static Animal getAnimalInfo(int type){ if (type == 1) {// 创建Dog对象Dog dog = new Dog(); dog.setBreed("拉布拉多");dog.setGender("公"); dog.setName("小黑");dog.setColor("白色");return dog;} else if (type == 2) {Cat cat =new Cat();cat.setHasWhiskers("有胡须");cat.setLivesLeft(9);cat.setName("小白");cat.setColor("黑色"); return cat;} return null;}public static void main(String[] args) {Dog dog = (Dog)getAnimalInfo(1); System.out.println(JSON.toJSONString(dog));Cat cat = (Cat)getAnimalInfo(2);System.out.println(JSON.toJSONString(cat));}
}
输出的结果是:```java{"breed":"拉布拉多","color":"白色","gender":"公","name":"小黑"}
{"color":"黑色","hasWhiskers":"有胡须","livesLeft":9,"name":"小白"}
这个我们就可以把子类对应的那个代码抽离放到一起,来快速解决这个问题。
PS:这样的设计其实还是有问题的,但是是个老系统的代码,把这个设计改起来会比较麻烦,就只能一步步去改了。