在Java中将基本类型数组转换为对象数组

44

为什么我不能在Java中这样做?

Object[] o = (Object[])(new int[]{0,1,2,3.14,4});
我有一个方法,它接收一个对象并将其表示为字符串,但取决于其类型(原始类型,原始类型包装器,数组等...)。当我创建一个单元测试时,我将一个数组作为Object传递进去是没问题的,但是当我将该对象强制转换为Object[]时,我会得到ClassCastException
只有原始类型数组才会发生这种情况。有没有办法避免这种行为?如果没有,有人能解释一下Java虚拟机中这种行为的原因吗?
非常感谢任何帮助。

4
顺便提一下,数字3.14不是整数。 - Alex
我将您的解决方案从您的问题中移出,并放入了一个社区维基下面的答案。如果您想自己发布该答案以赚取一些声望,请告诉我,我会删除 CW 的那个答案。 - Duncan Jones
可能是重复问题: https://dev59.com/b2865IYBdhLWcg3whfBf - Ciro Santilli OurBigBook.com
11个回答

36

这里有一个简单的一行代码:

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

您需要导入Apache commons-lang3

import org.apache.commons.lang3.ArrayUtils;

35
在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;
}

当他们有时决定向虚拟机添加新的基本类型时,这仍将起作用。

当然,您可能希望始终进行复制,而不仅仅是在基本类型情况下,那么它会变得更加简单:

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

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


2
Array.getLengthget正是我所需要的。这比检查数组是否为给定的原始类型,然后将其转换为该类型要干净得多。谢谢! - Jan Aagaard Meier

7

这是原始问题中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异常。
再次感谢您的所有回复。

6

原始类型无法以这种方式转换。 在您的情况下,存在由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[]**。有没有任何解决方法? - aumanets
我认为有一种更有组织的方式来编写这段代码,更“通用”。谢谢。 - aumanets

2

使用Java 8,您可以使用映射函数将流应用于数组,使其转换为任何其他类型,例如:

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

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

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

2
这里有另一种非常相似的方法,我发现它更加简洁:Arrays.stream(intArray).boxed().toArray(); - Flaom
3
如果您想要一个整数类型的数组,而不是一个通用的Object数组: Arrays.stream(intArray).boxed().toArray(Integer[]::new); - Lambart

2

正如原帖中所述:

我有一个接收对象并将其表示为字符串的方法

虽然目的是以友好的方式输出Object的值,但他使用了强制转换作为实现这一目的的一种手段。因此,在扩展Paŭlo Ebermann的答案之上,这里是我解决方案,使大多数对象都可以友好地使用toString()

主要问题在于数组,它会递归地将任何数组X[]转换为其等效的List<X>对象,无论X是否是基本类型。如果需要,每个具体对象的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;
}

1

另一种实现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;
}

0

你可以这样做:

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类的实例字段。

0

你只能将派生类型的对象强制转换为基础类型进行处理和传递。相反地,你可以将派生类型直接赋值给基础类型:

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

这里没有对象的转换 - 它只是被揭示为它本来就是的东西。

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

但是原始整数不是对象,因此它们不能分配给对象数组。然而,如果可能的话,强制转换只会在相反方向起作用。


0

这里呈现的所有答案都不能处理多维数组,所以我提供了一个通用的封装器。它适用于任何类型和任何维度的数组,并且可以正确地处理空引用(包括深度隐藏的引用)。

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

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