【问题标题】:Cast primitive type array into object array in java在java中将原始类型数组转换为对象数组
【发布时间】:2023-03-31 05:35:02
【问题描述】:

为什么我不能在 java 中做到这一点?

Object[] o = (Object[])(new int[]{0,1,2,3.14,4});

我有一个方法,它接收一个对象,然后将其表示为一个字符串,但取决于他的类型(原始、原始包装器、数组等)。当我创建单元测试时,我将一个数组作为 Object 传递,这很好,但是当我将该对象转换为 Object[] 时,我得到了 ClassCastException。 这仅发生在原始类型数组中。有什么办法可以避免这种行为?如果没有,有人可以解释一下 Java 虚拟机上出现这种行为的原因是什么。

非常感谢任何帮助。

【问题讨论】:

标签: java arrays casting primitive


【解决方案1】:

这是一个简单的单行:

Double[] objects = ArrayUtils.toObject(primitives);

你需要导入Apache commons-lang3:

import org.apache.commons.lang3.ArrayUtils;

【讨论】:

    【解决方案2】:

    在 Java 中,原始类型和引用类型是两个截然不同的世界。这反映在数组上:原始数组不是对象数组,这就是为什么不能强制转换的原因。

    这是问题中解决方案的更简单版本:

    private Object[] getArray(Object val){
        if (val instanceof Object[])
           return (Object[])val;
        int arrlength = Array.getLength(val);
        Object[] outputArray = new Object[arrlength];
        for(int i = 0; i < arrlength; ++i){
           outputArray[i] = Array.get(val, i);
        }
        return outputArray;
    }
    

    当他们有时决定向 VM 添加新的原始类型时,这仍然有效。

    当然,您可能希望始终进行复制,不仅在原始情况下,它会变得更加简单:

    private Object[] getArray(Object val){
        int arrlength = Array.getLength(val);
        Object[] outputArray = new Object[arrlength];
        for(int i = 0; i < arrlength; ++i){
           outputArray[i] = Array.get(val, i);
        }
        return outputArray;
    }
    

    当然,这不是强制转换,而是转换

    【讨论】:

    • Array.getLengthget 正是我所需要的。比检查数组是否属于给定的原始类型然后强制转换为该类型的代码要简洁得多。谢谢!
    【解决方案3】:

    不能以这种方式转换原始类型。 在您的情况下,有一个 double 值数组,原因是 3.14。 这将起作用:

        List<Object> objectList = new ArrayList<Object>();
        objectList.addAll(Arrays.asList(0,1,2,3.14,4));
    

    即使这样也有效:

    List<Object> objectList = new ArrayList<Object>();
    objectList.addAll(Arrays.asList(0,"sfsd",2,3.14,new Regexp("Test")));
    for(Object object:objectList)
    {
        System.out.println(object);
    }
    

    更新 好的,正如上面所说,没有直接的方法将原始数组转换为 Object[]。 如果你想要一个转换字符串中任何数组的方法,我可以建议这种方式

    public class CastArray {
    
        public static void main(String[] args) {
            CastArray test = new CastArray();
            test.TestObj(new int[]{1, 2, 4});
            test.TestObj(new char[]{'c', 'a', 'a'});
            test.TestObj(new String[]{"fdsa", "fdafds"});
        }
    
        public void TestObj(Object obj) {
            if (!(obj instanceof Object[])) {
                if (obj instanceof int[]) {
                    for (int i : (int[]) obj) {
                        System.out.print(i + " ");
                    }
                    System.out.println("");
                }
                if (obj instanceof char[]) {
                    for (char c : (char[]) obj) {
                        System.out.print(c + " ");
                    }
                    System.out.println("");
                }
                //and so on, for every primitive type.
            } else {
                System.out.println(Arrays.asList((Object[]) obj));
            }
        }
    }
    

    是的,为每个原始类型编写一个循环很烦人,但没有其他办法,恕我直言。

    【讨论】:

    • 但是我的方法接收到对象,不知道用户传递了什么样的对象。使用反射我可以检测它是否为数组,然后将其转换为 Object[]。有什么解决办法吗?
    • 我认为有一种更有条理的方式来编写这段代码,更“通用”。谢谢。
    【解决方案4】:

    最初由 OP 在问题本身中发布,但作为单独的答案拉到此处。


    得到 StKiller 和其他用户的回应后 我能够创建一个更通用的方法,它位于下面:

    private final Class<?>[] ARRAY_PRIMITIVE_TYPES = { 
            int[].class, float[].class, double[].class, boolean[].class, 
            byte[].class, short[].class, long[].class, char[].class };
    
    private Object[] getArray(Object val){
        Class<?> valKlass = val.getClass();
        Object[] outputArray = null;
    
        for(Class<?> arrKlass : ARRAY_PRIMITIVE_TYPES){
            if(valKlass.isAssignableFrom(arrKlass)){
                int arrlength = Array.getLength(val);
                outputArray = new Object[arrlength];
                for(int i = 0; i < arrlength; ++i){
                    outputArray[i] = Array.get(val, i);
                                }
                break;
            }
        }
        if(outputArray == null) // not primitive type array
            outputArray = (Object[])val;
    
        return outputArray;
    }
    

    您可以将数组种类传递给getArray方法,该方法将返回Object[]而不抛出ClassCastException。

    再次感谢您的所有回复。

    【讨论】:

      【解决方案5】:

      正如原发帖人在他的问题中首先指出的那样:

      我有一个方法接收一个对象,然后将它表示为一个字符串

      虽然意图是以友好的方式输出 Object 的值,但他使用 Casting 作为 mean 来达到此目的。因此,扩展 Paŭlo Ebermann 的答案,这是我使大多数对象 toString() 友好的解决方案。

      主要问题是数组,无论X 是否是原始数组,它都会递归地将任何数组X[] 转换为其对象等效的List&lt;X&gt;。如果需要,其余部分由每个特定对象的 toString() 处理。

      重要提示:假定没有循环引用!

      鉴于

      System.out.println(objectify(new double[][]{{65.5 * 15.9, 0}, {0.123456, 1}}))
      

      预期结果是

      [[1041.45, 0.0], [0.123456, 1.0]]
      

      实施

      public Object objectify(Object obj) {
          if(obj == null)
              return obj;
          Object o = obj;
          if(obj.getClass().isArray()) {
              // array
              if(obj instanceof Object[]) {
                  // array of Object
                  Object[] oI = (Object[])obj;
                  Object[] oO = new Object[oI.length];
                  for(int i = 0; i < oI.length; i++) {
                      // objectify elements
                      oO[i] = objectify(oI[i]);
                  }
                  o = Arrays.asList(oO);
              } else {
                  // array of primitive
                  int len = Array.getLength(obj);
                  Object[] out = new Object[len];
                  for(int i = 0; i < len; i++)
                      out[i] = Array.get(obj, i);
                  o = Arrays.asList(out);
              }
          }
          return o;
      }
      

      【讨论】:

        【解决方案6】:

        在 Java 8 中,您可以使用带有映射函数的 Streams 将数组转换为任何其他类型之一,如下所示:

        Foo[] fooArray = ...;
        Bar[] barArray = Arrays.stream(fooArray).map(object -> (Bar) object).toArray();
        

        假设给定的对象可以分配给您将其转换为的类型。在您的情况下,您可以将整数数组转换为对象数组,因为整数流具有不同的映射对象方式:

        Arrays.stream(intArray).mapToObj(i -> (Object) i).toArray();
        

        【讨论】:

        • 这是另一种非常相似的方法,我发现它更简洁:Arrays.stream(intArray).boxed().toArray();
        • 更好的是,如果你想要一个数组,例如Integer,而不是通用的 Object 数组:Arrays.stream(intArray).boxed().toArray(Integer[]::new);
        【解决方案7】:

        getArray 函数的另一种实现,可灵活处理原始类型:

        public static Object[] createArrayFromArrayObject(Object o) throws XYZException {
            if(!o.getClass().isArray())
                throw new XYZException("parameter is not an array");
        
            if(!o.getClass().getComponentType().isPrimitive())
                return (Object[])o;
        
            int element_count = Array.getLength(o);
            Object elements[] = new Object[element_count];
        
            for(int i = 0; i < element_count; i++){
                elements[i] = Array.get(o, i);          
            }
        
            return elements;
        }
        

        【讨论】:

          【解决方案8】:

          你可以这样做:

          int[] intArray = new int[]{0,1,2,3,14,4};
          ArrayList<MyObject> myObjArray = new ArrayList<MyObject>;
          
          for (int i = 0; i < intArray.length; i++) {
          myObjArray.set(new MyObject(intArray[i]));
          }
          

          您需要定义一个类,其中构造函数将整数参数设置为实例字段(在 MyObject 类中)。

          【讨论】:

            【解决方案9】:

            您只能转换类型为派生类型的对象,该类型作为基类型处理和传递。在相反的方向,您可以将派生类型分配给基类型:

            Object o = new String ("simple assignment");
            String s = (String) o; 
            

            对象并没有发生任何转变——它只是被揭穿为它本来的样子,它一直是的样子。

            Integer [] ia = new Integer [] {4, 2, 6};
            Object  [] oa = ia;     
            

            但是 primitive int 不是对象,因此不能将它们分配给对象数组。然而,如果可能的话,选角只会起到相反的作用。

            【讨论】:

              【解决方案10】:

              这里提供的答案都不适用于多维数组,所以这是我的通用包装器。它适用于任何类型和任何维度的数组,还可以正确处理空引用(包括深隐藏的引用)。

              import java.lang.reflect.Array;
              import java.util.Arrays;
              
              public class ArrayWrapper {
                  private static Object[] deepWrap(Class<?> componentClass, Object a) {
                      if (a == null)
                          return null;
                      Object[] result = (Object[])Array.newInstance(componentClass, Array.getLength(a));
                      if (componentClass.isArray()) {
                          Class<?> innerCompClass = componentClass.getComponentType();
                          Object[] b = (Object[])a;
                          Arrays.parallelSetAll(result, i -> deepWrap(innerCompClass, b[i]));
                      } else
                          Arrays.setAll(result, i -> Array.get(a, i));
                      return result;
                  }
              
                  public static Object[] wrap(Object a) {
                      if (a == null)
                          return null;
                      Class<?> c = a.getClass();
                      if (!c.isArray())
                          throw new IllegalArgumentException("Not an array");
                      int dimension = 0;
                      do {
                          dimension++;
                          c = c.getComponentType();
                      } while (c.isArray());
                      if (!c.isPrimitive())
                          return (Object[])a;
                      c = Array.get(Array.newInstance(c, 1), 0).getClass();
                      while (--dimension > 0)
                          c = Array.newInstance(c, 0).getClass();
                      return deepWrap(c, a);
                  }
              }
              

              使用演示:

              public static void main(String[] args) {
                  final int size1 = 5;
                  final int size2 = 4;
                  int[][] b = new int[size1][size2];
                  for (int i = 0; i < size1; i++)
                      for (int j = 0; j < size2; j++)
                          b[i][j] = (i + 1) * (j + 1);
                  b[1] = new int[7];
                  b[1][4] = 4;
                  b[3] = null;
                  System.out.println(b.getClass().getCanonicalName());
                  System.out.println(Arrays.deepToString(b));
                  Integer[][] w = (Integer[][])wrap(b);
                  System.out.println(w.getClass().getCanonicalName());
                  System.out.println(Arrays.deepToString(w));
              }
              

              【讨论】:

                【解决方案11】:

                原始类型不是对象。没有变通方法,您可以将任何包含对象的数组强制转换为 Object[],但不能将包含原始类型的数组。

                【讨论】:

                • 您不能直接投射,但如果您知道预期的数据,您可以创建新的类来相应地适应它。
                • 当然,但它不再投射了。
                • 你说“没有变通办法”,我只是说……总有变通办法。
                • 问题是关于选角的,我没办法。我假设 OP 足够聪明,可以将 int[] 数组转换为 Integer[] 数组。看来你没有。
                • 您不能将 int 强制转换为 Object,这就是问题所在。不是整数。简单的解决方案,使用 Java 类。对于将整个数据块放入对象的设计来说,这种方式更可取。
                猜你喜欢
                • 2020-12-19
                • 2010-10-08
                • 1970-01-01
                • 2014-09-03
                • 2015-12-09
                • 2021-09-09
                • 2013-01-12
                • 2011-10-03
                相关资源
                最近更新 更多