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.

类型的引用