我有一个类Bar
(可能还有许多其他类),它扩展了抽象类AbstractFoo
。将Bar
的实例转换为FooDTO
时,将检测到具体类。
但是,当将Bar
实例的集合转换为FooDTO
的列表时,具体的类信息将丢失,并且将基于AbstractFoo
进行转换。
这是怎么了?
public class CollectionGenericsNGTest {
public static abstract class AbstractFoo { }
public static class Bar extends AbstractFoo { }
public static class FooDTO {
final boolean isBar;
public FooDTO(AbstractFoo f) {
this.isBar = false;
}
public FooDTO(Bar b) {
this.isBar = true;
}
}
public static class FooDTOList {
List<FooDTO> list;
public FooDTOList(Collection<? extends AbstractFoo> source) {
list = source.stream()
.map(entry -> new FooDTO(entry))
.collect(Collectors.toList());
}
public List<FooDTO> getList() {
return list;
}
}
@Test
public void testDTO() {
Bar b = new Bar();
FooDTO f = new FooDTO(b);
assertTrue(f.isBar);
}
@Test
public void testDTO_abstract() {
AbstractFoo b = new Bar();
FooDTO f = new FooDTO(b);
assertTrue(f.isBar); // <-- fails, too
}
@Test
public void testDTOList() {
Bar b = new Bar();
List<Bar> collection = Arrays.asList(b);
FooDTOList list = new FooDTOList(collection);
FooDTO f = list.getList().get(0);
assertTrue(f.isBar); // <--- this fails!
}
}
答案 0 :(得分:3)
这里
.map(entry -> new FooDTO(entry))
您总是 打电话
new FooDTO(AbstractFoo)
将this.isBar
设置为false的构造函数。
即使entry
持有的对象的运行时类型为Bar
,变量entry
的类型也为AbstractFoo
,因为它是{{1}的一部分},因此编译器知道对象必须是Collection<? extends AbstractFoo>
,但不知道它是AbstractFoo
。重载解析适用于编译类型的引用类型,而不是运行时对象的类型。
如果要检查Bar
持有的对象的运行时类型,而不是变量类型,则可以考虑使用
entry
分配给您的字段时。这将检查this.isBar = (f instanceof Bar);
引用的实际对象的运行时类型。
在更简单的情况下
f
构造函数调用解析为Bar b = new Bar();
FooDTO f = new FooDTO(b);
,因为您正在向其传递类型为new FooDTO(Bar)
的引用。
如果您有:
Bar
然后,构造函数调用将解析为AbstractFoo b = new Bar();
FooDTO f = new FooDTO(b);
,因为您将传递类型为new FooDTO(AbstractFoo)
的引用。