Построение массива наследников Java и ковариантный возврат - PullRequest
3 голосов
/ 23 ноября 2011

(Название звучит слишком причудливо, но я не смог найти совпадение, поэтому будем надеяться, что оно наглядно.)

У меня есть рабочий фрагмент кода:

@SuppressWarnings("unchecked")
public static Copyable[] copyOf(Copyable[] objects, Class type) {
    Copyable[] copies = Arrays.copyOf(objects, objects.length, type);
    for( int i=0; i < copies.length; ++i ) {
        if( objects[i] == null )
            throw new IllegalArgumentException("Copyable[] contains null reference", new NullPointerException());
        copies[i] = objects[i].copy();
    }

    return copies;
}

Не так красиво, как хотелось бы, так как я должен передать класс массива, но он работает для того, что я хочу: выделить новый массив копируемого разработчика и заполнить его, используя реализованный метод.

Проблема, с которой я столкнулся, состоит в том, что это компилируется GWT, чья библиотека эмуляции JRE указывает на правильную Java, которую он не реализовал.Мне нужно сделать это без вызова Arrays.copyOf () и, надеюсь, без размышлений.

Примечание: я работаю над версией clone (), но на самом деле тоже не хочу полагаться на clone ().Я чувствую, что должно быть более чистое решение.

Ответы [ 5 ]

2 голосов
/ 23 ноября 2011

Как насчет этого:

public static Copyable[] copyOf(Copyable[] objects) {
    Copyable[] copies = new Copyable[objects.length];
    for( int i=0; i < copies.length; ++i ) {
        if( objects[i] == null )
            throw new IllegalArgumentException("Copyable[] contains null reference", new NullPointerException());
        copies[i] = objects[i].copy();
    }
    return copies;
}

Нет необходимости на самом деле копировать массив objects в copies перед копированием самих элементов.

EDIT

Если ваши копируемые объекты являются сериализуемыми, вы можете просто сериализовать и затем десериализовать массив для создания копии. В gwt-test-utils есть служебная функция DeepCopy.java , которая может делать именно то, что вам нужно.

1 голос
/ 23 ноября 2011

Возможно с Generics:

public interface Copyable<T> {
  T copy();
}



import java.lang.reflect.Array;
import java.util.ArrayList;
import java.util.List;

public class Test {
  static class My implements Copyable<My> {
    int x;

    public My copy() {
      My my = new My();
      my.x = this.x;
      return my;
    }
  }

  @SuppressWarnings("unchecked")
  public static <T extends Copyable<T>> T[] copyOf(T[] objects, Class<T> type) {
    List<T> copies = new ArrayList<T>();
    for (int i = 0; i < objects.length; ++i) {
      if (objects[i] == null) throw new IllegalArgumentException("Copyable[] contains null reference",
          new NullPointerException());
      copies.add((T) objects[i].copy());
    }
    T typeVar = null;
    try {
      typeVar = (T) Class.forName(type.getName()).newInstance();
    } catch (InstantiationException e) {
      e.printStackTrace();
    } catch (IllegalAccessException e) {
      e.printStackTrace();
    } catch (ClassNotFoundException e) {
      e.printStackTrace();
    }
    Object t = Array.newInstance(typeVar.getClass(),0);
    return copies.toArray((T[])t); 
  }

  public static void main(String[] args) {
    My[] stuff = new My[1];
    My elem = new My();
    elem.x = 1;
    stuff[0] = elem;
    My[] copies = copyOf(stuff, My.class);
    System.out.println(copies[0].x);
  }
}
1 голос
/ 23 ноября 2011

Почему ты не можешь просто сделать: Copyable[] copies = new Copyable[objects.length];? Эта строка выделяет массив, а затем вы уже заполняете его включенным циклом for.

0 голосов
/ 24 ноября 2011

Вы можете положиться на clone() для самого массива, а затем использовать copy() для каждого объекта:

@SuppressWarnings("unchecked")
public static Copyable[] copyOf(Copyable[] objects) {
    Copyable[] copies = (Copyable[]) objects.clone();
    for( int i=0; i < copies.length; ++i ) {
        if( objects[i] == null )
            throw etc;
        copies[i] = objects[i].copy();
    }
    return copies;
}

В каждом массиве реализован метод clone (), который возвращает поверхностную копию массива. Поэтому я думаю, что он достаточно чистый для ваших нужд.

Обратите внимание, что в этом случае вам также не нужен аргумент Class type.

0 голосов
/ 24 ноября 2011

Опираясь на Ответ GriffeyDog и немного веселья, которое я недавно получил с генериками , вот решение без Class объектов, которое работает непосредственно с массивами:

@SuppressWarnings("unchecked")
public static <T extends Copyable> T[] copyOf(T[] objects) {
    T[] copies = (T[]) new Object[objects.length];
    for (int i = 0; i < copies.length; ++i) {
        if (objects[i] == null)
            throw new IllegalArgumentException(/*...*/);
        copies[i] = (T) objects[i].copy();
    }
    return copies;
}
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...