Java 集合、泛型和摘要 class:具体 class 信息丢失
Java collection, generics and abstract class: concrete class information lost
我有 class Bar
(可能还有许多其他 classes)扩展了抽象 class AbstractFoo
。将 Bar
的实例转换为 FooDTO
时,检测到具体的 class。
但是,当将Bar
个实例的集合转换为FooDTO
个列表时,具体的class信息丢失了,转换是在[=15]的基础上进行的=].
这里出了什么问题?
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
设置为 false 的构造函数。
即使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
.
类型的引用
我有 class Bar
(可能还有许多其他 classes)扩展了抽象 class AbstractFoo
。将 Bar
的实例转换为 FooDTO
时,检测到具体的 class。
但是,当将Bar
个实例的集合转换为FooDTO
个列表时,具体的class信息丢失了,转换是在[=15]的基础上进行的=].
这里出了什么问题?
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
设置为 false 的构造函数。
即使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
.