Проблема с копированием универсального массива - PullRequest
0 голосов
/ 21 марта 2011

Цель


Я создаю класс Java, который предоставит расширенные возможности использования массивов, таких как методы add, remove и contains. Я подумал, что лучшее решение - создать класс (называемый ArrayPP) с параметром типа T. Таким образом, пользователь может взаимодействовать с объектом ArrayPP так же легко, как и с массивом того же типа.

Задача


Я быстро обнаружил, что такие методы, как add, потребуют создания отдельного массива и в конечном итоге преобразуют целевой массив t из массива T s в массив Object s. Как вы можете догадаться, это полностью разрушает удобство использования, и когда я пытаюсь сделать что-то вроде

File[] f = new File[0];
ArrayPP<File> appF = new ArrayPP(f);
appF.add(saveFile);
f = appF.toArray();

программа выбрасывает

Exception in thread "main" java.lang.ClassCastException: [Ljava.lang.Object; cannot be cast to [Ljava.io.File;

потому что метод add должен изменить массив на массив Object s, так как компилятор Java не позволит вам создать универсальный массив (T[] t = new T[0]; это плохо, но T[] t = (T[]) new Object[0]; нормально) , Из построчной отладки я знаю, что приведенный выше код сохраняет массив t, в данном случае, как n-массив из File s, пока не будет вызвана 4-я строка метода add. У кого-нибудь есть решение, при котором массив t будет массивом T с, а не массивом Object с?

Пример кода


Ниже представлена ​​ОЧЕНЬ разбавленная версия моего класса.

public class ArrayPP<T>
{
  T[] t;

  /**
   * Creates a new Array++ to manage the given array.
   * <h3>Analogy:</h3>
   * <tt>ArrayPP&lt;String&gt; s = new ArrayPP(args);</tt><br/>
   * is analogous to<br/>
   * <tt>String s[] = args;</tt>
   * @param array The array to be managed
   */
  public ArrayPP(T[] array)
  {
    t = array;
  }

  /**
   * Appends a value to the end of the array
   * @param val the value to be appended
   * @return the resulting array.
   */
  public ArrayPP add(T val)
  {
    T[] temp = (T[]) new Object[t.length + 1];
    System.arraycopy(t, 0, temp, 0, t.length);
    temp[temp.length - 1] = val;
    t = (T[])temp;
    return this;
  }

  /**
   * Returns the array at the core of this wrapper
   * @return the array at the core of this wrapper
   */
  public T[] toArray()
  {
    return t;
  }
}

Возможное решение?


Посмотрев на другие вопросы об универсальных массивах, я думаю, у меня есть решение:

вместо

  /**
   * Appends a value to the end of the array
   * @param val the value to be appended
   * @return the resulting array.
   */
  public ArrayPP add(T val)
  {
    T[] temp = (T[]) new Object[t.length + 1];
    System.arraycopy(t, 0, temp, 0, t.length);
    temp[temp.length - 1] = val;
    t = (T[])temp;
    return this;
  }

это будет работать?

  /**
   * Appends a value to the end of the array
   * @param val the value to be appended
   * @return the resulting array.
   */
  public ArrayPP<T> add(T val)
  {
    t = java.util.Arrays.copyOf(t, t.length + 1);
    t[t.length - 1] = val;
    return this;
  }

Ответы [ 3 ]

2 голосов
/ 21 марта 2011

В принципе, вы не можете легко создавать массивы универсального типа (или переменной типа).

Если у вас есть объект класса, вы можете использовать отражение или, если у вас есть пример массива, методы класса java.util.Arrays для создания (более длинной / короткой) копии. Но в любом случае это не элегантно.

Класс ArrayList внутренне просто использует Object[] для хранения своих элементов и конвертирует только при get / set / add / toArray. Что бы ваш класс делал лучше, чем ArrayList?


Edit:

Я бы порекомендовал либо просто делегировать ArraysList, либо выполнить реализацию, как это делает ArrayList, используя Object[] внутри, и конвертировать в выходные данные, где это необходимо.

Если вы действительно хотите иметь массив правильного типа внутри, это возможно - но, как я уже сказал, это выглядит ужасно.

Метод add по-прежнему является самым простым:

  /**
   * Appends a value to the end of the array
   * @param val the value to be appended
   * @return the resulting array.
   */
  public ArrayPP add(T val)
  {
     T[] temp = Arrays.copyOf(t, t.length+1);
     temp[t.length] = val;
     t = temp;
     return this;
  }

Если вы хотите добавить в середину или удалить, вам придется объединить это с вашим массивом.

1 голос
/ 21 марта 2011

Есть ли причина, по которой встроенный класс List<T> не может делать то, что вам нужно?Как в:

String[] theArray = {"a", "b", "c"};
List<String> theList = Arrays.asList(theArray);
0 голосов
/ 21 марта 2011
public ArrayPP(T[] array)
    componentClass = array.getClass().getComponentClass();

T[] newArray(int length)
    return Array.newInstance(componentClass, length)
...