实现接口的类不被通配符泛型所接受。

huangapple go评论86阅读模式
英文:

Class implementing an interface is not accepted for generics wildcard

问题

我想要一个以类类型作为键的映射,以避免在访问值时进行强制转换。

更新
以下是您可以复制粘贴到IDEA中并进行重现的实际代码。

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); // <== 我想避免在这里进行强制转换
}

问题:这段代码无法编译。我在以下这行:

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

出现了错误:

错误的第二个参数类型。找到:'SomeConcreteClass',需要:'?extends MyInterface'

我在这里做错了什么?SomeConcreteClass 应该被接受,因为它实现了接口。

英文:

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&lt;Class&lt;?&gt;, ? extends MyInterface&gt; map = newMap();

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

    private void accessMap() {
        SomeConcreteClass clazz = (SomeConcreteClass) map.get(SomeConcreteClass.class); &lt;== 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

答案1

得分: 1

这可以大大简化:

Map<String, ? extends CharSequence> map = new HashMap<>();
map.put("", ""); // <-- 将无法编译

List<? extends CharSequence> l = new ArrayList<>();
l.add(""); // <-- 将无法编译

这背后的原则被称为 PECS(非常著名)。为什么不允许这样做的解释有点冗长,但主要在这里有详细解释。虽然不明显,但如果允许这样做,可能会在其他地方引起问题,我链接的答案解释了这一点。

你可以通过所谓的“类型安全的异构容器”来实现你想要的效果:

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

使用方法如下:

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);
}
英文:

This can be simplified, a lot:

Map&lt;String, ? extends CharSequence&gt; map = new HashMap&lt;&gt;();
map.put(&quot;&quot;, &quot;&quot;); // &lt;-- will not compile

List&lt;? extends CharSequence&gt; l = new ArrayList&lt;&gt;();
l.add(&quot;&quot;); // &lt;-- 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 &lt;T&gt; TypeSafeValue setValue(MyInterface t) {
        this.t = t;
        return this;
    }

    public &lt;T&gt; T getValue(Class&lt;T&gt; clazz) {
        return clazz.cast(t);
    }
}

And usage would be:

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

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

huangapple
  • 本文由 发表于 2020年4月7日 18:48:37
  • 转载请务必保留本文链接:https://go.coder-hub.com/61078299.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定