13

I have pairs of classes where the fields of one is a subset of the fields of another and the getters of the superset classes are all predictably named (getFoo()). Is there some way to efficiently copy all the common fields over from the superset class to the subset class, or at least auto-generate the code to do so.

I should note that:

  • For various reasons, I can't edit the superset classes, nor can I just use them throughout to avoid having to do the data copy.
  • I can potentially create new methods in the subset classes, but I can't change their fields.
  • We have dozens of these pairs, and some of the classes have many many fields so doing this by hand is unwieldy to say the least.
  • A colleague has come up with a method to create a generic copy method that uses java reflection to take any two classes, iterate through the fields as Strings, do string manipulation to determine the getter name, and then execute it to automatically set the field in the subset class. It's awful, but it appears to work. I'm really hoping there's a better way.

Edit: some simple code as requested

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
Dusty
  • 2,283
  • 2
  • 20
  • 24
  • If you could edit the superset classes you could simply make them extend your subset classes and inherit all their fields and methods in classic OO fashion. Why can't you edit the superset classes? – Asaph Dec 09 '10 at 05:04
  • @Asaph - sadly, the superset classes are part of an external library that was developed separately and which I now need to be able to transform into the classes created for this specific project that don't utilize much of the info in the larger ones. – Dusty Dec 09 '10 at 05:08
  • What if you turned the relationship upside down and made your subset classes inherit from the superset class? You can override any methods relating to fields you don't need with empty bodies. It's not too elegant but at least you get to re-use code without copy/paste, reflection or code generation. – Asaph Dec 09 '10 at 05:16
  • possible duplicate of [Copy all values from fields in one class to another through reflection](http://stackoverflow.com/questions/1667854/copy-all-values-from-fields-in-one-class-to-another-through-reflection) – Mark Elliot Dec 09 '10 at 18:50

7 Answers7

14

You could use the BeanUtils class in the Spring Framework to do this. It may not necessarily be any more efficient than your reflection-based technique, but it's certainly simple to code. I expect that all you would need to do is:

BeanUtils.copyProperties(source, target);

Javadoc for this method is available at http://static.springsource.org/spring/docs/3.0.x/javadoc-api/org/springframework/beans/BeanUtils.html#copyProperties(java.lang.Object,%20java.lang.Object)

If that doesn't suit, you could also consider using BeanWrapper / BeanWrapperImpl in the Spring Framework to iterate through the properties of your classes. That would be simpler than using low-level reflection APIs.

gutch
  • 6,959
  • 3
  • 36
  • 53
  • 1
    Wow, that's essentially exactly what I'm looking for, but I don't think it's feasible to pull in the Spring Framework for just this one problem. – Dusty Dec 09 '10 at 05:15
  • 4
    Apache Commons BeanUtils might be suitable then, it's a bit more lightweight than Spring. It has an identically-named class and method that looks suspiciously similar, though I've never used it. See http://commons.apache.org/beanutils/ – gutch Dec 09 '10 at 06:35
  • Spring version works better on classes related through inheritance. – raffian Dec 01 '16 at 17:24
3

Similar to the first answer, but to clarify - spring is not needed. Commons BeanUtils.copy properties(Object dest, Object orig)

http://commons.apache.org/beanutils/api/org/apache/commons/beanutils/BeanUtils.html#copyProperties(java.lang.Object,%20java.lang.Object)

revdrjrr
  • 1,025
  • 1
  • 8
  • 16
2

To copy based on fields rather than getters and setters, you can use Spring's ReflectionUtils.shallowCopyFieldState().

Alexander
  • 2,761
  • 1
  • 28
  • 33
2

If you want to the task efficiently (in terms of runtime performance), then hand coding the copy using getters and setters is the way to go. Unless there is something funky about the getter or setter methods, their bodies will be inlined so that they are as fast as doing field assignments.

The reflective approach (e.g. using an existing class like BeanUtils) is less coding, but probably an order of magnitude slower than calling getters and setters in a simple way. If you try to implement this yourself, you may find yourself with more work than you bargained for, especially if your reflective copy class / method has to cope with overloaded methods, inheritance, value conversion, boxing/unboxing and so on.

With the code generation approach, you need to balance the effort and complexity of implementing the code generation (using whatever technology you choose) versus the effort of writing the copy methods by hand. You probably probably won't break even with the code generation approach before 20 classes ... and many more if you are not familiar with the technology.

Stephen C
  • 698,415
  • 94
  • 811
  • 1,216
  • can you provide any benchmarking, so it will make your answer more meaningful and help others to follow it. – Ravi May 25 '18 at 05:27
  • No @Ravi I can't. (Or more to be more accurate, I don't want to.) But if you wanted go to the effort of writing a (sound) benchmark, and posting your own Answer, I am sure you would be rewarded. – Stephen C May 25 '18 at 07:54
1

I'd write a simple java tool to autogenerate the source code for classes that can populate the the subsets fields with the common fields from superset. This tool will use reflection to get the names of the getter and setter methods. The rest are (trivial) String operations to "write" a source file in memory and store it to a *.java file. Compile all this autogenerated files and add the class files to the classpath.

The class could look like this:

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
   }
}
Andreas Dolk
  • 113,398
  • 19
  • 180
  • 268
0

Can you provide some sample code from your app that depicts the scenario you have mentioned in your post? Right now reflection seems like the best way , since it lets you inspect the class members at runtime.

Anupam Saini
  • 2,431
  • 2
  • 23
  • 30
0

This is obviously a task for Java's reflection and while others have already suggested valid although maybe a bit heavyweight solutions, here's one more:

About a year ago I wrote a smallish JavaBean property modifier library called BeanPropertyController. While I don't specifically recommend it to anyone, I do think that the namesake class of the library (see source) can be used as a reference to adopt similar functionality to your needs. As a quick example, here's how I used BPC to do (almost!) what you're asking:

// 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);

This prints out

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

So, what I suggest is that you go to the URL I linked and adapt this piece of code to your needs. I'm quite sure you don't need the various instantiation methods, default value providers etc. which I've included. And yes, BPC can be considered to be deprecated.

Esko
  • 29,022
  • 11
  • 55
  • 82