简体   繁体   English

如何投射列表<Object>列出<MyClass>

[英]How to cast List<Object> to List<MyClass>

This does not compile, any suggestion appreciated.这不会编译,任何建议表示赞赏。

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

Compiler says: cannot cast List<Object> to List<Customer>编译器说:无法将List<Object>List<Customer>

you can always cast any object to any type by up-casting it to Object first.您始终可以通过先将任何对象向上转换为 Object 来将其转换为任何类型。 in your case:在你的情况下:

(List<Customer>)(Object)list; 

you must be sure that at runtime the list contains nothing but Customer objects.您必须确保在运行时列表只包含 Customer 对象。

Critics say that such casting indicates something wrong with your code;批评者说这样的转换表明你的代码有问题; you should be able to tweak your type declarations to avoid it.你应该能够调整你的类型声明来避免它。 But Java generics is too complicated, and it is not perfect.但是Java泛型太复杂了,还不够完美。 Sometimes you just don't know if there is a pretty solution to satisfy the compiler, even though you know very well the runtime types and you know what you are trying to do is safe.有时你只是不知道是否有一个很好的解决方案来满足编译器,即使你非常了解运行时类型并且你知道你正在尝试做什么是安全的。 In that case, just do the crude casting as needed, so you can leave work for home.在这种情况下,只需根据需要进行粗铸,这样您就可以将工作留在家中。

That's because although a Customer is an Object, a List of Customers is not a List of Objects.那是因为尽管客户是一个对象,但客户列表不是对象列表。 If it was, then you could put any object in a list of Customers.如果是,那么您可以将任何对象放入客户列表中。

Depending on your other code the best answer may vary.根据您的其他代码,最佳答案可能会有所不同。 Try:尝试:

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

or

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

But have in mind it is not recommended to do such unchecked casts.但请记住,不建议进行此类未经检查的强制转换。

With Java 8 Streams :使用 Java 8

Sometimes brute force casting is fine:有时蛮力铸造很好:

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

But here's a more versatile solution:但这里有一个更通用的解决方案:

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

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

There's a ton of benefits, but one is that you can cast your list more elegantly if you can't be sure what it contains:有很多好处,但其中之一是,如果您不能确定列表包含的内容,则可以更优雅地投射列表:

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

您可以使用双重演员。

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

Another approach would be using a java 8 stream.另一种方法是使用 java 8 流。

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

Note that I am no java programmer, but in .NET and C#, this feature is called contravariance or covariance.请注意,我不是 Java 程序员,但在 .NET 和 C# 中,此功能称为逆变或协变。 I haven't delved into those things yet, since they are new in .NET 4.0, which I'm not using since it's only beta, so I don't know which of the two terms describe your problem, but let me describe the technical issue with this.我还没有深入研究这些东西,因为它们是 .NET 4.0 中的新东西,我没有使用它,因为它只是测试版,所以我不知道这两个术语中的哪一个描述了您的问题,但让我描述一下技术问题。

Let's assume you were allowed to cast.假设您被允许进行投射。 Note, I say cast , since that's what you said, but there are two operations that could be possible, casting and converting .请注意,我说的是cast ,因为这就是您所说的,但是有两种可能的操作, castconvert

Converting would mean that you get a new list object, but you say casting, which means you want to temporarily treat one object as another type.转换意味着您获得一个新的列表对象,但您说的是强制转换,这意味着您想暂时将一个对象视为另一种类型。

Here's the problem with that.这就是问题所在。

What would happen if the following was allowed (note, I'm assuming that before the cast, the list of objects actually only contain Customer objects, otherwise the cast wouldn't work even in this hypothetical version of java):如果允许以下情况会发生什么(注意,我假设在转换之前,对象列表实际上只包含 Customer 对象,否则即使在这个假设的 java 版本中,转换也不起作用):

List<Object> list = getList();
List<Customer> customers = (List<Customer>)list;
list.Insert(0, new someOtherObjectNotACustomer());
Customer c = customers[0];

In this case, this would attempt to treat an object, that isn't a customer, as a customer, and you would get a runtime error at one point, either form inside the list, or from the assignment.在这种情况下,这将尝试将不是客户的对象视为客户,并且您会在某一时刻收到运行时错误,无论是在列表中的形式还是从分配中。

Generics, however, is supposed to give you type-safe data types, like collections, and since they like to throw the word 'guaranteed' around, this sort of cast, with the problems that follow, is not allowed.然而,泛型应该为您提供类型安全的数据类型,例如集合,并且由于它们喜欢抛出“保证”这个词,因此这种类型的转换以及随之而来的问题是不允许的。

In .NET 4.0 (I know, your question was about java), this will be allowed in some very specific cases , where the compiler can guarantee that the operations you do are safe, but in the general sense, this type of cast will not be allowed.在 .NET 4.0 中(我知道,你的问题是关于 java),这在一些非常特殊的情况下是允许的编译器可以保证你所做的操作是安全的,但在一般意义上,这种类型的转换不会被允许。 The same holds for java, although I'm unsure about any plans to introduce co- and contravariance to the java language. Java 也是如此,尽管我不确定是否有任何计划将协变和逆变引入 Java 语言。

Hopefully, someone with better java knowledge than me can tell you the specifics for the java future or implementation.希望有比我更好的 Java 知识的人可以告诉您 Java 未来或实现的细节。

您应该遍历列表并一个一个地投射所有对象

You can do something like this你可以做这样的事情

List<Customer> cusList = new ArrayList<Customer>();

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

return cusList; 

Or the Java 8 way或者 Java 8 方式

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

return cuslist;

You can't because List<Object> and List<Customer> are not in the same inheritance tree.你不能,因为List<Object>List<Customer>不在同一个继承树中。

You could add a new constructor to your List<Customer> class that takes a List<Object> and then iterate through the list casting each Object to a Customer and adding it to your collection.您可以向List<Customer>类添加一个新的构造函数,该类接受List<Object> ,然后遍历列表,将每个Object转换为Customer并将其添加到您的集合中。 Be aware that an invalid cast exception can occur if the caller's List<Object> contains something that isn't a Customer .请注意,如果调用方的List<Object>包含不是Customer内容,则可能会发生无效的强制转换异常。

The point of generic lists is to constrain them to certain types.泛型列表的重点是将它们限制为某些类型。 You're trying to take a list that can have anything in it (Orders, Products, etc.) and squeeze it into a list that can only take Customers.您正在尝试获取一个可以包含任何内容(订单、产品等)的列表,并将其压缩到一个只能包含客户的列表中。

As others have pointed out, you cannot savely cast them, since a List<Object> isn't a List<Customer> .正如其他人指出的那样,您无法安全地投射它们,因为List<Object>不是List<Customer> What you could do, is to define a view on the list that does in-place type checking.您可以做的是在列表上定义一个进行就地类型检查的视图。 Using Google Collections that would be:使用Google Collections将是:

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.
  }
});

You can create a new List and add the elements to it:您可以创建一个新列表并向其中添加元素:

For example:例如:

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

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

Similar with Bozho above.类似于上面的Bozho。 You can do some workaround here (although i myself don't like it) through this method :你可以通过这个方法在这里做一些解决方法(虽然我自己不喜欢它):

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

Yes.是的。 It will cast your list into your demanded generic type.它会将您的列表转换为您所需的通用类型。

In the given case above, you can do some code like this :在上面给定的情况下,您可以执行以下代码:

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

Depending on what you want to do with the list, you may not even need to cast it to a List<Customer> .根据您想对列表做什么,您甚至可能不需要将其转换为List<Customer> If you only want to add Customer objects to the list, you could declare it as follows:如果您只想将Customer对象添加到列表中,您可以如下声明:

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

This is legal (well, not just legal, but correct - the list is of "some supertype to Customer"), and if you're going to be passing it into a method that will merely be adding objects to the list then the above generic bounds are sufficient for this.这是合法的(嗯,不仅合法,而且是正确的- 该列表是“客户的某个超类型”),如果您要将其传递给一个仅将对象添加到列表的方法,则上述内容通用边界就足够了。

On the other hand, if you want to retrieve objects from the list and have them strongly typed as Customers - then you're out of luck, and rightly so.另一方面,如果您想从列表中检索对象并将它们强类型化为客户 - 那么您就不走运了,这是正确的。 Because the list is a List<Object> there's no guarantee that the contents are customers, so you'll have to provide your own casting on retrieval.因为列表是一个List<Object>不能保证内容是客户,所以你必须在检索时提供你自己的转换。 (Or be really, absolutely, doubly sure that the list will only contain Customers and use a double-cast from one of the other answers, but do realise that you're completely circumventing the compile-time type-safety you get from generics in this case). (或者真的,绝对,双重确保该列表将只包含Customers并使用其他答案之一的双重转换,但要意识到您完全规避了从泛型中获得的编译时类型安全这种情况)。

Broadly speaking it's always good to consider the broadest possible generic bounds that would be acceptable when writing a method, doubly so if it's going to be used as a library method.从广义上讲,在编写方法时考虑可接受的最广泛的泛型边界总是好的,如果将其用作库方法,则更是如此。 If you're only going to read from a list, use List<? extends T>如果您只想从列表中读取,请使用List<? extends T> List<? extends T> instead of List<T> , for example - this gives your callers much more scope in the arguments they can pass in and means they are less likely to run into avoidable issues similar to the one you're having here. List<? extends T>而不是List<T> - 这为您的调用者提供更多他们可以传入的参数范围,并且意味着他们不太可能遇到类似于您在这里遇到的问题的可避免问题。

最简单的解决方案是使用

(((List<Object>)(List<?>) yourCustomClassList))
List<Object[]> testNovedads = crudService.createNativeQuery(
            "SELECT ID_NOVEDAD_PK, OBSERVACIONES, ID_SOLICITUD_PAGO_FK FROM DBSEGUIMIENTO.SC_NOVEDADES WHERE ID_NOVEDAD_PK < 2000");

Convertir<TestNovedad> convertir = new Convertir<TestNovedad>();
Collection<TestNovedad> novedads = convertir.toList(testNovedads, TestNovedad.class);
for (TestNovedad testNovedad : novedads) {
    System.out.println(testNovedad.toString());
}

public Collection<T> toList(List<Object[]> objects, Class<T> type) {
    Gson gson = new Gson();
    JSONObject jsonObject = new JSONObject();
    Collection<T> collection = new ArrayList<>();
    Field[] fields = TestNovedad.class.getDeclaredFields();
    for (Object[] object : objects) {
        int pos = 0;
        for (Field field : fields) {
            jsonObject.put(field.getName(), object[pos++]);
        }
        collection.add(gson.fromJson(jsonObject.toString(), type));
    }
    return collection;
}

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM