我今天偶然发现了这个有趣的强制转换/泛型问题:
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;
}
非常感谢任何帮助,我感觉这段代码闻起来很糟糕,我很乐意对其进行改进。
我的其他答案显示了如何完成您要求的演员阵容。
但是!不过,我认为有一个更好的解决方案:将字段类型更改为:
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>
所表达的意思。
要做你想做的事,你必须稍微绕过类型系统。
你必须通过额外的类型来欺骗编译器:
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
作为键来跟踪每个集合的成员类型。在这种情况下,使用上述技巧可能是合理的。