【问题标题】:Copy fields between similar classes in java在java中的相似类之间复制字段
【发布时间】:2017-02-22 10:51:15
【问题描述】:

我有一对类,其中一个的字段是另一个字段的子集,并且超集类的 getter 都可以预见地命名 (getFoo())。是否有某种方法可以有效地将所有公共字段从超集类复制到子集类,或者至少自动生成代码来执行此操作。

我应该注意:

  • 由于各种原因,我无法编辑超集类,也无法始终使用它们以避免复制数据。
  • 我可以在子类中创建新方法,但不能更改它们的字段。
  • 我们有几十个这样的对,有些类有很多字段,所以至少可以说手工操作很笨拙。
  • 同事想出了一个方法,创建一个通用的复制方法,使用java反射取任意两个类,以Strings的形式遍历字段,进行字符串操作确定getter名称,然后执行自动设置子集类中的字段。这很糟糕,但它似乎有效。我真的希望有更好的方法。

编辑:根据要求提供一些简单的代码

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

【问题讨论】:

  • 如果您可以编辑超集类,您可以简单地让它们扩展您的子集类并以经典的 OO 方式继承它们的所有字段和方法。为什么不能编辑超集类?
  • @Asaph - 遗憾的是,超集类是单独开发的外部库的一部分,我现在需要能够将其转换为为这个特定项目创建的类,这些类并没有使用太多较大的信息。
  • 如果你把关系颠倒过来,让你的子集类继承自超集类怎么办?您可以使用空主体覆盖与不需要的字段相关的任何方法。它不是太优雅,但至少您无需复制/粘贴、反射或代码生成即可重用代码。

标签: java


【解决方案1】:

您可以使用 Spring Framework 中的 BeanUtils 类来执行此操作。它可能不一定比基于反射的技术更有效,但它的代码肯定很简单。我希望您需要做的就是:

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)

如果不适合,您还可以考虑在 Spring Framework 中使用 BeanWrapper / BeanWrapperImpl 来遍历类的属性。这比使用低级反射 API 更简单。

【讨论】:

  • 哇,这基本上正是我正在寻找的东西,但我认为仅针对这一问题引入 Spring 框架是不可行的。
  • Apache Commons BeanUtils 可能是合适的,它比 Spring 更轻量级。它有一个同名的类和方法,看起来非常相似,尽管我从未使用过它。见commons.apache.org/beanutils
  • Spring 版本在通过继承相关的类上效果更好。
【解决方案2】:

与第一个答案类似,但要澄清一下 - 不需要弹簧。 Commons BeanUtils.copy 属性(Object dest, Object orig)

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

【讨论】:

    【解决方案3】:

    如果您想高效地完成任务(就运行时性能而言),那么使用 getter 和 setter 手动编码副本是可行的方法。除非 getter 或 setter 方法有什么奇怪的地方,否则它们的主体将被内联,以便它们与执行字段分配一样快。

    反射方法(例如,使用像 BeanUtils 这样的现有类)的编码更少,但可能比以简单方式调用 getter 和 setter 慢一个数量级。如果您尝试自己实现这一点,您可能会发现自己的工作量超出了您的预期,特别是如果您的反射复制类/方法必须处理重载方法、继承、值转换、装箱/拆箱等。

    使用代码生成方法,您需要平衡实现代码生成的工作量和复杂性(使用您选择的任何技术)与手动编写复制方法的工作量。在 20 节课之前,您可能无法使用代码生成方法收支平衡……如果您不熟悉该技术,您可能还无法收支平衡。

    【讨论】:

    • 您能否提供任何基准测试,以便您的答案更有意义并帮助其他人遵循它。
    • 不,@Ravi 我不能。 (或者更准确地说,我不想这样做。)但是,如果您想努力编写(合理的)基准测试并发布自己的答案,我相信您会得到回报。
    【解决方案4】:

    要基于字段而不是 getter 和 setter 进行复制,可以使用 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】:

        您能否提供一些应用示例代码来描述您在帖子中提到的场景? 现在反射似乎是最好的方法,因为它可以让你在运行时检查类成员。

        【讨论】:

        • 添加了一些示例代码,但通用(我无法显示实际代码)
        • @gutch 的方法完全符合您的要求,但不幸的是您不能使用。
        【解决方案7】:

        这显然是 Java 反思的一项任务,虽然其他人已经提出了有效的虽然可能有点重量级的解决方案,但这里还有一个:

        大约一年前,我编写了一个名为 BeanPropertyController 的小型 JavaBean 属性修饰符库。虽然我没有特别向任何人推荐它,但我确实认为该库的同名类 (see source) 可以用作参考,以采用类似的功能来满足您的需求。举个简单的例子,下面是我如何使用 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 已被弃用。

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2011-10-14
          • 1970-01-01
          • 2014-06-05
          • 2019-05-16
          相关资源
          最近更新 更多