我有一个像这样的类结构:
public class BaseClass {
}
public class ChildClass extends BaseClass {
}
让我们说我有一个方法getList返回List<BaseClass>
:
public List<BaseClass> getList() {
List<BaseClass> b = new ArrayList<>();
return b;
}
List<BaseClass> b = getList();
我想使上述方法getList()通用,以便它可以根据某些条件返回List<BaseClass>
或List<ChildClass>
类型的对象,并将返回值分配给对象类型List<BaseClass>
。原因是我正在处理一些旧代码,其中对象的类型为List<BaseClass>
,并且我想避免将它们重构为List<? extends BaseClass>
之类的内容,因为这会阻塞现有的add()之类的操作。列出对象。
我知道List<BaseClass>
和List<ChildClass>
不是协变的。
但是,使用下面的技术,我可以将List<ChildClass>
投射到List<BaseClass>
,但我对它在幕后的发生方式以及是否这样做是正确的感到困惑。
使用显式强制转换:
List<BaseClass> b = new ArrayList<>();
List<ChildClass> c = new ArrayList<>();
b = c; // gives compilation error
b = (List<BaseClass>) c; // gives compilation error
b = (List<BaseClass>)(List<?>) c; // this works
使用泛型:
public List<ChildClass> getChildClassList() {
// code to fetch/generate list and return
}
public List<BaseClass> getBaseClassList() {
// code to fetch/generate list and return
}
public static <T extends BaseClass> List<T> getList() {
if(something) {
List<ChildClass> c = getChildClassList();
return (List<T>) c; // this cast is required here.
} else {
List<BaseClass> b = getBaseClassList();
return (List<T>) b;
}
}
List<BaseClass> = getList(); // This works perfectly fine but,
// I dont understand how casting is happening here.
// Also with cast to List<T> inside getList how is the final return type determined?
使用通配符:
public List<ChildClass> getChildClassList() {
// code to fetch/generate list and return
}
public List<BaseClass> getBaseClassList() {
// code to fetch/generate list and return
}
public List<? extends BaseClass> getList() {
if(something) {
List<Child> c = getChildClassList();
return c;
} else {
List<Base> b = getBaseClassList();
return b;
}
}
List<BaseClass> = getList(); // Gives compilation error saying incompatible types
// Casting this, however, works fine
List<BaseClass> = (List<BaseClass>) getList();
这些感觉对我来说有点像黑客,原因可能是Java不支持直接投射。恐怕这里有一个我可能会错过的收获。
尽管这些对于我的用例来说都可以正常工作,但是我应该担心这种方法有什么暗示吗?
编写这样的代码安全吗?
编写代码是否不好?如果是,那么应该如何处理?
答案 0 :(得分:4)
我应该担心这种方法有什么影响吗?
对于一般情况,是的:您可以调用List<ChildClass> list = getList();
,最后得到一个列表,列表中列出的内容不是ChildClass
的实例。
从根本上讲,您无法做任何导致从方法安全返回不同类型的操作。最好的办法是使用通配符:
List<? extends BaseClass> list = getList();
因为该列表中的所有内容都是BaseClass
(有些味道)或为空。
然后您可以通过复制将其安全地转换为List<BaseClass>
:
List<BaseClass> list2 = new ArrayList<>(list);
请注意,如果您使用的是类似番石榴的ImmutableList
,则此“副本”可能是免费的:
ImmutableList<BaseClass> list2 = ImmutableList.copyOf(list);
尽管名称copyOf
返回参数cast,如果它是ImmutableList
的实例,因为这是一个安全的协变类型。
就将list
转换为ChildClass
而言:在编译时您无能为力。您必须将其转换为运行时检查:
// Throw an exception or something if this is false.
boolean allAreChildClass = list.stream().allMatch(e -> e == null || e instanceof ChildClass);
然后复制列表,进行投射。
List<ChildClass> list3 =
list.stream().map(ChildClass.class::cast).collect(Collectors.toList());
或者,如果可以确定list
不会在其他任何地方使用,则可以简单地进行以下铸造:
List<ChildClass> list3 = (List<ChildClass>) (List<?>) list;
但是您必须真的确保list
不在其他地方使用,否则除铸造代码外不会被其他任何东西修改。