Java集合,泛型和抽象类:丢失了具体的类信息

时间:2018-10-11 11:12:46

标签: java generics abstract

我有一个类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!
    }

}

1 个答案:

答案 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)的引用。