问题
There seems to be an edge case between the autoboxing and the varargs system for primitive arrays. Is there a clean (i.e. non-reflection) way around this?
Example:
public class Test {
class A<T> {
public void a(T... ts) {
System.out.println(ts.getClass().getCanonicalName() + " of " + ts[0].getClass().getCanonicalName() + " = " + Arrays.toString(ts));
}
}
public void test() {
// These all work fine - presumably all parameters are autoboxed.
new A().a(1, 2, 3);
new A().a(1.0, 2.0, 3.0);
new A().a(1L, 2L, 3L);
// You can even mix them up - which is unexpected.
new A().a(1.0f, 2.0d, 3L);
// Works fine - even though I get a "non-varargs call to varargs method with inexact argument type ..." hint.
new A().a(new Integer[]{1, 1});
// No hint - and doesn't do as intended.
new A().a(new int[]{1, 1});
// Works fine.
new A<Integer>().a(new Integer[]{1, 1});
}
public static void main(String args[]) {
try {
new Test().test();
} catch (Throwable t) {
t.printStackTrace(System.err);
}
}
}
Prints
java.lang.Object[] of java.lang.Integer = [1, 2, 3]
java.lang.Object[] of java.lang.Double = [1.0, 2.0, 3.0]
java.lang.Object[] of java.lang.Long = [1, 2, 3]
java.lang.Object[] of java.lang.Float = [1.0, 2.0, 3]
java.lang.Integer[] of java.lang.Integer = [1, 1]
java.lang.Object[] of int[] = [[I@4d815146]
java.lang.Integer[] of java.lang.Integer = [1, 1]
Notice that the int[] does not print correctly - it seems to be being boxed into an Object[]
whose first value is my int[]
. Everything else seems to box nicely.
I want the int[]
call to print correctly without breaking the others.
P.S. If you can do it by reflection, by all means post. I'd just prefer not to use it.
回答1:
Unfortunately you need to add
String text;
if (ts[0] instanceof int[])
text = Arrays.toString((int[]) ts[0]);
else
text = Arrays.toString(ts);
I suspect there is an Apache common library to do this for you, but I don't know which one.
回答2:
It prints perfectly, and there is no boxing going on. The object you are passing in is an Array of int
, which Java writes as default writes as [I@...
. Java won't look inside the Array
and try to autobox the int
s for you.
To get prettier looking printouts, you can take a look at @PeterLawrey:s answer.
回答3:
I had a hack around with Peter's idea and this now seems to work fine. It looks rather horrid but I think if I hide it away somewhere and add loads of comments about why it is necessary I may get away with it.
If anyone can find a way of not duplicating the T[] boxed = makeTArray(it.length);
by doing it in the caller I would appreciate it.
public class Test {
class A<T> {
public void a(T... ts) {
System.out.println(
ts.getClass().getCanonicalName()
+ " of "
+ ts[0].getClass().getCanonicalName()
+ " = "
+ Arrays.toString(Rebox.rebox(ts)));
}
}
public void test() {
// These all work fine - presumably all parameters are autoboxed.
new A().a(1, 2, 3);
new A().a(1.0, 2.0, 3.0);
new A().a(1L, 2L, 3L);
// You can even mix them up - which is unexpected.
new A().a(1.0f, 2.0d, 3L);
// Works fine - even though I get a "non-varargs call to varargs method with inexact argument type ..." hint.
new A().a(new Integer[]{1, 1});
// No hint - and doesn't do as intended - Does now!!
new A().a(new int[]{1, 1});
new A().a(new long[]{1, 1});
new A().a(new float[]{1, 1});
new A().a(new double[]{1, 1});
// Works fine.
new A<Integer>().a(new Integer[]{1, 1});
}
public static void main(String args[]) {
try {
new Test().test();
} catch (Throwable t) {
t.printStackTrace(System.err);
}
}
}
And the Rebox
class.
/**
* Can rebox a boxed primitive array into its Object form.
*
* Generally, if a primitive array is passed to a varargs it
* is wrapped up as the first and only component of an Object[].
*
* E.g.
*
* public void f(T... t) {};
* f(new int[]{1,2});
*
* actually ends up calling f with t an Object[1] and t[0] the int[].
*
* This unwraps it and returns the correct reboxed version.
*
* In the above example it will return an Integer[].
*
* Any other array types will be returned unchanged.
*
* @author OldCurmudgeon
*/
public class Rebox {
public static <T> T[] rebox(T[] it) {
// Default to return it unchanged.
T[] result = it;
// Special case length 1 and it[0] is primitive array.
if (it.length == 1 && it[0].getClass().isArray()) {
// Which primitive array is it?
if (it[0] instanceof int[]) {
result = rebox((int[]) it[0]);
} else if (it[0] instanceof long[]) {
result = rebox((long[]) it[0]);
} else if (it[0] instanceof float[]) {
result = rebox((float[]) it[0]);
} else if (it[0] instanceof double[]) {
result = rebox((double[]) it[0]);
} else if (it[0] instanceof char[]) {
result = rebox((char[]) it[0]);
} else if (it[0] instanceof byte[]) {
result = rebox((byte[]) it[0]);
} else if (it[0] instanceof short[]) {
result = rebox((short[]) it[0]);
} else if (it[0] instanceof boolean[]) {
result = rebox((boolean[]) it[0]);
}
}
return result;
}
// Rebox each one separately.
private static <T> T[] rebox(int[] it) {
T[] boxed = makeTArray(it.length);
for (int i = 0; i < it.length; i++) {
boxed[i] = (T) Integer.valueOf(it[i]);
}
return boxed;
}
private static <T> T[] rebox(long[] it) {
T[] boxed = makeTArray(it.length);
for (int i = 0; i < it.length; i++) {
boxed[i] = (T) Long.valueOf(it[i]);
}
return boxed;
}
private static <T> T[] rebox(float[] it) {
T[] boxed = makeTArray(it.length);
for (int i = 0; i < it.length; i++) {
boxed[i] = (T) Float.valueOf(it[i]);
}
return boxed;
}
private static <T> T[] rebox(double[] it) {
T[] boxed = makeTArray(it.length);
for (int i = 0; i < it.length; i++) {
boxed[i] = (T) Double.valueOf(it[i]);
}
return boxed;
}
private static <T> T[] rebox(char[] it) {
T[] boxed = makeTArray(it.length);
for (int i = 0; i < it.length; i++) {
boxed[i] = (T) Character.valueOf(it[i]);
}
return boxed;
}
private static <T> T[] rebox(byte[] it) {
T[] boxed = makeTArray(it.length);
for (int i = 0; i < it.length; i++) {
boxed[i] = (T) Byte.valueOf(it[i]);
}
return boxed;
}
private static <T> T[] rebox(short[] it) {
T[] boxed = makeTArray(it.length);
for (int i = 0; i < it.length; i++) {
boxed[i] = (T) Short.valueOf(it[i]);
}
return boxed;
}
private static <T> T[] rebox(boolean[] it) {
T[] boxed = makeTArray(it.length);
for (int i = 0; i < it.length; i++) {
boxed[i] = (T) Boolean.valueOf(it[i]);
}
return boxed;
}
// Trick to make a T[] of any length.
// Do not pass any parameter for `dummy`.
// public because this is potentially re-useable.
public static <T> T[] makeTArray(int length, T... dummy) {
return Arrays.copyOf(dummy, length);
}
}
来源:https://stackoverflow.com/questions/16854959/correctly-recognise-an-int-passed-as-a-varargs-parameter