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

时间:2015-11-10 11:27:50

标签: java oop

在大学讲座期间讲师说使用- (void)drawRect:(CGRect)rect { CGContextRef context = UIGraphicsGetCurrentContext(); CGContextSetFillColorWithColor(context, GRAPH_LINE_COLOR.CGColor); CGContextSetLineWidth(context, 1); //drawing graph layout CGContextBeginPath(context); CGContextMoveToPoint(context, SCREEN_WIDTH * 0.10, SCREEN_HEIGHT * 0.20); CGContextAddLineToPoint(context, SCREEN_WIDTH * 0.95,SCREEN_HEIGHT * 0.20); CGContextAddLineToPoint(context, SCREEN_WIDTH * 0.95, SCREEN_HEIGHT * 0.85); CGContextAddLineToPoint(context, SCREEN_WIDTH * 0.10, SCREEN_HEIGHT * 0.85); CGContextClosePath(context); CGContextDrawPath(context, kCGPathStroke); } getClass表示设计不好。

什么是不良设计的示例用法?使用这些方法可能导致什么问题?这些方法是否有任何有效的用法,这些都是不错的设计?

2 个答案:

答案 0 :(得分:6)

用法差

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

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

所有这些都是关于分析代码的使用方式和提取公共部分。如果在循环中你总是命令动物运行,那么在run()上声明Animal之后就不需要强制转换。另一方面,我们希望动物移动,并不重要,所以让他们通过在move()类中创建抽象Animal方法来选择默认移动类型。

答案 1 :(得分:1)

通常在精心设计的代码中,您不需要这样做。大多数情况下,它确实会发生,因为您正在与您无法改变的对象进行交互,但需要根据它们进行不同的行为。

例如,如果Jaroslaw Pawlak的例子中的动物是由第三方图书馆提供的,你无法改变,现在你需要添加一种新的行为(例如腿部受伤的动物,更长时间但可以飞行)然后使用instanceof可能是实现这一目标的唯一方法。

结果往往是糟糕的建筑,你不会在理想的世界中这样做,但有时它是获得你需要的结果的唯一方法。

我也在Swing GUI中使用它,例如,我在JPanel中有许多不同的控件,我在列表中扫描并根据控件的类型调用不同的方法。另一种方法是保留特定类型控件的列表和/或编写包装器并保留这些包装器的列表。这两种方法都会增加更多的簿记和开销,并确保列表在任何时候都是同步的,以后可能会导致奇怪的错误(例如,如果添加控件并忘记将其添加到列表中进行扫描)。