我有类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!
}
}
最佳答案
这里
.map(entry -> new FooDTO(entry))
你总是打电话
new FooDTO(AbstractFoo)
设置 this.isBar
的构造函数为假。
即使entry
持有的对象运行时类型 Bar
,变量entry
类型为AbstractFoo
,因为它是 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)
,因为您将传递 AbtractFoo
类型的引用.
关于Java集合、泛型和抽象类: concrete class information lost,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/52758643/