如何将List <Object>强制转换为List <MyClass>


Answers:


156

您始终可以通过将对象首先上载到Object来将其转换为任何类型。在您的情况下:

(List<Customer>)(Object)list; 

您必须确保在运行时该列表仅包含Customer对象。

批评家说,这种强制转换表明您的代码有问题;您应该能够调整类型声明以避免这种情况。但是Java泛型太复杂了,也不是完美的。有时,即使您非常了解运行时类型,并且知道您尝试执行的操作是安全的,但有时您只是不知道是否有满足该编译器的漂亮解决方案。在这种情况下,只需根据需要进行粗铸,即可将工作留在家中。


8
这也需要@SuppressWarnings("unchecked")。请注意,您也可以将(List)改为而不是(Object)
200_success '16

36

这是因为尽管客户对象,但客户列表不是对象列表。如果是这样,则可以将任何对象放入“客户”列表中。


2
不,不是。如果允许上述情况,您将规避类型安全。请注意,强制转换并不意味着创建新列表并复制项目。这意味着将单个实例作为不同的类型处理,因此您将拥有一个包含可能包含非客户对象的列表,并具有不应该的类型安全保证。这样与java无关。由于您认为此功能使Java陷入了黑暗时代,因此我向您挑战,解释一下您认为这应该如何工作。
Lasse V. Karlsen 2014年

1
@ BrainSlugs83满足您的需要(List <Customer>)(Object)list;
Muthu Ganapathy Nathan,2014年

@ LasseV.Karlsen我不是在谈论演员,我是在谈论转换。它不会绕过类型安全,而是会强制执行。Java的泛型实现,缺少运算符重载,扩展方法以及许多其他现代便利性使我感到非常失望。-同时,.NET为此有两种独立的扩展方法-一种叫做.Cast<T>(),一种叫做.OfType<T>()。前者对每个元素执行强制转换(引发所需的异常),而后者过滤掉无法强制转换的元素(因此您将根据使用情况选择一个)。
BrainSlugs83 2014年

1
@EAGER_STUDENT我不会把它放在实际上可能有效的Java之上(所有这些荒谬的类型擦除业务,我都必须尝试一下……)-但是,我永远不会那样写代码-你输了类型安全性,如果集合中的一个元素不是instanceof客户,会发生什么?
BrainSlugs83

1
@ BrainSlugs83问这个问题的人专门问了有关铸造的问题。而且,如果Java还没有像您所引用的.NET方法那样的相关方法(仍然没有转换btw),那么您应该可以轻松地添加它们。不过,这全都与眼前的问题有关,后者询问了特定的语法。
Lasse V. Karlsen 2014年

35

最佳答案取决于您的其他代码。尝试:

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

要么

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

但是请记住,不建议进行这种未经检查的演员表。


3
-1-这是一个非常不好的习惯,除非您使用“未经检查的”注释,否则编译器仍然会抱怨它
kdgregory

24

使用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());

1
不过,这更像是复制而不是演员。
200_success '16

被接受的分析工具中提到的演员表对我不起作用。我也使用Java7。但是Guava FluentIterable为我工作。
Sridhar Sarnobat

这就是我一直在寻找的内容,我只是不知道其语法
加油由


8

请注意,我不是Java程序员,但是在.NET和C#中,此功能称为逆方差或协方差。我还没有深入研究这些东西,因为它们是.NET 4.0中的新功能,因为它只是beta,所以我没有使用它,所以我不知道这两个术语中的哪个描述了您的问题,但让我描述与此相关的技术问题。

假设您被允许演员。注意,我说的是cast,因为这就是您所说的,但是可能有两种操作,即castconversion

转换将意味着您将获得一个新的列表对象,但您说的是强制转换,这意味着您要将一个对象临时视为另一种类型。

这就是问题所在。

如果允许以下操作会发生什么(请注意,我假设在强制转换之前,对象列表实际上仅包含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中。
卡尔·斯莫特里兹

协方差的极好解释,可以真正回答OP的问题。做得好。
凯文·戴

卡尔:我以为有些Java开发人员会继续创建C#?:)无论如何,Java在将来很有可能会朝着Scala的方向发展,而不是像一些不那么强类型的东西。
Esko

@Carl-在Scala中有一个细微的区别,即默认情况下列表是不可变的。因此,通常来说,将对象添加到“客户”列表中不会有问题,因为执行此操作时,您会得到一个对象列表。
Brian Agnew

呃...从技术上讲这是正确的-但即使在.NET 4.0之前-您也可以使用常规的IEnumerable扩展方法(.Cast <>和.OfType <>)来完成此操作-因此,如果您只需要强类型迭代。
BrainSlugs83 2014年

7

另一种方法是使用Java 8流。

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

1
谢谢您,使用方法参考,该解决方案非常漂亮
thang

1
没想到有人会向下滚动那么远:D
d0x


3

你可以做这样的事情

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;

2

您不能因为List<Object>List<Customer>不在同一个继承树中。

您可以将新的构造函数添加到使用的List<Customer>类中,List<Object>然后遍历将每个类型强制转换Object为a Customer并将其添加到集合中的列表。请注意,如果调用方的List<Object>内容不是,则可能发生无效的强制转换异常Customer

通用列表的重点是将它们限制为某些类型。您试图获取一个可以包含任何内容(订单,产品等)的列表,然后将其压缩到一个只能容纳客户的列表中。


2

您可以创建一个新列表并向其中添加元素:

例如:

List<A> a = getListOfA();
List<Object> newList = new ArrayList<>();
newList.addAll(a);

1

最好的选择是创建一个new List<Customer>,遍历List<Object>,将每个项目添加到新列表中,然后返回。


1

正如其他人指出的那样,您不能保存它们的类型,因为a List<Object>不是a List<Customer>。您可以做的是在列表上定义一个进行就地类型检查的视图。使用Google收藏夹可以:

return Lists.transform(list, new Function<Object, Customer>() {
  public Customer apply(Object from) {
    if (from instanceof Customer) {
      return (Customer)from;
    }
    return null; // or throw an exception, or do something else that makes sense.
  }
});

1

与上面的博zh相似。您可以通过以下方法在此处执行一些解决方法(尽管我自己不喜欢):

public <T> List<T> convert(List list, T t){
    return list;
}

是。它将您的列表转换为所需的通用类型。

在上面给定的情况下,您可以执行以下代码:

    List<Object> list = getList();
    return convert(list, new Customer());

我喜欢这个解决方案。即使您需要添加SuppressWarnings,也最好将它添加到一个位置,而不是在每个不安全的转换中添加它。
罗布森

1

根据您要对列表执行的操作,甚至可能不需要将其强制转换为List<Customer>。如果只想将Customer对象添加到列表中,则可以如下声明:

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

这是合法的(嗯,不仅合法,而且是正确的 -列表是“给客户的某种超类型”),如果您要将其传递给仅向列表添加对象的方法,则可以使用上面的方法通用界限就足够了。

另一方面,如果您想从列表中检索对象并将其强类型键入为“客户”,那么您就不走运了,这是正确的。因为列表是一个列表List<Object>,所以不能保证其中的内容是客户,因此您必须在检索时提供自己的演员表。(或者说真的,绝对,绝对要确保该列表仅包含Customers并使用其他答案中的一个双播,但要意识到您完全避开了从泛型中获得的编译时类型安全性。案件)。

广义上讲,考虑编写方法时可以接受的最广泛的泛型界限总是很好的,如果将其用作库方法,则加倍。如果您只想从列表中读取内容,请使用List<? extends T>而不是List<T>,例如-这将使调用者可以传入的参数具有更大的作用域,这意味着它们不太可能遇到与您所遇到的类似的可避免问题在这里。

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.