输入选角列表<ChildClass> 到列表<BaseClass>
Type Casting List<ChildClass> to List<BaseClass>
我有一个这样的 class 结构 :
public class BaseClass {
}
public class ChildClass extends BaseClass {
}
假设我有一个方法 getList returns List<BaseClass>
:
public List<BaseClass> getList() {
List<BaseClass> b = new ArrayList<>();
return b;
}
List<BaseClass> b = getList();
我想使上述方法 getList() 通用,以便它可以 return 类型的对象 List<BaseClass>
或 List<ChildClass>
基于某些条件和 仅将 returned 值分配给 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();
这些对我来说有点像 hack,可能是因为 Java 不支持直接转换。恐怕这里有一个问题我可能会遗漏。
尽管这些对我的用例来说效果很好,但我应该担心这种方法有什么影响吗?
写这样的代码安全吗?
写代码的方式不好吗?如果是,那么应该如何处理?
is there any implication with this approach that I should worry about?
对于一般情况,是的:您可以调用 List<ChildClass> list = getList();
,最后得到一个列表,其中包含不是 ChildClass
.
实例的内容
从根本上说,您不能做任何导致从方法安全返回不同类型的事情。你能做的最好的就是通配符:
List<? extends BaseClass> list = getList();
因为该列表中的所有内容要么是 BaseClass
(某种风格),要么是 null。
然后您可以通过复制将其安全地转换为 List<BaseClass>
:
List<BaseClass> list2 = new ArrayList<>(list);
请注意,如果您使用的是类似 Guava 的 ImmutableList
,此 "copy" 可能是免费的:
ImmutableList<BaseClass> list2 = ImmutableList.copyOf(list);
尽管名称,[=21=] returns 参数,如果它是 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
要么没有在其他地方使用,要么不会被除转换代码以外的任何其他内容修改。
我有一个这样的 class 结构 :
public class BaseClass {
}
public class ChildClass extends BaseClass {
}
假设我有一个方法 getList returns List<BaseClass>
:
public List<BaseClass> getList() {
List<BaseClass> b = new ArrayList<>();
return b;
}
List<BaseClass> b = getList();
我想使上述方法 getList() 通用,以便它可以 return 类型的对象 List<BaseClass>
或 List<ChildClass>
基于某些条件和 仅将 returned 值分配给 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();
这些对我来说有点像 hack,可能是因为 Java 不支持直接转换。恐怕这里有一个问题我可能会遗漏。
尽管这些对我的用例来说效果很好,但我应该担心这种方法有什么影响吗?
写这样的代码安全吗?
写代码的方式不好吗?如果是,那么应该如何处理?
is there any implication with this approach that I should worry about?
对于一般情况,是的:您可以调用 List<ChildClass> list = getList();
,最后得到一个列表,其中包含不是 ChildClass
.
从根本上说,您不能做任何导致从方法安全返回不同类型的事情。你能做的最好的就是通配符:
List<? extends BaseClass> list = getList();
因为该列表中的所有内容要么是 BaseClass
(某种风格),要么是 null。
然后您可以通过复制将其安全地转换为 List<BaseClass>
:
List<BaseClass> list2 = new ArrayList<>(list);
请注意,如果您使用的是类似 Guava 的 ImmutableList
,此 "copy" 可能是免费的:
ImmutableList<BaseClass> list2 = ImmutableList.copyOf(list);
尽管名称,[=21=] returns 参数,如果它是 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
要么没有在其他地方使用,要么不会被除转换代码以外的任何其他内容修改。