2

原始数组的自动装箱和可变参数系统之间似乎存在边缘情况。有没有一种干净(即不反射)的方式来解决这个问题?

例子:

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[]其第一个值为 my int[]。其他一切似乎都很好。

我希望int[]调用正确打印而不会破坏其他调用。

PS如果您可以通过反思来做到这一点,请务必发布。我只是不想使用它。

4

3 回答 3

2

不幸的是,您需要添加

String text;
if (ts[0] instanceof int[])
    text = Arrays.toString((int[]) ts[0]);
else
    text = Arrays.toString(ts);

我怀疑有一个 Apache 通用库可以为您执行此操作,但我不知道是哪一个。

于 2013-05-31T10:18:34.067 回答
1

它打印完美,并且没有拳击。您传入的对象一个 Array int,Java 将其默认写入[I@.... Java 不会查看内部Array并尝试int为您自动装箱 s。

要获得更漂亮的打印输出,您可以查看@PeterLawrey:s answer。

于 2013-05-31T10:19:31.083 回答
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);
  }

}
于 2013-05-31T11:33:46.280 回答