【问题标题】:Java 8 Consumer with additional parameters带有附加参数的 Java 8 Consumer
【发布时间】:2021-03-10 05:10:26
【问题描述】:

我想编写一个通用的 DTO 更新程序,它会更新它的一些属性。我的对象如下所示:

class Person {
    String firsName;
    String lastName;
    Integer age;
    setter/getter
}

我有一个枚举,它定义了哪些字段可以被覆盖:

enum OverriddenType {
    FIRST_NAME,
    LAST_NAME,
    AGE
}

而不是像这样执行“硬编码”设置器...

public void override(Person person, Map<OverriddenType,Object> newValuesForProperties) {
    newValuesForProperties.forEach((type,value)) -> {
        if(type == OverriddenType.FIRST_NAME) {
            person.setFirstName((String)value);
        } else if(type == OverriddenType.LAST_NAME) {
            person.setLastName((String)value);
        } else if(type == OverriddenType.AGE) {
            person.setAge((Integer)value);
        }
    });
}

... 我想使用一些 Java 8 函数/消费者特性来定义每个枚举中的设置器。我尝试过这样的事情:

enum OverriddenType {
    FIRST_NAME(p -> p.setFirstName(???????)),  //don't know what to set here since the value is not here
    LAST_NAME(p -> p.setLastName(???????)),
    AGE(p -> p.setAge(???????));
    
    Consumer<Person> consumer;
    OverriddenType(Consumer<Person> consumer) {
        this.consumer = consumer;
    }
    public Consumer<Person> getConsumer();
}

但正如您所见,我无法在此处传递动态值。也不知道逻辑会是什么样子,但我会想象这样的事情:

public void override(Person person, Map<OverriddenType,Object> newValuesForProperties) {
    newValuesForProperties.forEach((type,value)) -> 
        type.getConsumer().accept(person, value) // this doesn't exist, but I can imagine some additional parameter passing
    );
}

你能为我提供一个解决方案吗?它可以工作吗?我担心传入的值是动态的,因此它不适用于枚举..

【问题讨论】:

标签: java java-8 functional-programming java-stream consumer


【解决方案1】:

setter 方法是BiConsumer&lt;T,U&gt;,其中T 是对象实例,U 是值。

enum OverriddenType {
    FIRST_NAME((person, value) -> person.setFirsName((String) value)),
    LAST_NAME ((person, value) -> person.setLastName((String) value)),
    AGE       ((person, value) -> person.setAge((Integer) value));

    private final BiConsumer<Person, Object> setter;

    private OverriddenType(BiConsumer<Person, Object> setter) {
        this.setter = setter;
    }

    public void override(Person person, Object value) {
        this.setter.accept(person, value);
    }
}
public void override(Person person, Map<OverriddenType, Object> newValuesForProperties) {
    newValuesForProperties.forEach((type, value) -> type.override(person, value));
}

【讨论】:

    【解决方案2】:

    您需要一个BiConsumer,它可以使用目标对象和新值:

    enum OverriddenType {
        FIRST_NAME((p, v) -> p.setFirstName(v)),
        LAST_NAME((p, v) -> p.setLastName(v)),
        AGE((p, v) -> p.setAge(v));
        
        BiConsumer<Person> consumer;
        OverriddenType(BiConsumer<Person, Object> consumer) {
            this.consumer = consumer;
        }
    
        public BiConsumer<Person> getConsumer();
    }
    

    (如果您的 setter 接受 Object,您甚至可以使用方法引用:

        OBJECT(Person::setObject)
    

    )

    与其直接暴露消费者,不如考虑在您的枚举中定义一个公共的assignset 方法并使消费者对外部不可见(信息隐藏):

    public void assign(final Person target, final Object newValue) {
      this.consumer.accept(target, newValue);
    }
    

    然后调用FIRST_NAME.assign(person, "new name")

    【讨论】:

    • Bi Consumer 是错误的,因为它的名称中不应包含空格。 --- Person::setFirstName 是错误的,因为它需要一个 String 参数,但 consumer 具有 Object 作为值类型。
    • Person 没有 setObject 方法。 --- p.setFirstName(v) 将无法编译,因为 vObject,但 setFirstName 的参数是 String
    【解决方案3】:

    根据定义,Consumer 只接受一个参数。

    由于您需要同时传递要操作的对象和新值,因此您必须使用允许传递两个参数的接口。幸运的是,有the BiConsumer interface

    BiConsumer<Person, String> personFirstNameSetter = Person::setFirstName;
    

    注意这里使用方法引用大致相当于写(p, fn) -&gt; p.setFirstName(fn)

    但是,这引入了一个小问题,因为您需要知道(并指定)参数的类型,并且您的枚举想要混合类型(前两个将返回 BiConsumer&lt;Person,String&gt;,最后一个可能是 @987654327 @。

    【讨论】:

    • 所以你的答案是不同的值类型存在问题,而没有为该问题提供解决方案?那么,不是很有用,是吗?
    • @Andreas:我不考虑接受Object 并将其转换为可行的解决方案(因为传入的值仍然需要知道正确的类型,除非现在它无法告诉它们参数类型)。而且我对 OPs 问题的了解还不够,无法提供更好的解决方案。不过,感谢您提供宝贵的反馈。
    • 查看问题中间的override()方法,这表明覆盖值以Map&lt;OverriddenType, Object&gt;的形式出现,因此需要将Object转换为适当的值类型,并且OP选择简单地投射价值。问题不在于强制转换值,而在于使用enum 注册的BiConsumer 代替多路if 语句。
    猜你喜欢
    • 2014-09-06
    • 1970-01-01
    • 2015-11-19
    • 2016-07-12
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多