【问题标题】:Java Framework/pattern to modify valueJava 框架/模式来修改值
【发布时间】:2018-11-05 18:04:28
【问题描述】:

我有一个对象人

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

元数据

public class MetaData {
    private String fieldName;
    private String comparator;
    private String fieldValue;
    private String newFieldName;
    private String newFieldValue;

    public MetaData(String fieldName, String comparator, String fieldValue, String newFieldName, String newFieldValue) {
        this.fieldName = fieldName;
        this.comparator = comparator;
        this.fieldValue = fieldValue;
        this.newFieldName = newFieldName;
        this.newFieldValue = newFieldValue;
    }

    public String getFieldName() {
        return fieldName;
    }

    public void setFieldName(String fieldName) {
        this.fieldName = fieldName;
    }

    public String getComparator() {
        return comparator;
    }

    public void setComparator(String comparator) {
        this.comparator = comparator;
    }

    public String getFieldValue() {
        return fieldValue;
    }

    public void setFieldValue(String fieldValue) {
        this.fieldValue = fieldValue;
    }

    public String getNewFieldName() {
        return newFieldName;
    }

    public void setNewFieldName(String newFieldName) {
        this.newFieldName = newFieldName;
    }

    public String getNewFieldValue() {
        return newFieldValue;
    }

    public void setNewFieldValue(String newFieldValue) {
        this.newFieldValue = newFieldValue;
    }
}

我得到了 person 列表。对于每个人,我必须通过元数据(基于 ID ) 对于每个元数据,我必须查看要匹配的字段以及要替换的值

例如

public class TestPersonModifier {

    public static void main(String[] args) {
        List<Person> personList = new ArrayList<>();
        personList.add(new Person("John", 22, "London", "BETA", "ABC"));
        personList.add(new Person("Mathew", 44, "Glasgow", "ALPHA", "XYZ"));

        List<MetaData> metaData = new ArrayList<>();

        metaData.add(new MetaData("name", "EQUALS", "John", "group", "SIGMA"));
        metaData.add(new MetaData("name", "EQUALS", "Mathew", "address", "Singapore"));

        //g
        personList.forEach(p -> {

            metaData.forEach(m -> {

                if (m.getFieldName().equals("name")) {
                    if (m.getComparator().equals("EQUALS")) {

                        if (p.getName().equals(m.getFieldValue())) {

                            if (m.getNewFieldName().equals("group")) {

                                System.out.println(p.getId() + "::The group value changed from " + p.getGroup() + " to " + m.getNewFieldValue());
                                p.setGroup(m.getNewFieldValue());
                            }
                            if (m.getNewFieldName().equals("address")) {
                                System.out.println(p.getId() + "::The address value changed from " + p.getAddress() + " to " + m.getNewFieldValue());

                                p.setGroup(m.getNewFieldValue());
                            }
                        }
                    }
                }
            });
        });

    }
}

如上所述,它将非常笨拙,因为会有更多的比较器运算符和更多的字段。

是否有我可以使用的表达式语言框架或 JAva 库或任何开源框架来使其成为通用解决方案

【问题讨论】:

  • 投票结束问题的人应该注意发表评论。

标签: java java-8 expression comparator


【解决方案1】:

假设您对不太方便的表单感到满意

import com.google.common.base.Equivalence;
...
metaData.add(new MetaData(Person.class, Equivalence.equals(), "name", "John", "group", "SIGMA"));

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

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

您可以使用

进一步缩短通话时间
metaData.add(new MetaData<Person>(Equivalence.equals(), "name", "John", "group", "SIGMA"));

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

或者创建自己的子类,例如EqualsMetadataPersonMetadata,你懂的。


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

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

更长但类型更安全 - 您可以确保参数 JohnSIGMA 对应于 getter 和 setter 类型。


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

【讨论】:

    【解决方案2】:

    主要有三个陷阱:

    • 首先,您需要某种从字段名称到 getter/setter 方法的映射,以便从 POJO 模型类访问字段值。
    • 其次,个别字段的数据类型不同。所以如果字段值没有实现Comparable,你需要一个不同的比较器。你使用的类型实现了Comparable,所以你很好。
    • 第三,Java 不能以通用方式将内置值类型从Object 转换为Object;即 getter 方法的签名通常不匹配。您已经通过为 int 等琐碎数据类型选择盒装类型来避免这种情况。

    要让所有这些工作,您需要采取以下步骤:

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

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

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

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

    3. 假设你的所有值类型都实现了Comparable,下一步就很简单了。
      从存储库中调用您的 getter,比较值(注意空值)并应用所需的比较运算符(= 0&lt; 0&gt; 0 或其他) - 顺便说一句:你真的应该使用enum MetaData::comparator 而不是字符串。

    4. 如果需要,通过调用存储库中的设置器来分配结果值。

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

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

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

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2018-01-20
      • 2017-08-22
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多