creating a generic function to copy different objects

NoobEditor :

so i have a use case where i need to copy an object of classes ( classes may vary depending on the input type in factory.

here is a sample of what i am trying to do

public interface DataUtil {

    // the main wrapper 
    static Object copyObject(Object payload){
        if(payload instanceof Human))
            return copyEntry((Human) payload);
        if(payload instanceof Car))
            return copyEntry((Car) payload);
        if(payload instanceof Planet))
            return copyEntry((Planet) payload);        
        return payload;
    }

    static Human copyEntry(Human human) {
        return Human.builder()
                .name(human.getName())
                .age(human.getAge())
                .build();
    }

    static Car copyEntry(Car car) {
        return Car.builder()
                .model(car.getModel())
                .brand(car.getBrand())
                .build();
    }

    static Planet copyEntry(Planet planet) {
        // return builder like previous
    }
}

If you look at copyObject function, it does the job as intended but he issue is in return type. At present, to make itself compatible, its returning an Object but i would rather prefer to return it specific class Object ( say Human or Car for instance )

Is there a way to get this done with Generics (using <T>)? or is this a bad approach in the first place to do?

davidxxx :

Is there a way to get this done with Generics (using )? or is this a bad approach in the first place to do?

It is a bad approach because you receive as parameter a Object.
You cannot infer from that the concrete type : whereas the instanceof you used. Which is not a fine approach.
Here two ideas (related enough)

1) Introducing a Copyable interface

You could introduce an interface that the classes of the objects you want to copy implement :

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

that could be implemented such as :

public class Human implements Copyable<Human> {

   @Override
   public Human copy(Human t) {
       return    Human.builder()
                     .name(human.getName())
                     .age(human.getAge())
                     .build();
   }

}

So the general copy() method could look like :

// the main wrapper
static <T extends Copyable<T>> T copyObject(T payload) {
    return payload.copy(payload);
}

And you could use it in this way :

Human human = new Human();
// set some fields  ...
Human copiedHuman = copyObject(human); // compile
Car copiedCar = copyObject(human); // doesn't compile

2) Use the visitor pattern

As alternative, it is also a good case for the visitor pattern : you want to apply a processing according to the concrete type of the parameter.
It allows to group together copy operations as in your actual code.

The general copyObject() method could rely on CopyVisitor that will do the copy according to the concrete type of the parameter :

@SuppressWarnings("unchecked")
static <T extends Visited> T copyObject(T payload) {
    CopyVisitor visitor = new CopyVisitor();
    payload.accept(visitor);
    return (T) visitor.getCopy();
}

Where CopyVisitor implements a classic Visitor interface :

public interface Visitor {  
    void visitHuman(Human human);
    void visitCar(Car car);
    void visitPlanet(Planet planet);
}

in this way :

public class CopyVisitor implements Visitor {

    private Visited copy;

    @Override
    public void visitHuman(Human human) {
        copy = Human.builder()
                    .name(human.getName())
                    .age(human.getAge())
                    .build();

    }

    @Override
    public void visitCar(Car car) {
        copy = Car.builder()
                  .model(car.getModel())
                  .brand(car.getBrand())
                  .build();
    }

    @Override
    public void visitPlanet(Planet planet) {
        //...
    }

    public Visited getCopy() {
        return copy;
    }

}

The visited classes (Car, Human, Plan) would implement a specific interface to "accept" the visitor :

public interface Visited {
    void accept(Visitor visitor);
}

such as :

public class Human implements Visited {

    @Override
    public void accept(Visitor visitor) {
        visitor.visitHuman(this);
    }

}

So you can use the copy() method in this way :

Human human = new Human();
// set some fields  ...
Human copiedHuman = copyObject(human); // compile
Car copiedCar = copyObject(human); // doesn't compile

Guess you like

Origin http://43.154.161.224:23101/article/api/json?id=87981&siteId=1