java - 面向对象设计——何时使用 getClass 和 instanceof

标签 java oop

<分区>

在大学演讲中,讲师说使用 getClassinstanceof 表明设计不好。

什么是糟糕设计的示例用法?使用这些方法会导致什么问题?这些方法是否有任何有效的用法,这不是糟糕的设计?

最佳答案

用法不当

我会说在大多数情况下这是糟糕设计的标志。例如,假设您有一个对象列表,您正在执行 instanceof,然后进行强制转换,然后调用特定于该类的方法。相反,这些对象应该有共同的父类(super class),并且方法应该在那里声明 - 然后根据对象的实际类型执行不同的代码(因为子类可能定义不同的实现)。

private static class A {
    private void printA() {
        System.out.println("A");
    }
}

private static class B {
    private void printB() {
        System.out.println("B");
    }
}

public static void main(String[] args) {
    List<Object> list = asList(new A(), new B(), new A());

    list.forEach(element -> { // this is bad, don't do it!
        if (element instanceof A) {
            ((A) element).printA();
        }
        if (element instanceof B) {
            ((B) element).printB();
        }
    });
}

相反,您应该这样做:

private interface Printer {
    void print();
}

private static class A implements Printer {
    @Override
    public void print() {
        System.out.println("A");
    }
}

private static class B implements Printer  {
    @Override
    public void print() {
        System.out.println("B");
    }
}

public static void main(String[] args) {
    List<Printer> list = asList(new A(), new B(), new A());

    list.forEach(Printer::print);
}

好的用法

等于方法

您将在自动生成的 equals 方法中看到一个有效的用例。在实际比较对象之前,先检查它们是否属于同一类。如果不是,则它们不能相等,因此存在快速失败优化。这实际上是由 equals 方法采用 Object 类型的参数强制执行的。即使我们比较的两个对象实际上是相等的,我们也必须转换参数,在这样做之前我们应该检查它的类以返回 false 而不是得到 ClassCastException .

IntelliJ 生成的 Equals 方法:

public class Person {
    private String name;
    private String surname;

    @Override
    public boolean equals(Object o) {
        if (this == o) return true;
        if (o == null || getClass() != o.getClass()) return false;

        Person person = (Person) o;

        if (!name.equals(person.name)) return false;
        return surname.equals(person.surname);
    }
}

使用反射 API 的工具

另一个使用这些方法的有效案例是创建各种工具,例如 POJO 到 json 映射器,这只能通过反射 API 完成。

编辑:

根据您在评论中提出的问题,下面是如何实现动物列表的工作示例,其中狗可以跑,鹰可以跑也可以飞:

public static abstract class Animal {

    protected final String name;

    public Animal(String name) {
        this.name = name;
    }

    public void run() {
        System.out.println(name + " runs");
    }

    public abstract void move();
}

public static class Dog extends Animal {
    public Dog() {
        super("Dog");
    }

    @Override
    public void move() {
        run();
    }
}

public static class Eagle extends Animal {

    public Eagle() {
        super("Eagle");
    }

    public void fly() {
        System.out.println(name + " flies");
    }

    @Override
    public void move() {
        fly();
    }
}

public static void main(String[] args) {
    List<Animal> animals = Arrays.asList(new Dog(), new Eagle());

    animals.forEach(Animal::move);

    System.out.println("Eagle can run too!");
    new Eagle().run();
}

输出:

Dog runs
Eagle flies
Eagle can run too!
Eagle runs

就是分析代码的使用方式,提取通用部分。如果在循环中您总是命令 animal 运行,则不需要强制转换,因为 run() 是在 Animal 上声明的。另一方面,我们希望动物移动,不管如何移动,所以让它们通过在 Animal 类中创建抽象的 move() 方法来选择它们的默认移动类型。

关于java - 面向对象设计——何时使用 getClass 和 instanceof,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/33628934/

相关文章:

JavaScript 字段继承奇怪的行为

java - 我们可以使用 dex2jar 工具或任何其他方式混淆 java 中的注释吗?

java - 使用泛型正确扩展接口(interface)

java - 星微中心图像

Java多线程问题

java - 为什么接口(interface)数据成员是公共(public)的和静态的?

java - 构造函数和构造函数重载

java - 隐式与显式数据结构

java - 无法在我的 sqlite 数据库中插入值

java - 对象属性的更改在主类中不是永久的?