概述
instanceof 是Java中的一个关键字,称为类型比较操作符,用于检查对象是否为指定Class的实例。在这个教程中,我们将探讨传统 instanceof
方法的替代方案,可能需要这些替代方法来改善代码设计和可读性。
instanceof 示例
首先我们回顾下 instanceof 的用法。先定义一个父类:
/**
* 恐龙类
*/
public class Dinosaur {
}
接下来,创建第一子类:
/**
* 鸭龙
*/
public class Anatotitan extends Dinosaur {
String run() {
return "running";
}
}
创建第二个子类:
/**
* 始盗龙
*/
public class Euraptor extends Dinosaur {
String flies() {
return "flying";
}
}
Dinosaur 类具有子类共有的其他方法,但为了简化,我们略过它们。现在,我们编写一个方法,用于创建对象实例并调用其run
方法。在返回结果之前,我们会使用 instanceof
检查新实例的类型:
public static void moveDinosaur(Dinosaur dinosaur) {
if (dinosaur instanceof Anatotitan) {
Anatotitan anatotitan = (Anatotitan) dinosaur;
anatotitan.run();
}
else if (dinosaur instanceof Euraptor) {
Euraptor euraptor = (Euraptor) dinosaur;
euraptor.flies();
}
}
下面我们将使用其他方案来替代 instanceof
。
使用 getClass()
*getClass()*
方法有助于获取对象的类。当我们检查一个对象是否属于某类时,可以使用 getClass()
作为 instanceof
的替代。在我们的示例设置中,保持父类和子类结构不变,然后编写一个测试方法,使用 getClass()
替换 instanceof
:
public static String moveDinosaurUsingGetClass(Dinosaur dinosaur) {
if (dinosaur.getClass().equals(Anatotitan.class)) {
Anatotitan anatotitan = (Anatotitan) dinosaur;
return anatotitan.run();
} else if (dinosaur.getClass().equals(Euraptor.class)) {
Euraptor euraptor = (Euraptor) dinosaur;
return euraptor.flies();
}
return "";
}
验证测试:
@Test
public void givenADinosaurSpecie_whenUsingGetClass_thenGetMovementOfEuraptor() {
assertEquals("flying", moveDinosaurUsingGetClass(new Euraptor()));
}
使用多态
多态允许子类重写父类的方法。从而改进代码设计和可读性。由于我们知道所有恐龙都会移动,我们可以在父类中引入一个 move()
方法:
public class Dinosaur {
String move() {
return "walking";
}
}
接着,修改子类,重写 move()
方法:
public class Anatotitan extends Dinosaur {
@Override
String move() {
return "running";
}
}
public class Euraptor extends Dinosaur {
@Override
String move() {
return "flying";
}
}
现在,我们可以直接引用子类,而无需使用 instanceof
。编写一个接受父类作为参数的方法,根据物种返回恐龙的移动行为:
public static String moveDinosaurUsingPolymorphism(Dinosaur dinosaur) {
return dinosaur.move();
}
下面是测试:
@Test
public void givenADinosaurSpecie_whenUsingPolymorphism_thenGetMovementOfAnatotitan() {
assertEquals("running", moveDinosaurUsingPolymorphism(new Anatotitan()));
}
实际项目中推荐使用这种方式。instanceof
的使用可能表示违反了 里氏替换原则(Liskov Substitution Principle)。
使用枚举
在 枚举 类型中,变量可以定义为预定义常量的集合。我们可以利用这种方法改进我们的示例。首先,创建一个枚举,其中包含带方法的常量:
public enum DinosaurEnum {
Anatotitan {
@Override
public String move() {
return "running";
}
},
Euraptor {
@Override
public String move() {
return "flying";
}
};
abstract String move();
}
枚举常量的作用类似于其他替代方案中的子类。接下来,修改 moveDinosaur()
方法以使用枚举类型:
public static String moveDinosaurUsingEnum(DinosaurEnum dinosaurEnum) {
return dinosaurEnum.move();
}
测试:
@Test
public void givenADinosaurSpecie_whenUsingEnum_thenGetMovementOfEuraptor() {
assertEquals("flying", moveDinosaurUsingEnum(DinosaurEnum.Euraptor));
}
这个设计消除了父类和子类。在复杂的场景中,当父类的行为比较多时,这种做法并不推荐。
使用Visitor设计模式
访问者模式有助于对相似或相关的对象进行操作,它将逻辑从对象类转移到另一个类。让我们将此方法应用于我们的示例设置。首先,创建一个带有方法的接口,并传入一个访问者作为参数,以便获取对象类型:
public interface Dinosaur {
String move(Visitor visitor);
}
接下来,创建一个带有两个方法的访问者接口,方法接受子类作为参数:
public interface Visitor {
String visit(Anatotitan anatotitan);
String visit(Euraptor euraptor);
}
然后,让子类实现 Dinosaur 接口,并重写其方法。方法接受访问者作为参数,以获取对象类型,这将取代 instanceof
的使用:
public class Anatotitan implements Dinosaur {
public String run() {
return "running";
}
@Override
public String move(Visitor dinoMove) {
return dinoMove.visit(this);
}
}
接下来,创建一个类来实现访问者接口并重写方法:
public class DinoVisitorImpl implements Visitor {
@Override
public String visit(Anatotitan anatotitan) {
return anatotitan.run();
}
@Override
public String visit(Euraptor euraptor) {
return euraptor.flies();
}
}
最后,为这种方法编写测试方法:
public static String moveDinosaurUsingVisitorPattern(Dinosaur dinosaur) {
Visitor visitor = new DinoVisitorImpl();
return dinosaur.move(visitor);
}
编写单元测试:
@Test
public void givenADinosaurSpecie_whenUsingVisitorPattern_thenGetMovementOfAnatotitan() {
assertEquals("running", moveDinosaurUsingVisitorPattern(new Anatotitan()));
}
这种方法使用了一个接口。访问者包含了我们的程序逻辑。
总结
在这篇文章中,我们探讨了 instanceof
的不同替代方法。instanceof
方法可能违反了里氏替换原则。采用替代方案提供了更稳固的设计。多态方法是推荐的,因为它增加了更多的价值。如往常一样,完整的源代码可在 GitHub 查看。