简体   繁体   中英

Class implementing an interface is not accepted for generics wildcard

I would like to have a map with the class type as a key in order to avoid casting when accessing the value.

Update
The following is actual code you can copy/paste in an IDEA and reproduce it.

    interface MyInterface {

    }

    class SomeConcreteClass implements MyInterface{

    }

    private Map<Class<?>, ? extends MyInterface> map = newMap();

    private Map<Class<?>, ? extends MyInterface> newMap() {
        Map<Class<?>, ? extends MyInterface> map = new HashMap<>();
        map.put(SomeConcreteClass.class, new SomeConcreteClass());
        return map;
    }

    private void accessMap() {
        SomeConcreteClass clazz = (SomeConcreteClass) map.get(SomeConcreteClass.class); <== I want to avoid cast here
    }

Problem: This does not compile. I get in this line:

map.put(SomeConcreteClass.class, new SomeConcreteClass());  

the error:

Wrong 2nd argument type. Found: 'SomeConcreteClass required '? extends MyInterface`

What am I doing wrong here? SomeConcreteClass should be accepted as it implements the interface

This can be simplified, a lot:

Map<String, ? extends CharSequence> map = new HashMap<>();
map.put("", ""); // <-- will not compile

List<? extends CharSequence> l = new ArrayList<>();
l.add(""); // <-- will not compile

The principle behind this is called PECS ( very famous). Why this is not allowed is a bit verbose to read, but mainly explained here . Though not obvious, if that would be allowed - it might cause problems in other places, the answer I linked explains that.

You can achieve what you want with a so-called typesafe heterogeneous container :

static class TypeSafeValue {

    private MyInterface t;

    TypeSafeValue() {
    }

    public <T> TypeSafeValue setValue(MyInterface t) {
        this.t = t;
        return this;
    }

    public <T> T getValue(Class<T> clazz) {
        return clazz.cast(t);
    }
}

And usage would be:

private Map<Class<?>, TypeSafeValue> newMap() {
    Map<Class<?>, TypeSafeValue> map = new HashMap<>();
    map.put(SomeConcreteClass.class, new TypeSafeValue().setValue(new SomeConcreteClass()));
    return map;
}

private void accessMap() {
    SomeConcreteClass clazz = map.get(SomeConcreteClass.class).getValue(SomeConcreteClass.class);
}

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