Java: как реализовать `toArray` для` Collection` - PullRequest
7 голосов
/ 25 октября 2010

Прямо сейчас у меня есть:

    public <T> T[] toArray(T[] old) {
        T[] arr = Arrays.copyOf(old, old.length + size());
        int i = old.length;
        for(E obj : this) {
            arr[i] = old.getClass().getComponentType().cast(obj);
            ++i;
        }
        return arr;
    }

(Обратите внимание, что это не соответствует контракту, как было указано axtavt.)

, где я получаю это предупреждение:

Type safety: Unchecked cast from capture#2-of ? to T

Это по-прежнему лучший / самый простой способ его реализации?Могу ли я как-то кодировать это без предупреждения?Как бы я реализовал это иначе?


Редактировать: Мое текущее решение.Во-первых, я действительно хотел, чтобы такого предупреждения не было в самом toArray.Поэтому я кодировал эти маленькие вспомогательные функции ( читайте здесь для дальнейшего обсуждения):

@SuppressWarnings("unchecked") static <T> Class<? extends T> classOf(T obj) {
    return (Class<? extends T>) obj.getClass();
}

@SuppressWarnings("unchecked") static <T> Class<? extends T> classOf(T[] array) {
    return (Class<? extends T>) array.getClass().getComponentType();
}

@SuppressWarnings("unchecked") static <T> T[] newArray(Class<T> clazz, int size) {
    return (T[]) Array.newInstance(clazz, size);
}   

Теперь моя реализация toArray выглядит следующим образом:

    public <T> T[] toArray(T[] array) { 
        int size = size();
        if (array.length < size) { 
            array = newArray(classOf(array), size);
        } else if (array.length > size) {
            array[size] = null;
        }

        int i = 0;
        for (E e : this) {
            array[i] = classOf(array).cast(e);
            i++;
        }
        return array;
    } 

Ответы [ 2 ]

8 голосов
/ 25 октября 2010

Это по-прежнему лучший / самый простой способ его реализации?Как бы я реализовал это иначе?

Это не то, что сделал Джош Блох.Посмотрите на источник AbstractCollection#toArray().Вот выдержка из JDK 1.6.0_22.

public <T> T[] toArray(T[] a) {
    // Estimate size of array; be prepared to see more or fewer elements
    int size = size();
    T[] r = a.length >= size 
        ? a 
        : (T[]) Array.newInstance(a.getClass().getComponentType(), size);
    Iterator<E> it = iterator();

    for (int i = 0; i < r.length; i++) {
        if (!it.hasNext()) { // fewer elements than expected
            if (a != r)
                return Arrays.copyOf(r, i);
            r[i] = null; // null-terminate
            return r;
        }
        r[i] = (T) it.next();
    }
    return it.hasNext() ? finishToArray(r, it) : r;
}

Исходный код доступен в src.zip файле JDK.Вы можете интегрировать его в любой приличный IDE, такой как Eclipse / IDEA / Netbeans, чтобы вы могли видеть его при открытии класса AbstractCollection.

Можно ли каким-то образом кодировать его без этого предупреждения?

Нет.Используйте @SuppressWarnings("unchecked"), если это вас беспокоит.

Тем не менее, я бы предложил расширить AbstractCollection вместо реализации Collection, если это возможно, чтобы у вас были хотя бы базовые функции, уже реализованные для вас.

5 голосов
/ 25 октября 2010

Прежде всего, если предполагается, что это реализация Collection.toArray(), она не следует контракту - вы не должны хранить старые элементы в массиве (см. javadoc ).

Правильная реализация выглядит так:

public <T> T[] toArray(T[] array) { 
    int size = size();
    if (array.length < size) { 
        // If array is too small, allocate the new one with the same component type
        array = Array.newInstance(array.getClass().getComponentType(), size);
    } else if (array.length > size) {
        // If array is to large, set the first unassigned element to null
        array[size] = null;
    }

    int i = 0;
    for (E e: this) {
        // No need for checked cast - ArrayStoreException will be thrown 
        // if types are incompatible, just as required
        array[i] = (T) e;
        i++;
    }
    return array;
} 
...