开发者

Problem when copying array of different types using Arrays.copyOf

开发者 https://www.devze.com 2022-12-30 23:03 出处:网络
I am trying to create a method that pretty much takes anything as a parameter, and returns a concatenated string representation of the value with some delimiter.

I am trying to create a method that pretty much takes anything as a parameter, and returns a concatenated string representation of the value with some delimiter.

public static String getConcatenated(char delim, Object ...names) {
String[] stringArray = Arrays.copyOf(names, names.length, String[].class); //Exception here
return getConcatenated(delim, stringArray);
}

And the actual method

public static String getConcatenated(char delim, String ... names) {
if(names == null || names.length == 0)
    return "";

StringBuilder sb = new StringBuilder();

for(int i = 0; i < names.length; i++) {
    String n = 开发者_StackOverflow中文版names[i];
    if(n != null) {
        sb.append(n.trim());
        sb.append(delim);
    }
}

//Remove the last delim
return sb.substring(0, sb.length()-1).toString();
}

And I have the following JUnit test:

final String two = RedpillLinproUtils.getConcatenated(' ', "Shervin", "Asgari");
Assert.assertEquals("Result", "Shervin Asgari", two); //OK

final String three = RedpillLinproUtils.getConcatenated(';', "Shervin", "Asgari");
Assert.assertEquals("Result", "Shervin;Asgari", three); //OK

final String four = RedpillLinproUtils.getConcatenated(';', "Shervin", null, "Asgari", null);
Assert.assertEquals("Result", "Shervin;Asgari", four); //OK

final String five = RedpillLinproUtils.getConcatenated('/', 1, 2, null, 3, 4);
Assert.assertEquals("Result", "1/2/3/4", five); //FAIL

However, the test fails on the last part with the exception:

java.lang.ArrayStoreException at java.lang.System.arraycopy(Native Method) at java.util.Arrays.copyOf(Arrays.java:2763)

Can someone spot the error?


Since you can't store, say Integers, in a String[] array, there is no way you can just copy an array of objects into an array of Strings. You have to somehow go through .toString() on each object.

This solution would for instance work:

public static String concat(char delim, Object... objs) {
    if (objs == null || objs.length == 0) return "";
    StringBuilder sb = new StringBuilder();
    for (Object o : objs)
        sb.append(delim).append(o);
    return sb.substring(1);
}

As a side note on varargs; I doubt you need to check if objs == null. The compiler will turn a call of the from concat(",", "a", "b", "c") into concat(",", new Object[] {"a", "b", "c"), thus I can't see how objs could ever equal null. -->


The cause

You can't put an Integer in a String[]. That's why you get ArrayStoreException. Arrays.copyOf does no conversion.

From the API:

T[] copyOf(U[] original, int newLength, Class< ? extends T[]> newType)

Throws: ArrayStoreException - if an element copied from original is not of a runtime type that can be stored in an array of class newType

The ArrayStoreException API has an example:

Thrown to indicate that an attempt has been made to store the wrong type of object into an array of objects. For example, the following code generates an ArrayStoreException:

    Object x[] = new String[3];
    x[0] = new Integer(0);

See also

  • JLS 10.10 Array Store Exception

The fix

That said, you actually don't need a String... and Arrays.copyOf to a String[]. Your getConcatenated can just take Object... and it'd work just fine.

public static String getConcatenated(char delim, Object... objs) {
    if(objs == null || objs.length == 0)
        return "";

    StringBuilder sb = new StringBuilder();
    for (Object o : objs) {
        if(o != null) {
            if (sb.length() > 0) sb.append(delim);
            sb.append(o.toString().trim());
        }
    }
    return sb.toString();
}

Now you can do the following:

    System.out.println(getConcatenated(';', "Shervin", null, "Asgari", null));
    // prints "Shervin;Asgari"

    System.out.println(getConcatenated('/', 1, 2, null, 3, 4));
    // prints "1/2/3/4"

    System.out.println(getConcatenated(':', "  ", null, "boo", "boo"));
    // prints "boo:boo"

Note that this follows the intended specification in the original code, i.e. trim() and skips null.


You can only copy Strings into a String[].

System#arraycopy and Arrays#copyOf do not do any type conversions, you have to turn the objects into Strings one by one yourself, for example by calling String#valueOf (the null-safe version of Object#toString).

Why not change the method to accept Object... instead of String... and call String.valueOf(obj) on each of them.

0

精彩评论

暂无评论...
验证码 换一张
取 消