Java collections — polymorphic access to elements

痞子三分冷 提交于 2019-12-20 07:37:35

问题


I have a LinkedHashSet of values of ThisType. ThisType is implementing the interface ThatType.

I need the polymorphic use of this collection-- be able to point to it as LinkedHashSet< ThatType >.

How is this done?

i know this is a naive question, but several things i tried didn't work, and i wanna do it the right way.

Thanks in advance.

//==============================

UPDATE: more details:

In the following code, ThisType implements ThatType-- ThatType is an interface.

//LinkedHashMap<Integer, ? extends ThatType> theMap = new LinkedHashMap<>();
LinkedHashMap<Integer, ThisType> theMap = new LinkedHashMap<>();
for (Integer key:intArray)  {
    ThisType a = new ThisType();
    if (theMap.containsKey(key))  {
        a = theMap.get(key);    
        a.doStuff();
    } else {
        a.doOtherStuff();
    }
    theMap.put(key, a);
}

In the end, i wanna return the theMap as a collection of ThatType **, not **ThisType.

this is where the chain is broken. using the commented line (first line) for declaration is giving type mismatch error on put() & get() methods of the hash.

not sure whether this is significant-- but, i'm returning the result as LinkedHashSet< ThatType >. i'm doing the conversion from LinkedHashMap to LinkedHashSet. However, nothing worked nowhere for the polymorphic reference to collection values in the map or set. All and only type i used in all these operations so far is ThisType. ThatType gave me some error just about anywhere i tried it.


回答1:


I think what you want is to use wildcards.

 LinkedHashSet<? extends ThatType> someFunction(LinkedHashSet<? extends ThatType> set) {
      return set;
 }

As has been explained elsewhere LinkedHashSet<ThisType> is not a subclass of LinkedHashSet<ThatType>, since LinkedHashSet<ThisType> can't accept ThatType objects.

The wildcard in LinkedHashSet<? extends ThatType> means a LinkedHastSet of some class (not sure what) that extends ThatType.

Though you may want to consider using this:

 Set<? extends ThatType> someFunction(Set<? extends ThatType> set) {
      return set;
 }



回答2:


When you declare a wildcard bounded type argument as in

LinkedHashMap<Integer, ? extends ThatType> theMap = new LinkedHashMap<>();

you are telling the compiler that the type argument can be any class that is a sub type of ThatType. Imagine this case

LinkedHashMap<Integer, ? extends ThatType> theMap = getLinkedHashMap();

where getLinkedHashMap() is defined as

public LinkedHashMap<Integer, OtherType /* which implements ThatType */> getLinkedHashMap();

The previous assignment would work because OtherType is a sub type of ThatType. However, you could not then expect

ThisType a = theMap.get(key);

to work. Because of the wildcard, all you know is that the map definitely contains ThatType instances (called the upper bound), but they could be of type OtherType or ThatType (or other sub type of ThatType). At most you could do

ThisType a = (ThisType) theMap.get(key);

casting the return value, but then you are opening yourself up to ClassCastException.

Similarly, you cannot call put() with anything other than than null.

theMap.put(someInt, null); // will compile
theMap.put(someInt, new ThisType()); // will not compile
theMap.put(someInt, new OtherType()); // will not compile

The compiler doesn't let you add anything because it can't guarantee what the map contains, again because of the wildcard bound. But null can be used because

The null reference can always undergo a widening reference conversion to any reference type.

So null is a valid argument because it is the lower bound of the wildcard.


You're in a special situation where, it seems, your method creates the LinkedHashMap internally and you know what you will be passing to it. Therefore you can do the following

public static LinkedHashMap<Integer, ThatType> getTheSet(int[] intArray) {
    LinkedHashMap<Integer, ThatType> theMap = new LinkedHashMap<>();

    for (Integer key : intArray) {
        ThisType a = new ThisType();
        if (theMap.containsKey(key)) {
            a = (ThisType) theMap.get(key); // cast it because you know they will definitely be `ThisType` references
            a.doStuff();
        } else {
            a.doOtherStuff();
        }
        theMap.put(key, a);
    }

    return theMap;
}

Since ThisType is a sub type of ThatType, you can put instances of ThisType into the map. You need to be careful how you then use this method's returned value. All the compiler knows about the return value is that it contains ThatType references. It doesn't know that they are actually ThisType.




回答3:


You need the wildcard of Set<? super ThisType>. Look at following example

public static void main(String[] args) {
    Set<ThatType> set=new LinkedHashSet<>();
    Set<ThisType> set2=new LinkedHashSet<>();

    someFunction(set);
    someFunction(set2);
}

static void someFunction(Set<? super ThisType> set) {

}

class ThisType implements ThatType{

}
interface ThatType{

}


来源:https://stackoverflow.com/questions/19946238/java-collections-polymorphic-access-to-elements

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!