简体   繁体   中英

Accessing a Java generics stored in a Map

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

Because the return type of get() is a BusinessRuleSuite<? extends BusinessObject> 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:

  1. Use BusinessRuleSuite<SalesItemA> in the map declaration

  2. Use a cast

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.

alternatively:

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

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 .

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.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM