我无法将List of Fruit转换为List中包含的Fruit子类。
public class Response {
private List<Fruit> mFruitList;
public List<Fruit> getFruitList() {
return mFruitList;
}
}
public class Fruit {
}
public class Orange extends Fruit {
}
List<Fruit> oranges = response.getFruitList();
如何投射橙子以使其成为橙类列表?这是一个糟糕的设计模式吗?基本上我从服务器获取JSON响应是一个水果列表。对于Web服务的每个特定调用,我知道Fruit将获得什么子类,因此我需要适当地转换该List。
答案 0 :(得分:7)
如果您知道每个特定的调用,您将获得什么子类的Fruit,那么您应该使用泛型而不是强制列表。
public class Response<T extends Fruit> {
private List<T> mFruitList;
public List<T> getFruitList() {
return mFruitList;
}
}
Response<Orange> response = // create
List<Orange> oranges = response.getFruitList();
编辑:通过模板我的意思是泛型类型。对不起,我现在有太多的C ++
答案 1 :(得分:3)
拼写背后的整个想法是能够告诉编译器,“嘿,我比你更了解这个。”在您的代码中,编译器无法安全地将List<Fruit>
向下转换为List<Orange>
,因为它无法知道列表在运行时将包含的内容。
如果你绝对肯定该列表只有Orange
个实例,并且它使你的代码更易于管理,那就去吧。
List<Orange> oranges = (List<Orange>) response.getFruitList();
当然,编译器会给你一个警告,因为你正在做一些它认为你不应该做的事情。只要知道如果你错了就扔掉CastClassException
,JVM可能会笑到最后!
答案 2 :(得分:1)
将泛型想象为列表可以包含哪些类型的对象的门。因为这种继承和转换不会以你期望的方式工作。在您给出的示例中,您可以将橘子和苹果放在
中。如果列表同时包含苹果和橙子,您如何将其转换为List<Fruit>
List<Fruit>
。
如果您需要
,那么为什么还要打扰List<Orange>
List<Orange>
。无论如何你都明确地投射它并且你确切地知道它包含它可能是一个不必要的抽象。
如果您正在使用API,则无法更改,但您确切知道它包含的内容,那么您应该使用instanceof检查进行循环,以确保并明确地将每个
实例强制转换为{{1}当你需要List<Orange>
List<Orange>
API时。
答案 3 :(得分:0)
您应该强制转换List,并测试每个元素是否为instanceof Orange
,以及在Oranges中进行测试后。这是“最好的实践”。
答案 4 :(得分:0)
给出
List<Fruit> getFruits() {...}
你不能强制转换
List<Orange> oranges = (List<Orange>) getFruits();
由于类型擦除,在运行时getFruits的类型只是List。编译器甚至不允许你进行向下转换(我有疑问,所以我在回答之前在Eclipse中尝试过)。
你可以告诉编译器你的列表将包含一些Fruit的子类,在这种情况下,你需要在你的方法上使用通配符:
List<? extends Fruit> getFruits() {...}
然后演员阵容成为可能,但带有类型安全警告:
@SuppressWarnings("unchecked")
List<Orange> oranges = (List<Orange>) getFruits();
鉴于getFruits 的运行时类型是 List,您只需丢弃泛型类型信息并使用不安全的分配:
@SuppressWarnings("unchecked")
List<Orange> oranges = (List) getFruits();
可能是一种更优雅的方式,因为它清楚地表明了你的意图,尽管需要更多的系统资源:
List<Orange> oranges = Arrays.asList((Orange[])getFruits().toArray())
Java中的数组在运行时保留它们的类型信息,因此从编译器的角度看,转换是有效且“安全”的,但是如果你在水果篮中传递了一些苹果,它就会抛出运行时异常。