Muk*_*del 2 java generics collections casting wildcard
我有一个像这样的类结构:
public class BaseClass {
}
public class ChildClass extends BaseClass {
}
Run Code Online (Sandbox Code Playgroud)
可以说我有一个方法getList返回List<BaseClass>:
public List<BaseClass> getList() {
List<BaseClass> b = new ArrayList<>();
return b;
}
List<BaseClass> b = getList();
Run Code Online (Sandbox Code Playgroud)
我想使上述方法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
Run Code Online (Sandbox Code Playgroud)
使用泛型:
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?
Run Code Online (Sandbox Code Playgroud)
使用通配符:
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();
Run Code Online (Sandbox Code Playgroud)
在我看来,这些感觉有点像黑客,因为Java不支持直接投射。恐怕这里可能没有我想要的东西。
尽管这些对于我的用例来说很好用,但是我应该担心这种方法有什么影响吗?
编写这样的代码安全吗?
这是编写代码的不好方法吗?如果是,那应该如何处理?
我应该担心这种方法有什么影响吗?
对于一般情况,是的:您可以调用List<ChildClass> list = getList();,最后得到一个列表,其中包含不是实例的所有内容ChildClass。
从根本上讲,您不能做任何导致从方法安全地返回不同类型的操作。最好的办法是使用通配符:
List<? extends BaseClass> list = getList();
Run Code Online (Sandbox Code Playgroud)
因为该列表中的所有内容都会是BaseClass(某种意义上的)或为null。
然后,您可以List<BaseClass>通过复制将其安全地转换为:
List<BaseClass> list2 = new ArrayList<>(list);
Run Code Online (Sandbox Code Playgroud)
请注意,如果您使用的是Guava's之类的东西ImmutableList,此“副本”可能是免费的:
ImmutableList<BaseClass> list2 = ImmutableList.copyOf(list);
Run Code Online (Sandbox Code Playgroud)
尽管有名称,copyOf但如果参数是cast的实例,则返回参数cast ImmutableList,因为这是安全的协变强制转换。
就转换list为ChildClass:而言,在编译时您无能为力。您必须将其转换为运行时检查:
// Throw an exception or something if this is false.
boolean allAreChildClass = list.stream().allMatch(e -> e == null || e instanceof ChildClass);
Run Code Online (Sandbox Code Playgroud)
然后复制列表,执行转换。
List<ChildClass> list3 =
list.stream().map(ChildClass.class::cast).collect(Collectors.toList());
Run Code Online (Sandbox Code Playgroud)
或者,如果可以确定list不会在其他任何地方使用,则可以简单地进行以下转换:
List<ChildClass> list3 = (List<ChildClass>) (List<?>) list;
Run Code Online (Sandbox Code Playgroud)
但是您必须真正确定它list不会在其他地方使用,或者除铸造代码外不会被其他任何东西修改。