我应该始终使用通配符作为函数参数的泛型类型还是只传递一个泛型类型的实例?

时间:2014-11-10 14:46:49

标签: java generics inheritance

考虑以下无效的Java代码:

class Example {
    private static class Base {}
    private static class Child extends Base{
        public void do(){}
    }

    public void foo(List<Base>) {}

    public static void main(String[] args) {
        List<Child> list = new ArrayList<>();
        fillListInPlaceWithChildren(list);

        foo(list); //compile error, List<Child> does not extend List<Base>

        list.stream().forEach(Child::do);
    }
}

这不会编译,因为List<Child>不是List<Base>的子类,因此无法传递给foo函数。据我所知,要解决这类问题,我有两个选择:

  1. 让foo接受List<? extends Base>

    class Example {
        private static class Base {}
        private static class Child extends Base{
            public void do(){}
        }
    
        public void foo(List<? extends Base>) {}
    
        public static void main(String[] args) {
            List<Child> list = new ArrayList<>();
            fillListInPlaceWithChildren(list);
    
            foo(list); 
            list.stream().forEach(Child::do);
        }
    }
    
  2. list成为List<Base>,并在需要时将元素转换为Child的实例

    class Example {
        private static class Base {}
        private static class Child extends Base{
            public void do(){}
        }
    
        public void foo(List<Base>) {}
    
        public static void main(String[] args) {
            List<Base> list = new ArrayList<>();
            fillListInPlaceWithChildren(list);
    
            foo(list); 
            list.stream().forEach((item) -> ((Child)item).do());
        }
    }
    
  3. 哪种选择被认为是最佳做法,为什么?或者它们都有不同的用例?

2 个答案:

答案 0 :(得分:1)

我总是建议使用更高级别的抽象。如果您可以使用List<Base> list代替List<Child>列表,请使用它!。

但在这种情况下foo设定了合同。 foo应该接受来自List<? extend Base>的所有列表,还是只接受List<Child>才有意义?

问题在于答案。

答案 1 :(得分:1)

您无法将List<Child>传递给需要List<Base>的方法,因为它可能会导致一些问题。如果您传入List<Child>

,会发生什么情况
void foo(List<Base> list) {
    list.add(new Base());
}

如果您不依赖仅包含Base个实例的列表。使用List<? extends Base>比在代码中添加类型转换更安全,同时也减少了阅读代码的人的意外。