【问题标题】:creating a generic function to copy different objects创建一个通用函数来复制不同的对象
【发布时间】:2018-08-01 10:09:06
【问题描述】:

所以我有一个用例,我需要复制classes 的object(类可能因工厂中的输入类型而异。

这是我正在尝试做的一个示例

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
    }
}

如果您查看copyObject 函数,它会按预期完成工作,但他的问题是返回类型。目前,为了使其自身兼容,它返回一个Object,但我更愿意返回它特定的类对象(例如HumanCar

有没有办法通过泛型(使用<T>)来完成这项工作?或者这首先是一个不好的方法?

【问题讨论】:

  • 复制构造函数怎么样?
  • @MuratK。 :我希望copy 发生在util 内,所以要避免这种方法!
  • 目前你不需要这个类。只需提供一个复制构造函数并调用new Human(human)
  • 实际问题是 Human / Car 等是不受我控制的代码库,它们没有 copy const 的这种功能,因此所有这些都是混乱的。这就是为什么我想在Util 中包含它:|
  • 你应该得到一个可以为你进行克隆的库,这样你就不必浪费时间编写(坏的)代码了。它们也完全通用!

标签: java object java-8 copy


【解决方案1】:

有没有办法通过泛型(使用)来完成这项工作?或者这是 一个不好的方法首先要做什么?

这是一种不好的方法,因为您收到Object 作为参数。
您无法从中推断出具体类型 : 而您使用的 instanceof。这不是一个好方法。
这里有两个想法(足够相关)

1) 引入可复制界面

您可以引入要复制的对象的类实现的接口:

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

可以这样实现:

public class Human implements Copyable<Human> {

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

}

所以一般的 copy() 方法可能如下所示:

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

你可以这样使用它:

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

2) 使用访问者模式

作为替代方案,访问者模式也是一个很好的案例:您希望根据参数的具体类型应用处理。
它允许将复制操作组合在一起,就像在您的实际代码中一样。

一般的copyObject()方法可以依赖CopyVisitor,它会根据参数的具体类型进行复制:

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

CopyVisitor 实现了经典的Visitor 接口:

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

这样:

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;
    }

}

访问的类(Car、Human、Plan)将实现一个特定的接口来“接受”访问者:

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

如:

public class Human implements Visited {

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

}

所以你可以这样使用copy()方法:

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

【讨论】:

  • 不需要Copyable,有UnaryOperator
  • 这是对的,但public class Human implements Copyable&lt;Human&gt;{...}public class Human implements UnaryOperator&lt;Human&gt; {...} 更有意义
  • 只是想想我多么不喜欢/理解访客模式,这太棒了!加一个!
  • @Eugene 好吧。但是出于同样的原因,我也更喜欢 human.copy(human) 而不是 human.apply(human)` :更具可读性:) 谢谢 :) 我不是访客模式的粉丝,但在某些情况下它可能还不错:)
  • 非常好的答案。但我觉得将复制的结果存储在实例变量 copy 中,然后调用 getCopy() 似乎很奇怪......有什么想法吗?
【解决方案2】:

不幸的是,您必须像这样进行一些未经检查的强制转换:

static <TPayload> TPayload copyObject(Object payload) {
    if (payload instanceof Human)
        return (TPayload) copyEntry((Human) payload);
    if (payload instanceof Car)
        return (TPayload) copyEntry((Car) payload);
    if (payload instanceof Planet)
        return (TPayload) copyEntry((Planet) payload);
    return (TPayload) payload;
}

但是正如 cmets 中提到的,这并不妨碍你写作:

Number n = DataUtil.copyObject("someString");

【讨论】:

  • 变量的编译时类型可以比实际实例更宽,就像Object o = "foo";一样,所以这与类型擦除无关,无论如何你都需要instanceof检查,如果您想对实际的对象类型而不是编译时声明做出反应。除此之外,您的通用签名承诺返回调用者希望的任何内容,而不管他们传入了什么,这当然是从根本上破坏的。例如,它允许写Number n = copyObject("someString");...
  • @Holger 对。从答案中删除类型擦除
【解决方案3】:

如果您了解 Object 中的类型,您可以这样做:

static <T> T copyObject(Object payload)
    {
        if (payload instanceof Human)
        {
            return (T) copyEntry((Human) payload);
        }
        if (payload instanceof Car)
        {
            return (T) copyEntry((Car) payload);
        }
        if (payload instanceof Planet)
        {
            return (T) copyEntry((Planet) payload);
        }
        return (T) payload;
    };

然后:

Human h1 = new ...
Human h2= copyObject(h1);

即使 Java 的类型擦除不适用,您也需要了解您的语言的运行时知识才能做到这一点,也就是“依赖类型”。

因此,在 C++ 等某些语言中发现的返回类型重载对于 List&lt;Object&gt; 中的运行时类型切换没有帮助。

但是为什么你仍然需要这个,你将在一个新的异构列表中再次调用后收集所有对象实例

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2021-03-28
    • 2019-02-07
    • 2020-02-08
    • 1970-01-01
    • 1970-01-01
    • 2021-07-29
    • 1970-01-01
    • 2016-05-13
    相关资源
    最近更新 更多