Reputation:
I'm trying to access a custom Java generic stored in a map as below.
Unfortunately I get a type mis-match error.
Now, I can cast it to the type I want because but this seems messy to me.
Is there a clean way of doing the assignment?
Thanks
public interface BusinessObject {
}
public class SalesItemA implements BusinessObject {
}
public interface BusinessRuleSuite<T extends BusinessObject> {
public void fire(T shell);
}
public abstract class BusinessRuleSuiteCommon<T extends BusinessObject>
implements BusinessRuleSuite<T> {
public synchronized void fire(T bo) {
// do something with bo;
}
}
public class SalesBusinessRuleSuite extends
BusinessRuleSuiteCommon<SalesItemA> {
}
public class SalesProcessor {
private final Map<Class<? extends BusinessObject>, BusinessRuleSuite<? extends BusinessObject>> businessRules;
public SalesProcessor(Map<Class<? extends BusinessObject>, BusinessRuleSuite<? extends BusinessObject>> businessRules) {
this.businessRules = businessRules;
}
public void processItem(SalesItemA sia) {
/// This assignment doesn't work??? Why?
BusinessRuleSuite<SalesItemA> p = this.businessRules.get(sia.getClass());
p.fire(sia);
}
}
}
Upvotes: 1
Views: 282
Reputation: 269647
It looks like you are trying to create a single SalesProcessor
class, and change its processItem
method to accept any implementation of BusinessObject
.
Neal Gafter tried to make this work by using a "super type tokens" to expand Josh Block's "typesafe heterogenous container" pattern, but it has some flaws.
Upvotes: 0
Reputation: 1727
because businessRules is a
private final Map<Class<? extends BusinessObject>, BusinessRuleSuite<? extends BusinessObject>> businessRules
so instead of
BusinessRuleSuite<SalesItemA> p = this.businessRules.get(sia.getClass());
do
BusinessRuleSuite<? extends BusinessObject> p = this.businessRules.get(sia.getClass());
the Map
places no garuantees that for each key ( Class<K>
, BusinessRuleSuite<V>
), K=V
, which i assume is true in your code.
public class RuleProcessor<T extends BusinessObject> {
private final Map<Class<T>, BusinessRuleSuite<T>> businessRules;
public SalesProcessor(Map<Class<T>, BusinessRuleSuite<T>> businessRules) {
this.businessRules = businessRules;
}
// - or have a blank constructor, and add them one by one
public void add(Class<T> c, BusinessRuleSuite<T> rs) {
businessRules.add(c, rs);
}
public void processItem(T sia) {
BusinessRuleSuite<T> p = this.businessRules.get(sia.getClass());
p.fire(sia);
}
}
Upvotes: 1
Reputation: 10665
Your map does not contain items of type SalesItemA but of type <? extends BusinessObject>
change
BusinessRuleSuite<SalesItemA> p = this.businessRules.get(sia.getClass());
to
BusinessRuleSuite<BusinessRuleSuite<? extends BusinessObject>> p = this.businessRules.get(sia.getClass());
... not actually tested, so this might not work at all.
Upvotes: 0
Reputation: 328556
Because the return type of get()
is a BusinessRuleSuite<? extends BusinessObject>
.
This means it will accept anything that inherits from BusinessObject
during put()
. But when you use it on the right hand side of an assign, Java can't make assumptions. It has to play safe, so the get()
behaves as if you had used BusinessRuleSuite<BusinessObject>
(without the extends
).
There are two ways to achieve what you want:
Use BusinessRuleSuite<SalesItemA>
in the map declaration
Use a cast
Upvotes: 3