在此示例中了解Java泛型的最佳用法

时间:2012-11-29 20:59:51

标签: java generics

假设我有一个制造调度系统,它由四部分组成:

  • 有些工厂可以生产某种类型的产品并知道他们是否忙碌:

    interface Factory<ProductType> {
        void buildProduct(ProductType product);
        boolean isBusy();
    }
    
  • 有一套不同的产品,除其他外,知道它们在哪个工厂建造:

    interface Product<ActualProductType extends Product<ActualProductType>> {
        Factory<ActualProductType> getFactory();
    }
    
  • 然后有一个订购系统可以生成对要构建的产品的请求:

    interface OrderSystem {
        Product<?> getNextProduct();
    }
    
  • 最后,有一个调度员抓住订单并维护每个工厂的工作队列:

    class Dispatcher {
        Map<Factory<?>, Queue<Product<?>>> workQueues
                          = new HashMap<Factory<?>, Queue<Product<?>>>();
    
        public void addNextOrder(OrderSystem orderSystem) {
            Product<?> nextProduct = orderSystem.getNextProduct();
            workQueues.get(nextProduct.getFactory()).add(nextProduct);
        }
    
        public void assignWork() {
            for (Factory<?> factory: workQueues.keySet())
                if (!factory.isBusy())
                    factory.buildProduct(workQueues.get(factory).poll());
        }
    }
    

免责声明:此代码仅仅是一个示例,并且有几个错误(检查工厂是否存在缺少workQueues中的键,...)并且非常不优化(可以迭代入口集而不是键集,。 ..)

现在的问题是:

Dispatcher中的最后一行(factory.buildProduct(workqueues.get(factory).poll());)抛出此编译错误:

The method buildProduct(capture#5-of ?) in the type Factory<capture#5-of ?> is not applicable for the arguments (Product<capture#7-of ?>)

我一直在努力解决如何以类型安全的方式解决这个问题,但我的仿制技能让我失败了......

例如,将其更改为以下内容也无济于事:

public void assignWork() {
    for (Factory<?> factory: workQueues.keySet())
        if (!factory.isBusy()) {
            Product<?> product = workQueues.get(factory).poll();
            product.getFactory().buildProduct(product);
        }
}

即使在这种情况下,应该很清楚这是可以的......

我想我可以为每个调用factory.buildProduct(this)的产品添加“buildMe()”函数,但我很难相信这应该是我最优雅的解决方案。

有什么想法吗?

修改

产品和工厂实施的快速示例:

class Widget implements Product<Widget> {
    public String color;

    @Override
        public Factory<Widget> getFactory() {
            return WidgetFactory.INSTANCE;
    }
}

class WidgetFactory implements Factory<Widget> {
    static final INSTANCE = new WidgetFactory();

    @Override
    public void buildProduct(Widget product) {
        // Build the widget of the given color (product.color)
    }

    @Override
    public boolean isBusy() {
        return false; // It's really quick to make this widget
    }
}

3 个答案:

答案 0 :(得分:0)

你的代码很奇怪 您的问题是,您将A Product<?>传递给期望ProductType实际上为T的方法。
另外我不知道Product是什么,因为你没有在OP中提及它的定义 您需要传递Product<?>才能工作。我不知道你会在哪里得到它,因为我无法理解你想用你的代码做什么

答案 1 :(得分:0)

Map<Factory<?>, Queue<Product<?>>> workQueues = new HashMap<Factory<?>, Queue<Product<?>>>();

// factory has the type "Factory of ?"
for (Factory<?> factory: workqueues.keySet())       
    // the queue is of type "Queue of Product of ?"
    Queue<Product<?>> q = workqueues.get(factory);

    // thus you put a "Product of ?" into a method that expects a "?"
    // the compiler can't do anything with that.
    factory.buildProduct(q.poll());
}

答案 2 :(得分:0)

知道了!感谢meriton回答了这个问题的版本:

How to replace run-time instanceof check with compile-time generics validation

我需要通过product.getFactory().buildProduct(product) - 在一个单独的泛型函数中执行此操作来完成编译器。以下是我需要对代码进行的更改以使其工作(多么糟糕):

  • 更具体地说明OrderSystem:

    interface OrderSystem {
        <ProductType extends Product<ProductType>> ProductType getNextProduct();
    }
    
  • 定义我自己的,更强类型的队列来保存产品:

    @SuppressWarnings("serial")
    class MyQueue<T extends Product<T>> extends LinkedList<T> {};
    
  • 最后,将Dispatcher更改为此野兽:

    class Dispatcher {
        Map<Factory<?>, MyQueue<?>> workQueues = new HashMap<Factory<?>, MyQueue<?>>();
    
        @SuppressWarnings("unchecked")
        public <ProductType extends Product<ProductType>> void addNextOrder(OrderSystem orderSystem) {
            ProductType nextProduct = orderSystem.getNextProduct();
            MyQueue<ProductType> myQueue = (MyQueue<ProductType>) workQueues.get(nextProduct.getFactory());
            myQueue.add(nextProduct);
        }       
    
        public void assignWork() {
            for (Factory<?> factory: workQueues.keySet())
                if (!factory.isBusy())
                    buildProduct(workQueues.get(factory).poll());
        }
    
        public <ProductType extends Product<ProductType>> void buildProduct(ProductType product) {
            product.getFactory().buildProduct(product);
        }
    }   
    

注意所有通用函数,尤其是最后一个函数。还要注意,我不能像在原始问题中那样将此函数内联到我的for循环中。

另请注意,队列的类型转换需要@SuppressWarnings("unchecked")函数上的addNextOrder()注释,而不是某些Product对象。因为我只在这个队列上调用“add”,在编译和类型擦除之后,将所有元素简单地存储为对象,这不应该导致任何运行时转换异常。 (如果这是错的,请纠正我!)