考虑以下无效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函数。为了解决这类问题,据我所知,我有两个选择:
让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); } }
设
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()); } }
哪种选择被认为是最佳实践,为什么?或者它们都有不同的用例?
不能将List<Child>
传递给需要List<Base>
的方法,因为这可能会导致一些问题。如果你通过List<Child>
,在这种情况下会发生什么?
void foo(List<Base> list) {
list.add(new Base());
}
如果您不依赖仅包含Base
实例的列表。使用List<? extends Base>
比在代码中添加类型转换更安全,而且它不会给阅读代码的人带来太多惊喜。
我总是建议使用更高级别的抽象。如果您可以使用List<Base> list
而不是List<Child>
列表,请使用它!。
但在这种情况下,是foo
制定了合同。foo应该接受List<? extend Base>
中包含的所有列表,还是只接受List<Child>
才有意义?
问题就是答案。