3

I was wondering if the following scenario is possible.

Having two classes (Source and Destination) where in code I could do this:

public class Source{

   private String fieldA;
   private String fieldB;

   public Source(){ ... }
}

...

public class Destination{

   public Destination(Source src){ ... }
}

Source src = new Source();
Destination dest = new Destination(src);
dest.fieldA = "test";
dest.fieldB = "test";

So what I mean here is that I have two classes, one called Source that contains (private) fields and one called Destination with no fields. After creating two objects of these classes and passing in Source into the constructor of Destination, I want to be able to duplicate/copy the fields of Source into Destination.

Could something like this be possible in Java, whether or not using Reflection? And if possible, can someone give me a minor example I can start with.

RazorAlliance192
  • 722
  • 9
  • 23
  • The only way you could do that, would be by adding at least two **`public`** fields to `Destination`. But maybe you're really looking to combine a `Factory` and some kind of byte-code manipulation factory. I don't know you'd be able to use a `Destination` with fields you don't know before hand. – Elliott Frisch Jan 03 '15 at 09:40
  • Yes the fields in Destination should be public after they have been duplicated/copied from Source. However, as I commented earlier it is possible to pass in ANY object. In my example Source only has two fields, while it's perfectly possible I pass in objects with 5, 10, 20 fields – RazorAlliance192 Jan 03 '15 at 09:43
  • 1
    Not an easy problem to solve, and once you've solved it I have no idea how you will use it. But, I wish you the best of luck! – Elliott Frisch Jan 03 '15 at 09:45
  • I don't believe I've seen something like this in Java, but I'm pretty sure you can do it in Groovy(essentially a Java extension), with its meta-programming features. If you're flexible enough to change languages, that might be worth a shot. – kinbiko Jan 03 '15 at 09:50
  • How about storing the fields in a map (`Map`). Field values can be retrieved like this: [How do I read private field in Java](http://stackoverflow.com/questions/1196192/how-do-i-read-a-private-field-in-java). – wassgren Jan 03 '15 at 10:07
  • Why do you want to do thus? Why do you want the set if fields of an obje t to be dynamic? – Raedwald Jan 03 '15 at 10:23
  • @Raedwald The reason why is to have some fun coding and since I have not found something that does this, it would be great if I somehow managed to get it to work. Maybe there is not a real usage for this but it would be foolish to stop chasing our ideas just because it isn't useful :) – RazorAlliance192 Jan 03 '15 at 10:35
  • @RazorAlliance192, what solution did you end up with? – wassgren Jan 07 '15 at 16:11

5 Answers5

3

A hackish version to accomplish this is to add all fields to a Map. The fields can be copied from the source object to the destination object and the field name can be the key. Something along the lines of this:

public class FieldAccessor {
    public static class Destination {
        private final Map<String, Object> fields = new HashMap<>();

        public Destination(Object o) {
            final Set<Field> accessibleFields = Arrays.stream(o.getClass().getDeclaredFields())
                    .map(field -> {
                        field.setAccessible(true);
                        return field;
                    })
                    .collect(Collectors.toSet());

            accessibleFields.forEach(field -> {
                try {
                    fields.put(field.getName(), field.get(o));
                } catch (IllegalAccessException e) {
                    throw new IllegalStateException("Unable to access field", e);
                }
            });
        }

        public Set<String> fieldNames() {
            return fields.keySet();
        }

        public Optional<Object> fieldValue(String fieldName) {
            return Optional.ofNullable(fields.get(fieldName));
        }
    }

    public static class Source {
        private final String fieldA;
        private final Integer fieldB;
        private final int fieldC;

        public Source(String fieldA, Integer fieldB, int fieldC) {
            this.fieldA = fieldA;
            this.fieldB = fieldB;
            this.fieldC = fieldC;
        }

        public String getFieldA() {
            return fieldA;
        }

        public Integer getFieldB() {
            return fieldB;
        }

        public int getFieldC() {
            return fieldC;
        }
    }

    @Test
    public void testFields() {
        Destination destination = new Destination(new Source("Abc", 123, 456));

        destination.fieldNames().stream().forEach(fieldName -> {
            System.out.println("Fieldname: " + fieldName + ", value: " + destination.fieldValue(fieldName).get());
        });
    }
}

For more info, check out this SO.

However, this is not something I would use in real production code. Instead, I would use some sort of serialization by e.g. using Jackson.

Community
  • 1
  • 1
wassgren
  • 18,651
  • 6
  • 63
  • 77
1

So you want to dynamically create fields in an object ? That's not directly possible in Java. If you just wanted to copy methods of an interface, the answer would have be to use a JDK proxy. It may still be of interest if :

  • you accept to only use getters and setters in Destination class
  • Source class implements an interface defining the setters and getters you want to copy

If you cannot accept those restrictions, you will have to look to CGLIB proxies or Javassist, that is libraries that dynamically modify the bytecode of the compiled class objects at load time. It is a really advanced feature, that is mainly used in frameworks or other libraries and not in high-level programs. Typically it is used in Object Relational Mappers like Hibernate to replace simple collection classes with enhanced ones that transparently gets (an save) their elements in database.

In any other case, trying to access private fields outside of the class should be seen as an indicator for a possible design flaw. private means implementation dependant and can change across versions and should not be used without knowing why.

Serge Ballesta
  • 143,923
  • 11
  • 122
  • 252
  • Thanks for pointing out the CGLIB proxies and Javassist library. I will have a closer look on what they offer and if it's useful for what I wish to accomplish. – RazorAlliance192 Jan 03 '15 at 10:41
0

The simplest and most efficient way to do it is copying the fields explicitly :

   public Destination(Source src)
   {
       this.fieldA = src.getFieldA();
       this.fieldB = src.getFieldB();
   }

I don't see the point in using reflection for this purpose.

Eran
  • 387,369
  • 54
  • 702
  • 768
  • Yes ofcourse this would work if I knew the fields beforehand. I wish to pass in ANY object into Destination after which destination duplicates/copies its fields. – RazorAlliance192 Jan 03 '15 at 09:41
  • @RazorAlliance192 But for that purpose, Destination should have the same properties as the passed object. The alternative is that Destination would have a member of the `Source` type, and the constructor will create a clone of the Source instance passed to it. – Eran Jan 03 '15 at 09:43
  • @RazorAlliance192 how this would work while fieldA and fieldB are **private**!!?? – void Jan 03 '15 at 09:43
  • @FarhangAmary I'm sure Eran was implying that he create getter methods for the private fields. The OP should explain more of what he wants to accomplish. It sounds like inheritance may work for whatever he wants to do. – Jared Rummler Jan 03 '15 at 10:01
0

The only thing is in my mind for this at this time is extending Destination class from Source

public class Source{

   private String fieldA;
   private String fieldB;

   //You need to have both Getter and Setter for fieldA and fieldB

   public Source(){ ... }
}

...

public class Destination extends Source{

   public Destination(){...}
}

Source src =  new Destination();
dest.setFieldA("test");
dest.setFieldB("test");
void
  • 7,760
  • 3
  • 25
  • 43
-2

Private members of Source cannot be accessed from Destination object even if you are passing a Source object to Destination.

You need to add string fieldA, fieldB to Destination to

string fieldA, fieldB;
public Destination(Source src)
{
   fieldA = src.fieldA;
   fieldB = src.fieldB;
}
Eran
  • 387,369
  • 54
  • 702
  • 768
Neha Agrawal
  • 155
  • 1
  • 1
  • 13
  • "Private members of Source cannot be accessed from Destination object even if you are passing a Source object to Destination." Aren't you accessing private variable in your code? `fieldA = src.fieldA; fieldB = src.fieldB;` – SMA Jan 03 '15 at 09:52
  • @almasshaikh, I said they cannot be accessed from a Destination object. The accesses shown above are done in the constructor. – Neha Agrawal Jan 03 '15 at 16:27
  • Yes even from constructor you can't access those field. – SMA Jan 03 '15 at 16:33
  • You should be able to because you are passing a reference to Source.. :-? – Neha Agrawal Jan 03 '15 at 16:35
  • Why dont you try and let us know? – SMA Jan 03 '15 at 16:35