Roam
Roam

Reputation: 4949

Java collections -- polymorphic access to elements

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.

Upvotes: 0

Views: 144

Answers (3)

Sotirios Delimanolis
Sotirios Delimanolis

Reputation: 280174

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.

Upvotes: 1

Masudul
Masudul

Reputation: 21981

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{

}

Upvotes: 0

Paul Draper
Paul Draper

Reputation: 83393

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

Upvotes: 2

Related Questions