【发布时间】:2019-12-25 00:59:35
【问题描述】:
我有一个这样的类结构:
public class BaseClass {
}
public class ChildClass extends BaseClass {
}
假设我有一个返回 List<BaseClass> 的方法 getList :
public List<BaseClass> getList() {
List<BaseClass> b = new ArrayList<>();
return b;
}
List<BaseClass> b = getList();
我想让上述方法 getList() 通用,以便它可以根据某些条件返回 List<BaseClass> 或 List<ChildClass> 类型的对象,并将返回的值分配给 @987654326 类型的对象仅限@。这样做的原因是我正在处理一些遗留代码,其中对象的类型为 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 不支持直接转换。恐怕这里有一个我可能会遗漏的问题。
虽然这些对我的用例来说很好用,但我应该担心这种方法有什么含义吗?
写这样的代码安全吗?
这是一种糟糕的代码编写方式吗?如果是,那么应该如何处理?
【问题讨论】:
-
“使用泛型”(和“使用通配符”):在这两种情况下,您都返回一个空列表,因此您可以简单地将方法主体替换为
return new ArrayList<>();。两个列表实例之间没有区别。 -
这只是为了说明。实际上,这些列表将包含定义列表类型的对象。
-
列表元素从何而来?
-
我更新了代码。希望这能让它更清楚。不管它们来自哪里,最后,我们都只会有任何一种类型的列表。
-
“因为这会阻止现有的操作,比如对这些列表对象的 add() 操作”——这正是泛型的意义所在。当您的
List<? extends BaseClass>实际上是List<ChildClass>时,您不得添加不是ChildClass的BaseClass实例。正确的没有“未经检查”警告的通用代码将不允许您这样做。最终将引用从List<ChildClass>转换为List<BaseClass>的任何类型转换序列都感觉很老套,因为它们是 hacky。这条规则只有一个例外,List<BaseClass> a = Collections.unmodifiableList(b);猜猜为什么……
标签: java generics collections casting wildcard