投射嵌套泛型集时出现问题

问题描述 投票:0回答:2

我今天偶然发现了这个有趣的强制转换/泛型问题:

public class A {

    Map<Class<? extends B>, List<Set<B>>> mapListSet = new HashMap<>();
    Map<Class<? extends B>, Set<B>> mapSet = new HashMap<>();

    public <T extends B> List<Set<T>> foo(Class<T> clazz) {
        List<Set<T>> listSet = (List<Set<T>>) mapListSet.get(clazz);

        return listSet;
    }

    public <T extends B> Set<T> bar(Class<T> clazz) {
        Set<T> set = (Set<T>) mapSet.get(clazz);

        return set;
    }
}

class B {

}

我可以在 IDE 发出一个警告的情况下编译方法“bar”,而 IDE 完全拒绝编译方法“foo”。这是我最近编写的代码中的一个简化示例,有谁知道我是否可以使其更优雅,而不仅仅是这样做?

    public <T extends B> List<Set<T>> foo(Class<T> clazz) {
        List<Set<T>> listSet = (List) mapListSet.get(clazz);

        return listSet;
    }

非常感谢任何帮助,我感觉这段代码闻起来很糟糕,我很乐意对其进行改进。

java generics
2个回答
3
投票

我的其他答案显示了如何完成您要求的演员阵容。

但是!不过,我认为有一个更好的解决方案:将字段类型更改为:

class A {

    Map<Class<? extends B>, List<? extends Set<? extends B>>> mapListSet = new HashMap<>();
    Map<Class<? extends B>, Set<? extends B>> mapSet = new HashMap<>();
    ....

这样你就不需要强制转换额外类型的技巧,没有它它也能工作。

而且,更重要的是:字段类型表达了元素的实际类型。

您当前的类型表示元素是

Set<B>
。但他们不是!它们不是
B
类型的集合,而是
B
的某些未知子类型的集合。这正是
Set<? extends B>
所表达的意思。


2
投票

要做你想做的事,你必须稍微绕过类型系统。

你必须通过额外的类型来欺骗编译器:

List<Set<T>> listSet = (List<Set<T>>) (Object) mapListSet.get(clazz); // Compiles

问题是,正如 racraman 所指出的,

T extends B
并不意味着
List<Set<T>>
扩展了
List<Set<B>>
,并且由于编译器看不到强制转换如何工作,因此会生成错误。这与您无法投射的原因相同,例如从
String
Number
。由于它们没有子类型关系,编译器认为强制转换不可能是正确的。

看起来您在运行时使用

Class
作为键来跟踪每个集合的成员类型。在这种情况下,使用上述技巧可能是合理的。

© www.soinside.com 2019 - 2024. All rights reserved.