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

Java 面向对象设计-何时使用getClass和instanceof,java,oop,Java,Oop,在大学演讲中,讲师说使用getClass和instanceof表示设计不好 哪些是糟糕设计的示例用法?使用这些方法会导致哪些问题?这些方法是否有任何有效的用途,这些都是不错的设计?糟糕的用途 我想说的是,在大多数情况下,这是糟糕设计的标志。例如,假设您有一个对象列表,您正在执行instanceof,然后是cast,然后是调用特定于该类的方法。相反,这些对象应该具有公共超类,并且应该在其中声明方法——然后根据对象的实际类型执行不同的代码(因为子类可能定义不同的实现) 使用反射API的工具 使用这些

在大学演讲中,讲师说使用
getClass
instanceof
表示设计不好

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

糟糕的用途 我想说的是,在大多数情况下,这是糟糕设计的标志。例如,假设您有一个对象列表,您正在执行
instanceof
,然后是cast,然后是调用特定于该类的方法。相反,这些对象应该具有公共超类,并且应该在其中声明方法——然后根据对象的实际类型执行不同的代码(因为子类可能定义不同的实现)

使用反射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();
}

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

通常在设计良好的代码中,您不需要这样做。最常见的情况是,这是因为您与无法更改的对象交互,但需要基于这些对象具有不同的行为

例如,如果Jaroslaw Pawlak示例中的动物是由第三方库提供的,您无法更改,现在需要添加新的行为(例如,腿部受伤的动物不再能跑,但可以飞),那么使用instanceof可能是实现这一点的唯一方法

结果往往是糟糕的架构,在理想世界中你不会这样做,但有时这是获得所需结果的唯一途径


我也在Swing GUI中使用过它,例如,我在JPanel中有许多不同的控件,我正在扫描列表并根据控件的类型调用不同的方法。另一种方法是保存特定类型控件的列表和/或编写包装并保存这些包装的列表。这两种方法都会增加更多的簿记和开销,并且确保列表始终同步可能会在将来导致奇怪的错误(例如,如果您添加了一个控件,但忘记将其添加到要扫描的列表中)。

每个重载的equals()方法都使用instanceOf或getClass,所以我不认为使用这些是一个糟糕的设计。@EdwinJarosiński我想不出更多的有效案例。我见过很多代码,其中在多个if语句中使用了
instanceof
,根据输入参数等决定要执行的代码。这是一个糟糕的设计。嗯。。。在其他情况下这是正确的,这是糟糕的设计。我不想遇到像检查输入参数这样的人,这是一个很好的答案!非常感谢:)在以下情况下可以避免它:Animal是一个抽象类,具有以下字段:name、groundSpeed和run作为抽象方法。我们有狗类有相同的字段和运行方法。除此之外,我们还有鹰类,也有飞行速度场。Eagle有一个run method和fly method的实现。@Niminim这一切都取决于您实际如何使用这些对象。@Niminim所以即使Eagle既能跑又能飞,您也不能让它跑,而是让它飞。这意味着您可以在
Animal
中创建
move
方法-
Dog
将其委托给
run
,而
Eagle
将其委托给
fly
。然后你只需对动物调用
move
,不管它到底是什么动物。在编写时,我实际上没有考虑在
move()
中添加逻辑:)很高兴我能提供帮助。关于外部库的观点很好。为了给您提供另一个真实的示例,我最近使用了NetSuite的SuiteTalk,一个SOAP web服务API。从概念上讲,它有一个事务类,其中包含子类Invoice、CustomerRefund、CashDeposit等,但它们在Java类定义中实际上并不相关。它们都有常用的方法,例如getTranId()。要编写一个接受任何事务类型并打印其事务id的方法,需要使用反射,因为您不能以多态方式调用getTranId()。谢谢Tim和klitos!我还没有使用过任何第三方库,但我相信这一见解在将来会更加相关。
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);
}
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);
    }
}
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