最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

Java 如何優(yōu)雅的拷貝對(duì)象屬性

 更新時(shí)間:2020年11月19日 09:43:48   作者:rxliuli  
這篇文章主要介紹了Java 如何優(yōu)雅的拷貝對(duì)象屬性,幫助大家更好的理解和學(xué)習(xí)Java,感興趣的朋友可以了解下

場(chǎng)景

在 Java 項(xiàng)目中,經(jīng)常遇到需要在對(duì)象之間拷貝屬性的問(wèn)題。然而,除了直接使用 Getter/Stter 方法,我們還有其他的方法么?
當(dāng)然有,例如 Apache Common Lang3 的 BeanUtils,然而 BeanUtils 卻無(wú)法完全滿(mǎn)足吾輩的需求,所以吾輩便自己封裝了一個(gè),這里分享出來(lái)以供參考。

  • 需要大量復(fù)制對(duì)象的屬性
  • 對(duì)象之間的屬性名可能是不同的
  • 對(duì)象之間的屬性類(lèi)型可能是不同的

目標(biāo)

簡(jiǎn)單易用的 API

  • copy: 指定需要拷貝的源對(duì)象和目標(biāo)對(duì)象
  • prop: 拷貝指定對(duì)象的字段
  • props: 拷貝指定對(duì)象的多個(gè)字段
  • exec: 執(zhí)行真正的拷貝操作
  • from: 重新開(kāi)始添加其他對(duì)象的屬性
  • get: 返回當(dāng)前的目標(biāo)對(duì)象
  • config: 配置拷貝的一些策略

思路

  • 定義門(mén)面類(lèi) BeanCopyUtil 用以暴露出一些 API
  • 定義每個(gè)字段的操作類(lèi) BeanCopyField,保存對(duì)每個(gè)字段的操作
  • 定義 BeanCopyConfig,用于配置拷貝屬性的策略
  • 定義 BeanCopyOperator 作為拷貝的真正實(shí)現(xiàn)

圖解

實(shí)現(xiàn)

注:反射部分依賴(lài)于 joor, JDK1.8 請(qǐng)使用 joor-java-8

定義門(mén)面類(lèi) BeanCopyUtil 用以暴露出一些 API

/**
 * java bean 復(fù)制操作的工具類(lèi)
 *
 * @author rxliuli
 */
public class BeanCopyUtil<F, T> {
  /**
   * 源對(duì)象
   */
  private final F from;
  /**
   * 目標(biāo)對(duì)象
   */
  private final T to;
  /**
   * 拷貝的字段信息列表
   */
  private final List<BeanCopyField> copyFieldList = new LinkedList<>();
  /**
   * 配置信息
   */
  private BeanCopyConfig config = new BeanCopyConfig();

  private BeanCopyUtil(F from, T to) {
    this.from = from;
    this.to = to;
  }

  /**
   * 指定需要拷貝的源對(duì)象和目標(biāo)對(duì)象
   *
   * @param from 源對(duì)象
   * @param to  目標(biāo)對(duì)象
   * @param <F> 源對(duì)象類(lèi)型
   * @param <T> 目標(biāo)對(duì)象類(lèi)型
   * @return 一個(gè) {@link BeanCopyUtil} 對(duì)象
   */
  public static <F, T> BeanCopyUtil<F, T> copy(F from, T to) {
    return new BeanCopyUtil<>(from, to);
  }

  /**
   * 拷貝指定對(duì)象的字段
   *
   * @param fromField 源對(duì)象中的字段名
   * @param toField  目標(biāo)對(duì)象中的字段名
   * @param converter 將源對(duì)象中字段轉(zhuǎn)換為目標(biāo)對(duì)象字段類(lèi)型的轉(zhuǎn)換器
   * @return 返回 {@code this}
   */
  public BeanCopyUtil<F, T> prop(String fromField, String toField, Function<? super Object, ? super Object> converter) {
    copyFieldList.add(new BeanCopyField(fromField, toField, converter));
    return this;
  }

  /**
   * 拷貝指定對(duì)象的字段
   *
   * @param fromField 源對(duì)象中的字段名
   * @param toField  目標(biāo)對(duì)象中的字段名
   * @return 返回 {@code this}
   */
  public BeanCopyUtil<F, T> prop(String fromField, String toField) {
    return prop(fromField, toField, null);
  }

  /**
   * 拷貝指定對(duì)象的字段
   *
   * @param field   源對(duì)象中與目標(biāo)對(duì)象中的字段名
   * @param converter 將源對(duì)象中字段轉(zhuǎn)換為目標(biāo)對(duì)象字段類(lèi)型的轉(zhuǎn)換器
   * @return 返回 {@code this}
   */
  public BeanCopyUtil<F, T> prop(String field, Function<? super Object, ? super Object> converter) {
    return prop(field, field, converter);
  }

  /**
   * 拷貝指定對(duì)象的字段
   *
   * @param field 源對(duì)象中與目標(biāo)對(duì)象中的字段名
   * @return 返回 {@code this}
   */
  public BeanCopyUtil<F, T> prop(String field) {
    return prop(field, field, null);
  }

  /**
   * 拷貝指定對(duì)象的多個(gè)字段
   *
   * @param fields 源對(duì)象中與目標(biāo)對(duì)象中的多個(gè)字段名
   * @return 返回 {@code this}
   */
  public BeanCopyUtil<F, T> props(String... fields) {
    for (String field : fields) {
      prop(field);
    }
    return this;
  }

  /**
   * 執(zhí)行真正的拷貝操作
   *
   * @return 返回 {@code this}
   */
  public BeanCopyUtil<F, T> exec() {
    new BeanCopyOperator<>(from, to, copyFieldList, config).copy();
    return this;
  }

  /**
   * 重新開(kāi)始添加其他對(duì)象的屬性
   * 用于在執(zhí)行完 {@link #exec()} 之后還想復(fù)制其它對(duì)象的屬性
   *
   * @param from 源對(duì)象
   * @param <R> 源對(duì)象類(lèi)型
   * @return 一個(gè)新的 {@link BeanCopyUtil} 對(duì)象
   */
  public <R> BeanCopyUtil<R, T> from(R from) {
    return new BeanCopyUtil<>(from, to);
  }

  /**
   * 返回當(dāng)前的目標(biāo)對(duì)象
   *
   * @return 當(dāng)前的目標(biāo)對(duì)象
   */
  public T get() {
    return to;
  }

  /**
   * 配置拷貝的一些策略
   *
   * @param config 拷貝配置對(duì)象
   * @return 返回 {@code this}
   */
  public BeanCopyUtil<F, T> config(BeanCopyConfig config) {
    this.config = config;
    return this;
  }
}

定義每個(gè)字段的操作類(lèi) BeanCopyField,保存對(duì)每個(gè)字段的操作

/**
 * 拷貝屬性的每一個(gè)字段的選項(xiàng)
 *
 * @author rxliuli
 */
public class BeanCopyField {
  private String from;
  private String to;
  private Function<? super Object, ? super Object> converter;

  public BeanCopyField() {
  }

  public BeanCopyField(String from, String to, Function<? super Object, ? super Object> converter) {
    this.from = from;
    this.to = to;
    this.converter = converter;
  }

  public String getFrom() {
    return from;
  }

  public BeanCopyField setFrom(String from) {
    this.from = from;
    return this;
  }

  public String getTo() {
    return to;
  }

  public BeanCopyField setTo(String to) {
    this.to = to;
    return this;
  }

  public Function<? super Object, ? super Object> getConverter() {
    return converter;
  }

  public BeanCopyField setConverter(Function<? super Object, ? super Object> converter) {
    this.converter = converter;
    return this;
  }
}

定義 BeanCopyConfig,用于配置拷貝屬性的策略

/**
 * 拷貝屬性的配置
 *
 * @author rxliuli
 */
public class BeanCopyConfig {
  /**
   * 同名的字段自動(dòng)復(fù)制
   */
  private boolean same = true;
  /**
   * 覆蓋同名的字段
   */
  private boolean override = true;
  /**
   * 忽略 {@code null} 的源對(duì)象屬性
   */
  private boolean ignoreNull = true;
  /**
   * 嘗試進(jìn)行自動(dòng)轉(zhuǎn)換
   */
  private boolean converter = true;

  public BeanCopyConfig() {
  }

  public BeanCopyConfig(boolean same, boolean override, boolean ignoreNull, boolean converter) {
    this.same = same;
    this.override = override;
    this.ignoreNull = ignoreNull;
    this.converter = converter;
  }

  public boolean isSame() {
    return same;
  }

  public BeanCopyConfig setSame(boolean same) {
    this.same = same;
    return this;
  }

  public boolean isOverride() {
    return override;
  }

  public BeanCopyConfig setOverride(boolean override) {
    this.override = override;
    return this;
  }

  public boolean isIgnoreNull() {
    return ignoreNull;
  }

  public BeanCopyConfig setIgnoreNull(boolean ignoreNull) {
    this.ignoreNull = ignoreNull;
    return this;
  }

  public boolean isConverter() {
    return converter;
  }

  public BeanCopyConfig setConverter(boolean converter) {
    this.converter = converter;
    return this;
  }
}

定義 BeanCopyOperator 作為拷貝的真正實(shí)現(xiàn)

/**
 * 真正執(zhí)行 copy 屬性的類(lèi)
 *
 * @author rxliuli
 */
public class BeanCopyOperator<F, T> {
  private static final Logger log = LoggerFactory.getLogger(BeanCopyUtil.class);
  private final F from;
  private final T to;
  private final BeanCopyConfig config;
  private List<BeanCopyField> copyFieldList;

  public BeanCopyOperator(F from, T to, List<BeanCopyField> copyFieldList, BeanCopyConfig config) {
    this.from = from;
    this.to = to;
    this.copyFieldList = copyFieldList;
    this.config = config;
  }

  public void copy() {
    //獲取到兩個(gè)對(duì)象所有的屬性
    final Map<String, Reflect> fromFields = Reflect.on(from).fields();
    final Reflect to = Reflect.on(this.to);
    final Map<String, Reflect> toFields = to.fields();
    //過(guò)濾出所有相同字段名的字段并進(jìn)行拷貝
    if (config.isSame()) {
      final Map<ListUtil.ListDiffState, List<String>> different = ListUtil.different(new ArrayList<>(fromFields.keySet()), new ArrayList<>(toFields.keySet()));
      copyFieldList = Stream.concat(different.get(ListUtil.ListDiffState.common).stream()
          .map(s -> new BeanCopyField(s, s, null)), copyFieldList.stream())
          .collect(Collectors.toList());
    }
    //根據(jù)拷貝字段列表進(jìn)行拷貝
    copyFieldList.stream()
        //忽略空值
        .filter(beanCopyField -> !config.isIgnoreNull() || fromFields.get(beanCopyField.getFrom()).get() != null)
        //覆蓋屬性
        .filter(beanCopyField -> config.isOverride() || toFields.get(beanCopyField.getTo()).get() == null)
        //如果沒(méi)有轉(zhuǎn)換器,則使用默認(rèn)的轉(zhuǎn)換器
        .peek(beanCopyField -> {
          if (beanCopyField.getConverter() == null) {
            beanCopyField.setConverter(Function.identity());
          }
        })
        .forEach(beanCopyField -> {
          final String fromField = beanCopyField.getFrom();
          final F from = fromFields.get(fromField).get();
          final String toField = beanCopyField.getTo();
          try {
            to.set(toField, beanCopyField.getConverter().apply(from));
          } catch (ReflectException e) {
            log.warn("Copy field failed, from {} to {}, exception is {}", fromField, toField, e.getMessage());
          }
        });
  }
}

使用

使用流程圖

測(cè)試

代碼寫(xiě)完了,讓我們測(cè)試一下!

public class BeanCopyUtilTest {
  private final Logger log = LoggerFactory.getLogger(getClass());
  private Student student;
  private Teacher teacher;

  @Before
  public void before() {
    student = new Student("琉璃", 10, "女", 4);
    teacher = new Teacher();
  }

  @Test
  public void copy() {
    //簡(jiǎn)單的復(fù)制(類(lèi)似于 BeanUtils.copyProperties)
    BeanCopyUtil.copy(student, teacher).exec();
    log.info("teacher: {}", teacher);
    assertThat(teacher)
        .extracting("age")
        .containsOnlyOnce(student.getAge());
  }

  @Test
  public void prop() {
    //不同名字的屬性
    BeanCopyUtil.copy(student, teacher)
        .prop("sex", "sex", sex -> Objects.equals(sex, "男"))
        .prop("realname", "name")
        .exec();
    assertThat(teacher)
        .extracting("name", "age", "sex")
        .containsOnlyOnce(student.getRealname(), student.getAge(), false);
  }

  @Test
  public void prop1() {
    //不存的屬性
    assertThat(BeanCopyUtil.copy(student, teacher)
        .prop("sex", "sex", sex -> Objects.equals(sex, "男"))
        .prop("realname", "name2")
        .exec()
        .get())
        .extracting("age", "sex")
        .containsOnlyOnce(student.getAge(), false);
  }

  @Test
  public void from() {
    final Teacher lingMeng = new Teacher()
        .setName("靈夢(mèng)")
        .setAge(17);
    //測(cè)試 from 是否覆蓋
    assertThat(BeanCopyUtil.copy(student, teacher)
        .prop("sex", "sex", sex -> Objects.equals(sex, "男"))
        .prop("realname", "name")
        .exec()
        .from(lingMeng)
        .exec()
        .get())
        .extracting("name", "age", "sex")
        .containsOnlyOnce(lingMeng.getName(), lingMeng.getAge(), false);
  }

  @Test
  public void get() {
    //測(cè)試 get 是否有效
    assertThat(BeanCopyUtil.copy(student, teacher)
        .prop("sex", "sex", sex -> Objects.equals(sex, "男"))
        .prop("realname", "name")
        .exec()
        .get())
        .extracting("name", "age", "sex")
        .containsOnlyOnce(student.getRealname(), student.getAge(), false);
  }

  @Test
  public void config() {
    //不自動(dòng)復(fù)制同名屬性
    assertThat(BeanCopyUtil.copy(new Student().setAge(15), new Teacher())
        .config(new BeanCopyConfig().setSame(false))
        .exec()
        .get())
        .extracting("age")
        .containsOnlyNulls();
    //不覆蓋不為空的屬性
    assertThat(BeanCopyUtil.copy(new Student().setAge(15), new Teacher().setAge(10))
        .config(new BeanCopyConfig().setOverride(false))
        .exec()
        .get())
        .extracting("age")
        .containsOnlyOnce(10);
    //不忽略源對(duì)象不為空的屬性
    assertThat(BeanCopyUtil.copy(new Student(), student)
        .config(new BeanCopyConfig().setIgnoreNull(false))
        .exec()
        .get())
        .extracting("realname", "age", "sex", "grade")
        .containsOnlyNulls();
  }

  /**
   * 測(cè)試學(xué)生類(lèi)
   */
  private static class Student {
    /**
     * 姓名
     */
    private String realname;
    /**
     * 年齡
     */
    private Integer age;
    /**
     * 性別,男/女
     */
    private String sex;
    /**
     * 年級(jí),1 - 6
     */
    private Integer grade;

    public Student() {
    }

    public Student(String realname, Integer age, String sex, Integer grade) {
      this.realname = realname;
      this.age = age;
      this.sex = sex;
      this.grade = grade;
    }

    public String getRealname() {

      return realname;
    }

    public Student setRealname(String realname) {
      this.realname = realname;
      return this;
    }

    public Integer getAge() {
      return age;
    }

    public Student setAge(Integer age) {
      this.age = age;
      return this;
    }

    public String getSex() {
      return sex;
    }

    public Student setSex(String sex) {
      this.sex = sex;
      return this;
    }

    public Integer getGrade() {
      return grade;
    }

    public Student setGrade(Integer grade) {
      this.grade = grade;
      return this;
    }

    @Override
    public String toString() {
      return ToStringBuilder.reflectionToString(this);
    }
  }

  /**
   * 測(cè)試教師類(lèi)
   */
  private static class Teacher {
    /**
     * 姓名
     */
    private String name;
    /**
     * 年齡
     */
    private Integer age;
    /**
     * 性別,true 男,false 女
     */
    private Boolean sex;
    /**
     * 職位
     */
    private String post;

    public Teacher() {
    }

    public Teacher(String name, Integer age, Boolean sex, String post) {
      this.name = name;
      this.age = age;
      this.sex = sex;
      this.post = post;
    }

    public String getName() {
      return name;
    }

    public Teacher setName(String name) {
      this.name = name;
      return this;
    }

    public Integer getAge() {
      return age;
    }

    public Teacher setAge(Integer age) {
      this.age = age;
      return this;
    }

    public Boolean getSex() {
      return sex;
    }

    public Teacher setSex(Boolean sex) {
      this.sex = sex;
      return this;
    }

    public String getPost() {
      return post;
    }

    public Teacher setPost(String post) {
      this.post = post;
      return this;
    }

    @Override
    public String toString() {
      return ToStringBuilder.reflectionToString(this);
    }
  }
}

如果沒(méi)有發(fā)生什么意外,那么一切將能夠正常運(yùn)行!

好了,那么關(guān)于在 Java 中優(yōu)雅的拷貝對(duì)象屬性就到這里啦

以上就是Java 如何優(yōu)雅的拷貝對(duì)象屬性的詳細(xì)內(nèi)容,更多關(guān)于Java 拷貝對(duì)象屬性的資料請(qǐng)關(guān)注腳本之家其它相關(guān)文章!

相關(guān)文章

  • java中設(shè)計(jì)模式之適配器模式

    java中設(shè)計(jì)模式之適配器模式

    這篇文章主要介紹了java中設(shè)計(jì)模式之適配器模式的相關(guān)資料,適配器模式將一個(gè)類(lèi)的接口轉(zhuǎn)換成客戶(hù)期望的另一個(gè)接口。適配器讓原本不兼容的類(lèi)可以合作得親密無(wú)間,需要的朋友可以參考下
    2017-09-09
  • java如何判斷時(shí)間段是否交叉重疊

    java如何判斷時(shí)間段是否交叉重疊

    這篇文章主要介紹了java如何判斷時(shí)間段是否交叉重疊問(wèn)題,具有很好的參考價(jià)值,希望對(duì)大家有所幫助,如有錯(cuò)誤或未考慮完全的地方,望不吝賜教
    2023-12-12
  • Spring?JPA的實(shí)體屬性類(lèi)型轉(zhuǎn)換器并反序列化工具類(lèi)詳解

    Spring?JPA的實(shí)體屬性類(lèi)型轉(zhuǎn)換器并反序列化工具類(lèi)詳解

    這篇文章主要介紹了Spring?JPA的實(shí)體屬性類(lèi)型轉(zhuǎn)換器并反序列化工具類(lèi)詳解,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教
    2022-02-02
  • java排序算法之冒泡排序

    java排序算法之冒泡排序

    這篇文章主要為大家詳細(xì)介紹了java排序算法之冒泡排序,文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下
    2021-05-05
  • cmd中javac命令無(wú)法運(yùn)行(java指令能運(yùn)行)解決步驟

    cmd中javac命令無(wú)法運(yùn)行(java指令能運(yùn)行)解決步驟

    這篇文章主要介紹了在安裝JDK后,執(zhí)行javac命令沒(méi)有返回值的問(wèn)題,可能是由于命令提示符窗口緩存問(wèn)題、系統(tǒng)路徑優(yōu)先級(jí)問(wèn)題、文件權(quán)限問(wèn)題或命令行輸入問(wèn)題,文中通過(guò)代碼將解決的步驟介紹的非常詳細(xì),需要的朋友可以參考下
    2025-02-02
  • 解決SpringBoot中使用@Async注解失效的問(wèn)題

    解決SpringBoot中使用@Async注解失效的問(wèn)題

    這篇文章主要介紹了解決SpringBoot中使用@Async注解失效的問(wèn)題,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過(guò)來(lái)看看吧
    2020-09-09
  • Java如何通過(guò)Socket同時(shí)發(fā)送文本和文件

    Java如何通過(guò)Socket同時(shí)發(fā)送文本和文件

    這篇文章主要介紹了Java如何通過(guò)Socket同時(shí)發(fā)送文本和文件問(wèn)題,具有很好的參考價(jià)值,希望對(duì)大家有所幫助,如有錯(cuò)誤或未考慮完全的地方,望不吝賜教
    2024-08-08
  • java利用CountDownLatch實(shí)現(xiàn)并行計(jì)算

    java利用CountDownLatch實(shí)現(xiàn)并行計(jì)算

    這篇文章主要介紹了java利用CountDownLatch實(shí)現(xiàn)并行計(jì)算,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下
    2018-10-10
  • Java多線程 生產(chǎn)者消費(fèi)者模型實(shí)例詳解

    Java多線程 生產(chǎn)者消費(fèi)者模型實(shí)例詳解

    這篇文章主要介紹了Java多線程 生產(chǎn)者消費(fèi)者模型實(shí)例詳解,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參考下
    2019-09-09
  • JavaSE static final及abstract修飾符實(shí)例解析

    JavaSE static final及abstract修飾符實(shí)例解析

    這篇文章主要介紹了JavaSE static final及abstract修飾符實(shí)例解析,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參考下
    2020-06-06

最新評(píng)論

德格县| 凌海市| 崇左市| 泰来县| 海兴县| 呼图壁县| 含山县| 家居| 永泰县| 岚皋县| 北碚区| 乌拉特前旗| 樟树市| 灵川县| 乳山市| 泾阳县| 喀喇| 通道| 龙胜| 句容市| 南康市| 胶州市| 宜州市| 湾仔区| 山西省| 南涧| 宁海县| 张家口市| 长海县| 若尔盖县| 武穴市| 隆安县| 新田县| 施甸县| 盘锦市| 信宜市| 安平县| 嵊泗县| 雷波县| 辛集市| 宜阳县|