Копирование полей между аналогичными классами в java

У меня есть пары классов, где поля одного являются подмножеством полей другого, а геттеры классов надмножеств все предположительно называются (getFoo()). Есть ли способ эффективно копировать все общие поля из класса superset в класс подмножества или, по крайней мере, автоматически генерировать код для этого.

Я должен отметить, что:

  • По разным причинам я не могу редактировать классы надмножеств, а также не могу использовать их во всем, чтобы избежать копирования данных.
  • Я могу потенциально создать новые методы в классах подмножеств, но я не могу изменить их поля.
  • У нас есть десятки этих пар, и некоторые из классов имеют много полей, поэтому делать это вручную громоздко, если не сказать больше.
  • Сотрудник разработал метод создания универсального метода копирования, который использует java-отражение для принятия любых двух классов, итерации по полям как строки, выполняет строчную манипуляцию, чтобы определить имя получателя, а затем выполнить его для автоматического набора поле в классе подмножества. Это ужасно, но, похоже, это работает. Я действительно надеюсь, что там будет лучший способ.

Изменить: некоторый простой код по запросу

public class SuperClass {
  private int foo;
  private int bar;
  private float bat;
  public int getFoo() { return foo; }
  public int getBar() { return bar; }
  public float getBat() { return bat; }
}

public class SubClass {
  private int foo;
  private float bat;
}

//wanted
public static copySuperFieldsToSubMethod(Object super, Object sub) { ??? }

// also acceptable would be some way to autogenerate all the assignment 
// functions needed

Ответ 1

Для этого можно использовать класс BeanUtils в Spring Framework. Возможно, это не обязательно будет более эффективным, чем ваш метод, основанный на отражении, но он, безусловно, прост в кодировании. Я ожидаю, что все, что вам нужно сделать, это:

BeanUtils.copyProperties(source, target);

Javadoc для этого метода доступен в http://static.springsource.org/spring/docs/3.0.x/javadoc-api/org/springframework/beans/BeanUtils.html#copyProperties(java.lang.Object,%20java.lang.Object)

Если это не подходит, вы можете также рассмотреть возможность использования BeanWrapper/BeanWrapperImpl в Spring Framework для повторения свойств ваших классов. Это было бы проще, чем использование API-интерфейсов с низким уровнем отражения.

Ответ 3

Если вы хотите эффективно выполнять задачу (с точки зрения производительности во время выполнения), то способ кодирования копии с использованием геттеров и сеттеров - это путь. Если в методах getter или setter не существует чего-то напуганного, их тела будут встраиваться так, чтобы они были такими же быстрыми, как и задания полей.

Отражательный подход (например, с использованием существующего класса, такого как BeanUtils), является менее кодирующим, но, вероятно, на порядок медленнее, чем вызов геттеров и сеттеров простым способом. Если вы попытаетесь реализовать это самостоятельно, вы можете столкнуться с большей работой, чем вы рассчитывали, особенно если ваш рефлексивный класс/метод копирования должен справляться с перегруженными методами, наследованием, преобразованием значений, боксированием/распаковкой и т.д.

При подходе к генерации кода вам необходимо сбалансировать усилия и сложность внедрения генерации кода (используя любую технологию, которую вы выберете) по сравнению с попыткой написать методы копирования вручную. Вероятно, вы, вероятно, не столкнетесь с подходом к генерации кода до 20 классов... и еще много, если вы не знакомы с технологией.

Ответ 4

Чтобы скопировать на основе полей, а не геттеров и сеттеров, вы можете использовать Spring ReflectionUtils.shallowCopyFieldState().

Ответ 5

Я бы написал простой инструмент java для автоматического генерации исходного кода для классов, которые могут заполнять поля подмножеств с общими полями из надмножества. Этот инструмент будет использовать отражение, чтобы получить имена методов getter и setter. Остальные являются (тривиальными) строковыми операциями для "записи" исходного файла в память и сохранения его в файл *.java. Скомпилируйте все эти автоматически созданные файлы и добавьте файлы классов в путь к классам.

Класс может выглядеть следующим образом:

class AClassToBClassPopulator implements Populator {
   @Overwrite
   public void populate(Object superSet, Object subSet) {
      subSet.setFieldA(superSet.getFieldA());
      subSet.setFieldB(superSet.getFieldB());
      // .. and so on. The method body is created through reflection
   }
}

Ответ 6

Можете ли вы предоставить пример кода из вашего приложения, в котором показан сценарий, упомянутый в вашем сообщении? Прямо сейчас отражение кажется лучшим, поскольку оно позволяет вам проверять членов класса во время выполнения.

Ответ 7

Это, очевидно, задача для отражения Java, и в то время как другие уже предложили правильные, хотя, возможно, несколько тяжеловесных решений, здесь еще одно:

Примерно год назад я написал небольшую библиотеку модификаторов свойств JavaBean под названием BeanPropertyController. Хотя я специально не рекомендую его кому-либо, я думаю, что тезисный класс библиотеки (см. Источник) можно использовать в качестве ссылки использовать аналогичные функции для ваших нужд. В качестве быстрого примера, здесь, как я использовал BPC (почти!), Что вы спрашиваете:

// somewhere in code...
SuperClass a = new SuperClass();
a.foo = 101;
a.bar = 102;
a.bat = 103f;

SubClass b = new SubClass();
b.foo = 201;
b.bat = 202f;

BeanPropertyController fromB = BeanPropertyController.of(b, ExtractionDepth.QUESTIMATE);
BeanPropertyController toA = BeanPropertyController.of(a, ExtractionDepth.QUESTIMATE);

// This is where the magic happens:
for (String propertyName : fromB.getPropertyNames()) {
    toA.mutate(propertyName, fromB.access(propertyName));
}
a = (SuperClass) toA.getObject();
b = (SubClass) fromB.getObject();

System.out.println("SuperClass' foo="+a.foo+" bar="+a.bar+" bat="+a.bat);
System.out.println("SubClass' foo="+b.foo+" bat="+b.bat);

Отпечатает

SuperClass' foo=201 bar=102 bat=202.0
SubClass' foo=201 bat=202.0

Итак, я предлагаю вам перейти к URL-адресу, который я связал, и адаптировать этот фрагмент кода к вашим потребностям. Я совершенно уверен, что вам не нужны различные методы создания экземпляров, поставщики стоимости по умолчанию и т.д., Которые я включил. И да, BPC можно считать устаревшим.