对于原始数组,autoboxing和varargs系统之间似乎存在边缘情况。这周围有干净的(即非反射)方式吗?
示例:
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);
}
}
}
打印
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]
请注意,int []无法正确打印 - 它似乎被装入Object[]
,其第一个值是我的int[]
。其他一切似乎都很好。
我希望int[]
调用正确打印而不会破坏其他调用。
P.S。如果你能通过反思来做到这一点,一定要发布。我只是不想使用它。
答案 0 :(得分:2)
不幸的是你需要添加
String text;
if (ts[0] instanceof int[])
text = Arrays.toString((int[]) ts[0]);
else
text = Arrays.toString(ts);
我怀疑有一个Apache公共库可以为你做这个,但我不知道哪一个。
答案 1 :(得分:1)
它完美打印,并且没有拳击正在进行。您在中传递的对象是一个int
的数组,Java将其写为默认写入为[I@...
。 Java不会查看Array
内部,并尝试为您自动装箱int
。
为了获得更漂亮的打印输出,你可以看一下@PeterLawrey的答案。
答案 2 :(得分:0)
我对彼得的想法有所了解,现在这似乎工作得很好。它看起来相当可怕,但我想如果我将它隐藏在某个地方,并添加大量关于为什么有必要的评论,我可以侥幸逃脱。
如果有人能通过在来电者中找到一种不复制T[] boxed = makeTArray(it.length);
的方法,我将不胜感激。
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);
}
}
}
Rebox
班。
/**
* 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);
}
}