如何将List<Object>转换为List<MyClass>?

137

这段代码无法编译,有什么建议可以提供吗?

 ...
  List<Object> list = getList();
  return (List<Customer>) list;

编译器报错:无法将 List<Object> 强制转换为 List<Customer>

17个回答

186

您可以通过先向Object进行向上转型,然后将任何对象转换为任何类型。在您的情况下:

(List<Customer>)(Object)list; 

在运行时,您必须确保列表中只包含Customer对象。

批评者认为这种类型转换表明您的代码存在问题;您应该能够调整类型声明以避免它。但是Java泛型太复杂了,而且它并不完美。有时你就是不知道是否有一个漂亮的解决方案来满足编译器,尽管你非常清楚运行时类型,而且你知道你正在尝试做的事情是安全的。在这种情况下,只需进行必要的粗略转换,这样您就可以把工作留给家里了。


15
这也需要 @SuppressWarnings("unchecked")。请注意,您还可以将其向上转型为 (List) 而不是 (Object) - 200_success

41

根据您的其他代码,最佳答案可能会有所不同。 尝试:

List<? extends Object> list = getList();
return (List<Customer>) list;
或者
List list = getList();
return (List<Customer>) list;

但需要记住不建议进行这样的未经检查的转换。


3
这是一个非常不好的习惯,除非你使用“Unchecked”注释,否则编译器仍会抱怨。 - kdgregory

41

使用Java 8的Streams

有时候粗暴地强制类型转换是可以的:

List<MyClass> mythings = (List<MyClass>) (Object) objects

但是这里有一个更加多才多艺的解决方案:

List<Object> objects = Arrays.asList("String1", "String2");

List<String> strings = objects.stream()
                       .map(element->(String) element)
                       .collect(Collectors.toList());

有很多好处,但其中一个是如果你不能确定它包含什么,你可以更优雅地转换你的列表:

objects.stream()
    .filter(element->element instanceof String)
    .map(element->(String)element)
    .collect(Collectors.toList());

4
那更像是复制而不是铸造。 - 200_success
被接受的答案中提到的强制转换对我不起作用。此外,我使用的是Java 7。但是Guava的FluentIterable对我有用。 - Sridhar Sarnobat
这就是我一直在寻找的,只是不知道语法。 - Fueled By Coffee
这对我不起作用。我收到错误java.lang.ClassCastException: class java.lang.Object无法转换为类my.package.MyInterface。java.lang.Object在引导加载器的java.base模块中;my.package.MyInterface在org.springframework.boot.devtools.restart.classloader.restartClassLoader加载器的未命名模块中 - cd491415

37

这是因为尽管一个客户 一个对象,但是一组客户 不是 一组对象。如果是这样的话,那么就可以将 任何 对象放入客户列表中。


3
不是这样的。如果允许上述操作,那么您将绕过类型安全性。请注意,类型转换并不意味着创建一个新列表并复制其中的项目。它意味着将单个实例处理为不同类型,因此您将获得一个包含潜在的非Customer对象的列表,并且具有类型安全性保证其不应该这样做。这与Java本身无关。既然您认为这个特性让Java停留在黑暗时代,我向您发出挑战,请解释一下您认为应该如何工作。 - Lasse V. Karlsen
1
@BrainSlugs83 您需要的是(List<Customer>)(Object)list; - Muthu Ganapathy Nathan
@LasseV.Karlsen 我不是在谈论强制转换,而是在谈论转换。这不会规避类型安全性,而是强制执行它。Java的泛型实现、缺乏运算符重载、扩展方法和许多其他现代便利设施让我感到非常失望。与此同时,.NET有两个单独的扩展方法——一个叫做.Cast<T>(),另一个叫做.OfType<T>()。前者对每个元素执行转换(抛出所需的异常),而后者则过滤掉无法转换的元素(因此您可以根据使用场景选择其中之一)。 - BrainSlugs83
1
@EAGER_STUDENT 我不会排除 Java 可能真的可以这样工作(所有这些荒谬的类型擦除业务,我必须尝试一下...)-- 但是不,我永远不会写那样的代码 -- 你失去了类型安全性,如果集合中的一个元素不是 Customer 的 instanceof,会发生什么? - BrainSlugs83
2
@BrainSlugs83,提问者特别询问了有关类型转换的问题。如果Java没有像你所提到的.NET方法一样的相关方法(顺便说一下,这仍然不是转换),那么你应该很容易地添加它们。但这与问题本身有些无关,问题是关于具体语法的。 - Lasse V. Karlsen

30

你可以使用双重转换。

return (List<Customer>) (List) getList();

16

另一种方法是使用Java 8的流(Stream)。

    List<Customer> customer = myObjects.stream()
                                  .filter(Customer.class::isInstance)
                                  .map(Customer.class::cast)
                                  .collect(toList());

4
谢谢,这个解决方案非常漂亮,使用了方法引用。 - thang
3
从来没想过有人会滚到那么远:D - d0x
这仅适用于过滤子句。 - noircc
滚动值得了!! - Jay Yadav

7
请注意,我不是Java程序员,但在.NET和C#中,这个特性被称为逆变或协变。由于它们是.NET 4.0中的新功能,而我没有使用beta版,所以我还没有深入研究这些内容,因此我不知道这两个术语中哪一个描述了您的问题,但让我描述一下这个技术问题。
假设你允许转换。请注意,我说的是转换,因为这就是你说的,但有两个操作可能是可能的,即转换强制类型转换
转换意味着你会得到一个新的列表对象,但你说的是强制类型转换,这意味着你想暂时将一个对象视为另一种类型。
这就是问题所在。
如果允许以下操作会发生什么(请注意,在强制类型转换之前,假设对象列表实际上只包含Customer对象,否则即使在这个假设版本的Java中,强制类型转换也不会起作用):
List<Object> list = getList();
List<Customer> customers = (List<Customer>)list;
list.Insert(0, new someOtherObjectNotACustomer());
Customer c = customers[0];

在这种情况下,这将尝试将一个不是客户的对象视为客户,并且您将在某个时候(从列表内部或赋值处)得到运行时错误。
然而,泛型应该为您提供类型安全的数据类型,例如集合,因为它们喜欢抛出“保证”的词,所以这种带有随之而来问题的转换是不允许的。
在.NET 4.0中(我知道,你的问题是关于Java的),这将被允许在一些非常特定的情况下,其中编译器可以保证您进行的操作是安全的,但在一般情况下,这种类型的转换将不被允许。对于Java也是如此,尽管我不确定是否有任何计划将协变和逆变引入Java语言。
希望比我更了解Java的人能告诉您Java未来或实现的具体情况。

3
Java的未来是Scala。说真的,那个将泛型添加到Java的家伙已经开发了一种新语言,它与Java有些相似,但在类型方面非常非常高效:它具有非常彻底和一致的类型处理实现。我认为没有人确切地知道哪些Scala特性会流回Java,以及何时会流回。 - Carl Smotricz
一个非常出色的协方差解释,真正回答了OP的问题。做得好。 - Kevin Day
卡尔:我想一些Java开发人员已经开始创建C#了吧? :) 无论如何,未来Java很可能会朝着Scala的方向发展,而不是朝着某些弱类型语言的方向。 - Esko
@Carl - 在Scala中,有一个微妙的区别是默认情况下List是不可变的。因此,通常您不会遇到将对象添加到客户列表的问题,因为这样做会得到一个新的对象列表。 - Brian Agnew
嗯...从技术上讲这是正确的 -- 但即使在 .NET 4.0 之前 -- 你也可以使用常规的 IEnumerable 扩展方法 (.Cast<> 和 .OfType<>) 来实现这一点 -- 因此,如果你只想要强类型迭代,就没有必要走得太极端。 - BrainSlugs83
从技术上讲,那是正确的,但那不会给你一个 List<T>,这正是 OP 寻求帮助的。你不能做 OP 要求的事情,但显然如果你可以接受不是 List<T> 的东西,那么有一些变通方法。 - Lasse V. Karlsen

4
您可以像这样做:
List<Customer> cusList = new ArrayList<Customer>();

for(Object o: list){        
    cusList.add((Customer)o);        
}

return cusList; 

或者使用Java 8的方法

list.stream().forEach(x->cusList.add((Customer)x))

return cuslist;

3
你只需要遍历列表并逐个将对象转换即可。

2
你可以创建一个新的列表并将元素添加到其中:
例如:
List<A> a = getListOfA();
List<Object> newList = new ArrayList<>();
newList.addAll(a);

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接