如何围绕setter创建通用方法?

时间:2018-08-07 05:33:15

标签: java amazon-web-services dao lombok

我想在具有20多个字段的DynamoDB上定义DAO。在Java中,我可以使用Lombok并执行类似的操作以避免一堆样板代码。

@Setter
@Getter
@DynamoDBTable("MyTable")
public class MyDAO {
    //FIELD_1, FIELD_2, FIELD_3 defined as static final String elsewhere

    @DynamoDBAttribute(attribute = FIELD_1) 
    private final String field1;

    @DynamoDBAttribute(attribute = FIELD_2)
    private final Long field2;

    @DynamoDBAttribute(attribute = FIELD_3)
    private final int field3;
    ...
}

问题是,如果我有对每个字段执行如下操作的方法,那么我将一遍又一遍地复制代码,因为步骤2中的设置方法会有所不同,而步骤3中的字段名称将是不同(例如,第一个是setField1,第二个是setField2)。

public void addField1(String key, String field1Value) {
    //Wrap some retry logic and error handling around the following
    // 1. get DAO for key
    // 2. set FIELD_1 to field1Value in DAO if not set
    // 3. put DAO in DynamoDB using attribute name FIELD_1
}

public void addField2(String key, Long field2Value) {
    //Wrap some retry logic and error handling around the following
    // 1. get DAO for key
    // 2. set FIELD_2 to field2Value in DAO if not set
    // 3. put DAO in DynamoDB using attribute name FIELD_2
}

理想情况下,我希望使用下面的addField方法,并具有所有重试逻辑,因此我不必为每个字段重复所有操作。

private void addField(String fieldName, String key, Object value);

public void addField1(String key, String field1Value) {
    addField(FIELD_1, key, (Object) field1Value);
}

我已经尝试过在字段名称和BiConsumers之间建立映射

Map<String, BiConsumer<MyDAO, Object>> setterMap = 
    new HashMap<String, BiConsumer<MyDAO, Object>>(){{ 
        put(FIELD_1, MyDAO::setField1);
        put(FIELD_2, MyDAO::setField2);
    }};

private void addField(String fieldName, String key, Object value) {
    ...
    // 2. Use setterMap.get(fieldName).accept(value);
    ...
}

问题是我收到一条错误消息,说我无法将BiConsumer<MyDAO, String>强制转换为BiConsumer<MyDAO, Object>

这是唯一的方法-为每种类型创建单独的映射和方法吗?还是有一种更优雅的方式做到这一点?

2 个答案:

答案 0 :(得分:2)

好吧,我不认为可以使用Map 如果来保持类型安全。相反,这是我会做的:

1)我将创建一个像这样的特殊类:

@AllArgsConstructor
@Getter
final class FieldDefinition<T> {

    private final String name;
    private final BiConsumer<MyDAO, T> setter;
}

2)然后,我将在MyDAO(或者更好的是,在MyDAO附近的一些辅助对象中)中创建常量,如下所示:

static final FieldDefinition<String> FIELD_1_DEF = new FieldDefinition<>(FIELD_1, MyDAO::setField1);

3)最后,我将创建以下类型安全的addField方法:

private <T> void addField(FieldDefinition<T> fieldDefinition, String key, T value) {
    // ...
    fieldDefinition.getSetter().accept(this, value);
    // ...
}

应该这样称呼的人:

myDao.addField(FIELD_1_DEF, key, value);

答案 1 :(得分:2)

动态选择方法实际上不适用于功能接口。围绕方法选择对代码进行参数化最好通过反射而不是使用功能接口来完成。

使用BiConsumer接口难以实现逻辑的主要原因是,从技术上讲,您仍然必须为每个字段(无论是使用lambda,方法引用还是类)为其提供静态实现。 )。

这是一个基于反射的实现示例:

private void addField(String fieldName, String key, Object value) {
    MyDAO.class.getDeclaredField(fieldName).set(value, key);
}

因此,我只是将setterMap映射为键到字段名称的映射,并像这样使用它:

private void addField(String key, Object value) {
    String field = setterMap.get(key);
    MyDAO.class.getDeclaredField(field).set(value, key);
}