1

In the code below I would like to get a "? super SomeInterface" from the Map how do I declare the type of "value" to enable me to do so?

class SomeClass { /* override hashCode & equals appropriately */}
interface SomeInterface<T> { }
interface AnotherInterface { }


class MainClass {
       private final Map<SomeClass, ? super SomeInterface<? extends AnotherInterface>> someMap;
       private final SomeClass someClass = new SomeClass();

       public MainClass() {
          someMap = new HashMap<SomeClass, SomeInterface<? extends AnotherInterface>>();
          someMap.put(someClass, new SomeInterface<AnotherInterface>(){});
       }

       private void getValue(SomeClass someClass) {
          /*
           * I want to get a "? super SomeInterface<? extends AnotherInterface>" from the Map
           * how do I declare the type of "value" to enable me to do so
           * 
           */
          Object value = someMap.get(someClass);
       }

       private
       <T extends SomeInterface<? extends AnotherInterface>>
       T getValue2(SomeClass someClass) {
          T value;
          // the code below does not work
          // Type mismatch: cannot convert from capture#3-of
          // ? super SomeInterface<? extends AnotherInterface> to T
          value = someMap.get(someClass);
        }
    }

Thanks in advance.

3 Answers3

3

Object is the only thing you could possibly declare value as, since if you have a ? super Anything it could be any superclass of Anything all the way up to Object. You must therefore assign it to the most general type.

If you have a generic type that produces a <? super Something> it's almost surely a poor design (I don't even think the language supports it). That's because you can make no deductions on what it produces, and almost always gains you nothing (see below for a question I asked on the subject, though). "PECS" is a good mnemonic for remembering this: "produces: (use) extends, consumes: (use) super".

See also

Community
  • 1
  • 1
Mark Peters
  • 80,126
  • 17
  • 159
  • 190
1

For your getValue2 method, the compiler cannot guarantee that the provided key will map to that particular type T (whatever the caller is expecting). This is because the map's values are declared using a wildcard, and there is no way to be sure an arbitrary ? super SomeInterface<? extends AnotherInterface> is a T, even if they have the same restrictions. You will need to cast to T (possibly after checking it if you can't be sure it will succeed)

I encountered the same issue a while back when implementing the following Map:

final Map<Class<? extends MyClass>, MyClassMetaInfo<? extends MyClass>> metaMap;

public <T extends MyClass> MyClassMetaInfo<T> getMetaInfo(Class<T> myClass) {
    T metaInfo = (T)metaMap.get(myClass); //need to cast here
    return metaInfo;
}

Beyond this, though, I'm confused with what you're trying to do here and would like to see an explanation/use case.

Paul Bellora
  • 54,340
  • 18
  • 130
  • 181
0

Lets try another approach. I replaced the interfaces with classes since I think this helps clarify things a bit. I also made them extend to demonstrate an example.

class SomeClass { /* override hashCode & equals appropriately */ }
class Parent { }    
class Child extends Parent { }

class MainClass {

    private final Map<SomeClass, ? super Child> someMap;
    private final SomeClass someClass = new SomeClass();

    public MainClass() {
        someMap = new HashMap<SomeClass, Child>();     // #1
        // someMap = new HashMap<SomeClass, Parent>(); // #2 , also valid
        // someMap = new HashMap<SomeClass, Object>(); // #3 , also valid
        someMap.put(someClass, new Child());     // #4
        // someMap.put(someClass, new Parent()); // #5 error
        // someMap.put(someClass, new Object()); // #6 error
    }

    private void getValue(SomeClass someClass) {

        Object value = someMap.get(someClass);
    }

    private <T extends Child> T getValue2(SomeClass someClass) {
        // error
        T value = someMap.get(someClass);
    }
}

someMap is defined to hold a map. The line #1 is allowed but #2 and #3 also. So your map can hold maps which hold different kind of objects. Now think a bit which put operations are allowed. The signature of put() is as follows:

? super Child java.util.Map.put(SomeClass key, ? super Child value)

So value is either of type Child, Parent or Object. But the compiler doesn't know since Generics are implemented with type erasure. So if the actual type is Child and you would put a Parent this would not work.

get() looks like this:

? super Child java.util.Map.get(Object key)

Now for the hard part. Again, you don't know what actual type this is. As stated above, #1, #2 or #3 could be used. If you used #2 or #3 and you would expect the return type to be at least Child you would assign a Parent or an Object to a variable of type Child. And this is why the compiler forbids it.

Robert Kühne
  • 898
  • 1
  • 12
  • 33