在本例中理解Java泛型的最佳使用

  • 本文关键字:泛型 最佳 Java 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)的Product添加一个"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
}
}

您的代码很奇怪
您的问题是将Product<?>传递给一个方法,该方法期望ProductType实际上是T
我也不知道Product是什么,因为你在OP中没有提到它的定义。
你需要传递一个Product<?>才能工作。我不知道你会在哪里得到它,因为我不明白你试图用你的代码做什么

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());
}

明白了!感谢meritn,他回答了这个版本的问题:

如何用编译时泛型验证替换运行时检查实例

我需要在一个单独的泛型函数中逐步完成product.getFactory().buildProduct(product)部分。以下是我需要对代码进行的更改,以使其正常工作(真是一团糟):

  • 更具体地介绍订单系统:

    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循环中。

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

最新更新