当有许多'时,替代实施INSTANCEOF子类'

时间:2016-07-23 07:19:07

标签: java instanceof

我已经尝试寻找合适的解决方案,但很难找到它。

这是一个非常简单的问题 - 当我们想要在该类的实例中有很多时确定一个类类型时,是否存在使用INSTACEOF的正确,有效的替代方法?

例如:

我们有一个父Product

    public abstract class Product {

    public abstract String preview();

}

其中有3个子类(乙烯基,书籍和视频)

public class Vinyl extends Product {

@Override
public String preview() {
    return "Playing a vinyl";
}

}

public class Book extends Product {

@Override
public String preview() {
    return "Reading a book";
}

}

@Override
public String preview() {
    return "Playing a video";
}

现在,我想根据每种产品类型检查它的预览方法返回值 - 当我们有 3 子类时,它在编写时非常清晰整洁: / p>

    public class TestProduct {

    List<Product> productList = new ArrayList<>();

    public void preview(Product product) {
        for (Product currentProduct : productList) {
            String preview = currentProduct.preview();

            if (product instanceof Book) {
                Assert.assertEquals(preview, "Reading a book");

            }   else if (product instanceof Vinyl) {
                Assert.assertEquals(preview, "Playing vinyl");

            } else {
                Assert.assertEquals(preview, "Playing video");
            }
        }
    }
}

但是,如果我们没有3个产品,但是100个或更多?,该怎么办? 我写这个INSTACEOF语句的次数是100次没有任何意义。 有没有适当的,可扩展的方式?

4 个答案:

答案 0 :(得分:3)

这是使用interface

的可能实现
public interface class Product {
    public void doSomething();
}

然后每个子类都必须实现自己的doSomething()

public class Vinyl implements Product {
    public void doSomething(){
        System.out.println("Vinyl product");
    }
}

public class Book implements Product {
    public void doSomething(){
        System.out.println("Book product");
    }
}

public class Video implements Product {
    public void doSomething(){
        System.out.println("Video product");
    }
}

然后

Product product = new Vinyl();
product.doSomething();
// prints on the console --> "Vinyl product"

同样适用于实现接口产品的其他类。 抽象类无法实例化,在您的情况下没有帮助,这就是我使用接口的原因。 附注:通常当您需要使用instanceof时,您可能使用了错误的设计。

答案 1 :(得分:2)

为什么不让你做的工作成为子类本身的一部分呢?

public abstract class Product {
    abstract void doSomething();
}

然后,执行循环的代码不需要知道它具有什么类型的产品。它可以让产品决定做什么。

for (Product product : products) {
    product.doSomething();
}

如果需要,doSomething()方法可以有参数和返回类型。

您还可以将访问者模式用于同一主题的变体,以便您在实际的Product实现类之外指定实现。

答案 2 :(得分:1)

使用多态或访客模式并不总是切实可行或可能,例如当您无法更改子类时,或者您希望基于特定子类型doSomething()的许多不同情况时,例如当你有许多不同的装饰器时 - 你不希望子类本身的逻辑。在这种情况下,您可以使用外部处理程序,例如:

class Handler<T> {
    private Map<Class<? extends T>, Consumer<? super T>> handlers = new HashMap<>();

    Handler<T> register(Class<? extends T> subtype, Consumer<? super T> action) {
        handlers.put(subtype, action);
        return this;
    }

    void handle(T instance) {
        Optional.ofNullable(instance)
                .map(Object::getClass)
                .map(this.handlers::get)
                .orElseThrow(() -> new IllegalStateException("Unknown subtype!"))
                .accept(instance);
    }
}

用法:

Handler<Product> productHandler = new Handler<Product>()
        .register(Vinil.class, vinil -> gramophone.play(vinil.sideB()))
        .register(Book.class, book -> library.put(book.isbn(), book))
        .register(Video.class, video -> { /* no-op */ });

products.stream()
        .forEach(productHandler::handle);

答案 3 :(得分:1)

一种可能的解决方案是享受多态性

public static void check(Object o) {
        List<? extends Products> list = Arrays.asList(new A(), new B(), new C());

            if (o instanceof Products) {
                Products p = (Products) o;
                p.doSomething();
            }
        }

,界面是

public interface Products {
    public void doSomething();
}