【问题标题】:How to cast List<Object> to List<MyClass>如何将 List<Object> 转换为 List<MyClass>
【发布时间】:2009-12-16 21:23:10
【问题描述】:

这不会编译,任何建议表示赞赏。

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

编译器说:不能将List&lt;Object&gt; 转换为List&lt;Customer&gt;

【问题讨论】:

    标签: java generics casting


    【解决方案1】:

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

    (List<Customer>)(Object)list; 
    

    您必须确保在运行时列表只包含客户对象。

    批评者说这样的转换表明你的代码有问题;你应该能够调整你的类型声明来避免它。但是Java泛型太复杂了,并不完美。有时您只是不知道是否有一个很好的解决方案可以满足编译器的要求,即使您非常了解运行时类型并且知道您正在尝试做的事情是安全的。这种情况下,只要根据需要进行粗铸,就可以下班回家了。

    【讨论】:

    • 这也需要@SuppressWarnings("unchecked")。请注意,您也可以向上转换为 (List) 而不是 (Object)
    【解决方案2】:

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

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

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

    但请记住,不建议进行这种未经检查的强制转换。

    【讨论】:

    • -1 - 这是一个非常糟糕的习惯,除非你使用“Unchecked”注释,否则编译器仍然会抱怨它
    【解决方案3】:

    这是因为虽然客户是一个对象,但客户列表不是对象列表。如果是,那么您可以将 any 对象放入客户列表中。

    【讨论】:

    • 不,不是。如果允许上述情况,您将绕过类型安全。请注意,强制转换并不意味着创建新列表并复制项目。这意味着将单个实例作为不同的类型处理,因此您将拥有一个列表,其中包含潜在的非客户对象,并具有不应该的类型安全保证。这与java本身无关。由于您认为此功能使 Java 陷入了黑暗时代,因此我挑战您解释您认为它应该如何工作。
    • @BrainSlugs83 满足您的需求 (List)(Object)list;
    • @LasseV.Karlsen 我不是在谈论选角,而是在谈论转换。它不会规避类型安全,而是会强制执行它。 Java 对泛型的实现、缺乏运算符重载、扩展方法和许多其他现代便利设施让我非常失望。 -- 同时,.NET 对此有两种独立的扩展方法——一种称为.Cast&lt;T&gt;(),另一种称为.OfType&lt;T&gt;()。前者对每个元素执行强制转换(抛出所需的异常),而后者过滤掉无法强制转换的元素(因此您可以根据您的使用场景选择一个)。
    • @EAGER_STUDENT 我不会把它放在可能实际工作的 Java 之上(所有这些荒谬的类型擦除业务,我必须尝试它......) - 但不,我永远不会编写这样的代码——你失去了类型安全,如果集合中的一个元素不是instanceofCustomer,会发生什么?
    • @BrainSlugs83 提出问题的人专门询问了关于选角的问题。如果 Java 还没有像您引用的 .NET 方法这样的相关方法(顺便说一句,它仍然没有转换),那么您应该可以轻松地添加它们。不过,这与手头的问题有点正交,后者询问了具体的语法。
    【解决方案4】:

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

    【讨论】:

    • 不过,这与其说是演员表,不如说是抄袭。
    • 接受的分析器中提到的演员表对我不起作用。我也在使用 Java 7。但是 Guava 的 FluentIterable 对我有用。
    • 这是我要找的,我只是不知道语法
    • 这对我不起作用。我收到错误 java.lang.ClassCastException:类 java.lang.Object 无法转换为类 my.package.MyInterface。 java.lang.Object 在加载器引导的模块 java.base 中; my.package.MyInterface 位于加载器 org.springframework.boot.devtools.restart.classloader.restartClassLoader 的未命名模块中
    【解决方案5】:

    您可以使用双重转换。

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

    【讨论】:

      【解决方案6】:

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

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

      【讨论】:

      • 谢谢,这个方案太漂亮了,使用方法参考
      • 没想到有人会向下滚动那么远:D
      【解决方案7】:

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

      假设您被允许施放。请注意,我说的是 cast,因为这就是你所说的,但有两种操作是可能的,castingconverting

      转换意味着你得到了一个新的列表对象,但你说的是转换,这意味着你想暂时把一个对象当作另一种类型。

      这就是问题所在。

      如果允许以下情况会发生什么(注意,我假设在转换之前,对象列表实际上只包含 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 未来或实现的细节。

      【讨论】:

      • Java 的未来是...... Scala。说真的,将泛型绑定到 Java 上的人开发了一种新语言,它与 Java 有点相似,但非常非常精通类型:类型处理的非常彻底和一致的实现。我认为没有人确切地知道哪些 Scala 特性会回归 Java,以及何时回归。
      • 对协方差的出色解释,真正回答了 OP 的问题。干得好。
      • Carl:我认为一些 Java 开发人员继续创建 C#? :) 无论如何,Java 很可能在未来朝着 Scala 的方向发展,而不是诸如强类型的东西。
      • @Carl - 在 Scala 中有一个细微的差别,默认情况下列表是不可变的。所以一般来说,你不会遇到将对象添加到客户列表的问题,因为当你这样做时,你会得到一个 Objectsnew 列表。
      • 呃...从技术上讲这是正确的——但即使在 .NET 4.0 之前——你也可以使用常规的 IEnumerable 扩展方法(.Cast 和 .OfType)来做到这一点——所以没有如果您只想要强类型迭代,则需要深入研究。
      【解决方案8】:

      您应该只遍历列表并一个一个地转换所有对象

      【讨论】:

        【解决方案9】:

        你可以这样做

        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;
        

        【讨论】:

          【解决方案10】:

          你不能因为List&lt;Object&gt;List&lt;Customer&gt; 不在同一个继承树中。

          您可以向您的List&lt;Customer&gt; 类添加一个新的构造函数,该类接受List&lt;Object&gt;,然后遍历列表,将每个Object 转换为Customer,并将其添加到您的集合中。请注意,如果调用者的 List&lt;Object&gt; 包含不是 Customer 的内容,则可能会发生无效转换异常。

          泛型列表的意义在于将它们限制为特定类型。您正在尝试获取一个可以包含任何内容的列表(订单、产品等),然后将其压缩到一个只能包含客户的列表中。

          【讨论】:

            【解决方案11】:

            正如其他人指出的那样,你不能保存它们,因为 List&lt;Object&gt; 不是 List&lt;Customer&gt;。你可以做的是在列表上定义一个视图来进行就地类型检查。使用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.
              }
            });
            

            【讨论】:

              【解决方案12】:

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

              例如:

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

              【讨论】:

                【解决方案13】:

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

                【讨论】:

                  【解决方案14】:

                  与上面的博卓类似。你可以通过这个方法在这里做一些解决方法(虽然我自己不喜欢它):

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

                  是的。它会将您的列表转换为所需的泛型类型。

                  在上面给定的情况下,你可以做一些这样的代码:

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

                  【讨论】:

                  • 我喜欢这个解决方案。即使您需要添加 SuppressWarnings,最好将其添加到一个位置,而不是在每个不安全的强制转换中添加。
                  【解决方案15】:

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

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

                  这是合法的(嗯,不仅合法,而且正确 - 该列表是“客户的某些超类型”),如果您要将它传递给一个方法只需将对象添加到列表中,那么上述通用边界就足够了。

                  另一方面,如果您想从列表中检索对象并将它们强类型化为客户 - 那么您就不走运了,这是正确的。因为列表是List&lt;Object&gt;,所以不能保证内容是客户,所以您必须在检索时提供自己的演员表。 (或者真的,绝对地,加倍确保该列表将仅包含 Customers 并使用来自其他答案之一的双重转换,但请意识到您完全规避了您从中获得的编译时类型安全在这种情况下是泛型)。

                  从广义上讲,在编写方法时考虑尽可能广泛的通用范围总是好的,如果将其用作库方法,则更是如此。如果您只打算从列表中读取,请使用 List&lt;? extends T&gt; 而不是 List&lt;T&gt;,例如 - 这为您的调用者提供了更多更多的参数范围,他们可以传入并意味着它们是不太可能遇到与您在这里遇到的类似的可避免问题。

                  【讨论】:

                    【解决方案16】:

                    使用最简单的解决方案

                    (((List<Object>)(List<?>) yourCustomClassList))
                    

                    【讨论】:

                      【解决方案17】:
                      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;
                      }
                      

                      【讨论】:

                        猜你喜欢
                        • 1970-01-01
                        • 1970-01-01
                        • 2020-11-27
                        • 1970-01-01
                        • 2016-05-01
                        • 1970-01-01
                        • 2023-03-08
                        • 1970-01-01
                        • 1970-01-01
                        相关资源
                        最近更新 更多