在此示例中了解Java泛型的最佳用法 [英] Understanding best use of Java Generics in this example case
问题描述
-
有些工厂可以制造某种类型的产品并知道他们是否忙碌:
界面工厂< ProductType> {
void buildProduct(ProductType product);
boolean isBusy();
}
-
有一组不同的产品, )知道它们在哪个工厂生成:
interface产品< ActualProductType extends Product< ActualProductType>> {
工厂< ActualProductType> getFactory();
}
-
然后有一个订单系统, :
interface OrderSystem {
Product<?> getNextProduct();
$ / code $ <$ pre $ $ $ $ $ $ $ $ $ $ $ $ $ $ $最后,有一个调度程序抓取订单并维护工作每个工厂的队列:
class Dispatcher {
Map< Factory<?>,队列< Product< ;? >>> workQueues
= new HashMap< Factory<>,Queue< Product<>>>();
public void addNextOrder(OrderSystem orderSystem){
Product<?> nextProduct = orderSystem.getNextProduct();
workQueues.get(nextProduct.getFactory())。add(nextProduct); (工厂<>工厂:workQueues.keySet())
如果(!factory.isBusy())为
public void assignWork(){
)
factory.buildProduct(workQueues.get(factory).poll());
code $
$ b $ p免责声明:这段代码仅仅是一个例子,并且存在一些错误(检查工厂是否存在作为workQueues缺失的关键字,...)并且非常不优化(可以迭代入口集而不是键集,... )
现在提问: Dispatcher(
factory.buildProduct(workqueues.get(factory).poll());
)会引发此编译错误:
类型Factory< capture#5-of?>中的方法buildProduct(capture#5-of?不适用于参数(Product< capture#7-of?>)
我已经一直在绞尽脑汁想如何以类型安全的方式解决这个问题,但是我的泛型技巧让我在这里失败了......
将其更改为以下内容例如,也没有帮助:
public void assignWork(){
for(Factory&?b> factory :workQueues.keySet())
if(!factory.isBusy()){
Product<?> product = workQueues.get(factory).poll();
product.getFactory()。buildProduct(product);
$ b即使在这种情况下,我猜我可以为每个调用factory.buildProduct(this)的产品添加一个buildMe()函数。 / code>,但我很难相信这应该是我最优雅的解决方案。
有什么想法?
编辑:
产品和工厂实施的简单示例:
类Widget实现Product< Widget> {
public String color;
@Override
public Factory< Widget> getFactory(){
返回WidgetFactory.INSTANCE;
}
}
class WidgetFactory实现Factory< Widget> {
static final INSTANCE = new WidgetFactory();
$ b $ @Override
public void buildProduct(Widget product){
//构建给定颜色的小部件(product.color)
}
@Override
public boolean isBusy(){
return false; //很快就可以制作这个小部件
$ / code>
解决方案明白了!感谢meriton回答了这个问题的这个版本:
我需要通过
product.getFactory()。buildProduct(product)
--part来通过一个单独的通用函数来编译这个编译器。以下是我需要对代码进行的修改以实现它的功能(乱七八糟):
-
更多具体关于OrderSystem:
interface OrderSystem {
< ProductType extends Product< ProductType>> ProductType getNextProduct();
-
定义我自己的更强类型的队列来保存产品:
@SuppressWarnings(serial)
class MyQueue< T延伸产品< T>>扩展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); (工厂<>工厂:workQueues.keySet())
如果(!factory.isBusy())为
public void assignWork(){
)
buildProduct(workQueues.get(factory).poll());
}
public< ProductType extends Product< ProductType>> void buildProduct(ProductType product){
product.getFactory()。buildProduct(product);
code $
$ b $ p注意所有通用函数,特别是最后一个函数。另外请注意,我不能像在原始问题中那样将此函数内联到我的for循环中。
另请注意,
@ SuppressWarnings(unchecked)
注释在addNextOrder()
函数中用于队列的类型转换,而不是某些Product对象。由于我只在这个队列上调用add,在编译和类型擦除之后,它将所有元素简单地存储为对象,这不会导致任何运行时抛出异常。 (如果这是错误,请纠正我)Let's say I have a manufacturing scheduling system, which is made up of four parts:
There are factories that can manufacture a certain type of product and know if they are busy:
interface Factory<ProductType> { void buildProduct(ProductType product); boolean isBusy(); }
There is a set of different products, which (among other things) know in which factory they are built:
interface Product<ActualProductType extends Product<ActualProductType>> { Factory<ActualProductType> getFactory(); }
Then there is an ordering system that can generate requests for products to be built:
interface OrderSystem { Product<?> getNextProduct(); }
Finally, there's a dispatcher that grabs the orders and maintains a work-queue for each factory:
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()); } }
Disclaimer: This code is merely an example and has several bugs (check if factory exists as a key in workQueues missing, ...) and is highly non-optimal (could iterate over entryset instead of keyset, ...)
Now the question:
The last line in the Dispatcher (
factory.buildProduct(workqueues.get(factory).poll());
) throws this compile-error:The method buildProduct(capture#5-of ?) in the type Factory<capture#5-of ?> is not applicable for the arguments (Product<capture#7-of ?>)
I've been racking my brain over how to fix this in a type-safe way, but my Generics-skills have failed me here...
Changing it to the following, for example, doesn't help either:
public void assignWork() { for (Factory<?> factory: workQueues.keySet()) if (!factory.isBusy()) { Product<?> product = workQueues.get(factory).poll(); product.getFactory().buildProduct(product); } }
Even though in this case it should be clear that this is ok...
I guess I could add a "buildMe()" function to every Product that calls
factory.buildProduct(this)
, but I have a hard time believing that this should be my most elegant solution.Any ideas?
EDIT:
A quick example for an implementation of Product and Factory:
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 } }
解决方案Got it! Thanks to meriton who answered this version of the question:
How to replace run-time instanceof check with compile-time generics validation
I need to baby-step the compiler through the
product.getFactory().buildProduct(product)
-part by doing this in a separate generic function. Here are the changes that I needed to make to the code to get it to work (what a mess):Be more specific about the OrderSystem:
interface OrderSystem { <ProductType extends Product<ProductType>> ProductType getNextProduct(); }
Define my own, more strongly typed queue to hold the products:
@SuppressWarnings("serial") class MyQueue<T extends Product<T>> extends LinkedList<T> {};
And finally, changing the Dispatcher to this beast:
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); } }
Notice all the generic functions, especially the last one. Also notice, that I can NOT inline this function back into my for loop as I did in the original question.
Also note, that the
@SuppressWarnings("unchecked")
annotation on theaddNextOrder()
function is needed for the typecast of the queue, not some Product object. Since I only call "add" on this queue, which, after compilation and type-erasure, stores all elements simply as objects, this should not result in any run-time casting exceptions, ever. (Please do correct me if this is wrong!)这篇关于在此示例中了解Java泛型的最佳用法的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!
-