Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Generic way to update pojos via getters and setters

Let's say I have POJO with getters and setters of different types. I want to write some generic algorithm for updating data from one to another based on just defining getters and setters via lambdas. I'm trying to create it this way

private static final Map<Function<Entity, Object>, BiConsumer<Entity, Object>> ACCESSORS = new HashMap
        <Function<Entity, Object>, BiConsumer<Entity, Object>>() {{
    put(Entity::getAreaCode, Entity::setAreaCode);
}});

Then I go through all entries applying target entity to them and if result of getter is not null then I want to apply corresponding setter for other entity.

But it won't work because Object is not castable to String. And I want to use it for different types not only String but also Integers, etc...

Is is solvable in some simple approach without creating special converter and associate it with every entry?

like image 944
user_x Avatar asked Jan 21 '19 16:01

user_x


People also ask

What can I use instead of getters and setters?

You may use lombok - to manually avoid getter and setter method. But it create by itself. The using of lombok significantly reduces a lot number of code.

Can POJO have getters and setters?

The POJO class must be public. It must have a public default constructor. It may have the arguments constructor. All objects must have some public Getters and Setters to access the object values by other Java Programs.

What will happen if getters and setters are made private?

The private getter/setter methods provide a place for adding extra behavior or error checking code. They can provide a place for logging state changes or access to the fields.

How do you generate getters and setters?

How to use: Select a struct type, then press Alt + Enter on Windows/Linux or ⌥ + ⏎ on macOS and choose Generate getter and setter from the list of available actions. Choose which fields should have these methods generated, then press OK to generate them.


1 Answers

Use something like

private static final List<BiConsumer<Entity,Entity>> ACCESSORS =
    Collections.unmodifiableList(Array.asList(
        (src,dst) -> dst.setAreaCode(src.getAreaCode()),
        (src,dst) -> dst.setOtherProperty(src.getOtherProperty())
        /* etc */
));

Then, you can loop over the list and apply each operation to two entities, like

static final void copyAll(Entity src, Entity dst) {
    ACCESSORS.forEach(op -> op.accept(src, dst));
}

The key point is that the actual property value type is handled within each BiConsumer but is not part of the generic signature anymore and therefore doesn’t need to be declared for ACCESSORS. It’s even more efficient, as it can handle primitive data types without boxing overhead.

The Map wasn’t an appropriate data structure for this task anyway, as for these functions, no meaningful lookup could be performed, so this is a data structure is only intended to be iterated over.

You can integrate the “copy only when non-null” logic with a generic helper method:

private static final List<BiConsumer<Entity,Entity>> ACCESSORS =
    Collections.unmodifiableList(Arrays.asList(
        copyWhenNonNull(Entity::getAreaCode, Entity::setAreaCode),
        copyWhenNonNull(Entity::getOtherProperty, Entity::setOtherProperty)
        /* etc */
));
private static <E,V> BiConsumer<E,E> copyWhenNonNull(
    Function<? super E, ? extends V> getter, BiConsumer<? super E, ? super V> setter) {
    return (src,dst) -> {
        V value = getter.apply(src);
        if(value != null) setter.accept(dst, value);
    };
}

The copyAll method doesn’t change. This even allows mixing unconditional copying of properties which can never be null with conditional copying.

like image 196
Holger Avatar answered Oct 23 '22 05:10

Holger