正确识别作为varargs参数传递的int []

时间:2013-05-31 10:16:14

标签: java arrays variadic-functions autoboxing

对于原始数组,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。如果你能通过反思来做到这一点,一定要发布。我只是不想使用它。

3 个答案:

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

}