为了账号安全,请及时绑定邮箱和手机立即绑定

用于修改值的 Java 框架/模式

用于修改值的 Java 框架/模式

心有法竹 2021-12-30 20:48:52
我有一个对象人public class Person {    private String name;    private Integer age ;    private String address;    private String group;    private String id;    public Person(String name, Integer age, String address, String group, String id) {        this.name = name;        this.age = age;        this.address = address;        this.group = group;        this.id = id;    }    @Override    public String toString() {        return "Person{" +                "name='" + name + '\'' +                ", age=" + age +                ", address='" + address + '\'' +                ", group='" + group + '\'' +                ", id='" + id + '\'' +                '}';    }    public void setName(String name) {        this.name = name;    }    public void setAge(Integer age) {        this.age = age;    }    public void setAddress(String address) {        this.address = address;    }    public void setGroup(String group) {        this.group = group;    }    public void setId(String id) {        this.id = id;    }    public String getName() {        return name;    }    public Integer getAge() {        return age;    }    public String getAddress() {        return address;    }    public String getGroup() {        return group;    }    public String getId() {        return id;    }}如上所述,它将非常笨拙,因为将有更多的比较运算符和更多的字段。是否有我可以使用的表达语言框架或 JAva 库或任何开源框架来使其成为通用解决方案
查看完整描述

2 回答

?
拉莫斯之舞

TA贡献1820条经验 获得超10个赞

假设您对稍微不太方便的形式感到满意


import com.google.common.base.Equivalence;

...

metaData.add(new MetaData(Person.class, Equivalence.equals(), "name", "John", "group", "SIGMA"));

最简单的基于 Java 的解决方案是将内省实例的类添加到元数据中,参数化比较对象的方式(我在这里使用了 Guava,你也可以使用普通的 Java 8 BiPredicate<T super Person>)并指定属性的名称。


执行时,您可以利用它java.beans.Introspector#getBeanInfo()来发现 getter 和 setter 名称(这是克服各种 Java Beans 怪胎的最安全方法,例如布尔属性、完全大写的属性名称等。 - 我建议更喜欢它而不是基于字符串替换的半生不熟的工具)并相应地调用它们。


您可以使用以下方法进一步缩短通话时间


metaData.add(new MetaData<Person>(Equivalence.equals(), "name", "John", "group", "SIGMA"));

(类可以使用称为类型标记或类型文字的反射黑客派生)


或创建自己的子类,例如EqualsMetadataor PersonMetadata,您就明白了。


或者,您可以完全避免(普通的)反射并使用方法引用定义所有内容:


metaData.add(new MetaData(Equivalence.equals(), Person::getName, Person::setName, "John", Person::getGroup, Person::setGroup, "SIGMA"));

它更长但类型更安全 - 您可以确保参数John并SIGMA与 getter 和 setter 类型相对应。


要使用外部工具实现它,我建议查看 mapstruct.org。Dozer 也是可能的,但是在我们的项目中,由于基于生成代码的性能和哲学,我们迁移到 mapstruct,恕我直言,它可以更好地了解转换过程中真正发生的情况。


查看完整回答
反对 回复 2021-12-30
?
慕虎7371278

TA贡献1802条经验 获得超4个赞

主要有以下三个坑:

  • 首先,您需要某种从字段名称到 getter/setter 方法的映射,以从 POJO 模型类访问字段值。

  • 其次,各个字段的数据类型不同。因此,如果字段值没有实现,您需要一个不同的比较器Comparable。你使用的类型实现了Comparable,所以你很好。

  • 第三,Java 不能Object以泛型的方式来转换内置值类型;即 getter 方法的签名通常不匹配。您已经通过为诸如 int 之类的琐碎数据类型选择盒装类型来避免这种情况。

要使所有这些工作正常运行,您需要执行以下步骤:

  1. 因此,在第一步中,您需要构建一个 getter 方法的存储库。即类似的东西HashMap<String, Function<Person, Object>> map1。您可以通过map1.put("name", Person::getName)或通过反射显式添加所有 getter 来静态执行此操作。

  2. 接下来,您需要为 setter 构建一个存储库。即类似的东西HashMap<String, BiConsumer<Person, Object>> map2。同样,您可以使用反射或显式初始化。在后一种情况下,还有另一个陷阱,因为 setter 的泛型类型转换不再是协变的。这可以通过双重未经检查的强制转换来处理:

    map2.put("name", (BiConsumer)(BiConsumer<Person, String>)Person::setName);

    由于 Java 的类型擦除,所有 setter 实际上都具有相同的匹配方法签名。

  3. 假设您的所有值类型都实现Comparable了下一步是非常简单的。
    调用您的getter从存储库,比较值(注意空的),并应用所需要的比较操作符(= 0< 0> 0或其他) -顺便说一句:你真的应该使用enumMetaData::comparator,而不是字符串。

  4. 如果需要,通过从存储库调用 setter 来分配结果值。

如果您不需要字符串作为字段名称的键,您可以加入步骤 1 和步骤 2。只需使用 getter 方法引用作为(强类型)键而不是名称。现在您只需要一个从 getter 映射到 setter 的存储库。IE:

HashMap<Function<Person, Object>, BiConsumer<Person, Object>> map;

这从 getter(用作键)映射到匹配的 setter 方法。
如果需要,我更喜欢这种方式,因为强类型值不易出错且重构更安全。


查看完整回答
反对 回复 2021-12-30
  • 2 回答
  • 0 关注
  • 194 浏览

添加回答

举报

0/150
提交
取消
微信客服

购课补贴
联系客服咨询优惠详情

帮助反馈 APP下载

慕课网APP
您的移动学习伙伴

公众号

扫描二维码
关注慕课网微信公众号