Generate getters in the Builder class
Sometimes there is a need to access to a Builder getter to do extra logic.
@Value.Immutable
public abstract class Foo {
public abstract double price();
public abstract Instant pricedModifiedOn();
public static abstract class Builder {
abstract double price(); // Builder getter access
abstract Builder setPrice(double price);
abstract Builder setPriceModifiedOn(Instant priceOn);
public abstract Foo build();
public Builder setThePrice(double price) {
if(price() != price) { setPrice(price); setPriceModifiedOn(Instant.now()); }
}
}
}
There's pre-canned answer to this: If your logic requires complex multistep construction with getters or "is set" queries, please, resort to using Modifiable companion class, generated by @Value.Modifiable annotation. When using builder, if you set something, then you usually know what you've set, so why need to query?
If the Builder.from() is used, you probably don't know what is the original value in the Builder.
I would like to create an Immutable entity not a Modifiable entity, however during the build phase I may need to query some fields. I think it's a valid request. @AutoValue has this functionality already, and I think @Value.Immutable should have it.
The builder getter should have always package visibility in order the logic be internal to the original definition class (like provided in the above example).
You use Modifiable object as a builder, you can even use styles so it can be named *Builder and not Modifiable*. Then, there's toImmutable() method (also, rename-able using styles). Please, take a look at Guava library, and compare the API of ImmutableList.Builder with ArrayList. Basically modifiable class is your arraylist which you can manipulate until you create an immutable copy.
Thank you @elucash.
Can I use the @Value.Modifiable as a static class inside the @Value.Immutable class as we do for Builders? In my understanding to be able to use toImmutable we need to register both @Value.Immutable and @Value.Modifiable for an entity, but then the main Immutable entity will be different from that one.
By chance, do you have any example/documentation that uses Modifiable as a builder for Immutable entities? I would be able to control the setters of the Modifiable entity as shown in the above example.
Many thanks in advance.
Can I use the @Value.Modifiable as a static class inside the @Value.Immutable class as we do for Builders?
It's a standalone class, cannot be generated as nested to Immutable. This should be only cosmetic problem in practice.
By chance, do you have any example/documentation that uses Modifiable as a builder for Immutable entities?
http://immutables.github.io/immutable.html#modifiable
Here's an example of how I would create builder-in-disguice using modifiable companion class.
@Value.Style(
create = "new",
toImmutable = "build",
typeModifiable = "*Builder")
@Value.Immutable
@Value.Modifiable
public interface MyValue {
int getAttr();
String getButtr();
// just as an example, not required
static MyValueBuilder builder() {
return new MyValueBuilder();
}
// sample usage
public static void main(String... args) {
MyValueBuilder builder = MyValue.builder()
.setAttr(1)
.setButtr("2");
if (builder.attrIsSet()) {
int attr = builder.getAttr();
}
MyValue v = builder.build();
}
}
Here's additional example which mimicks initial example
@Value.Immutable
public abstract class Foo {
public abstract double price();
public abstract Instant pricedModifiedOn();
public static abstract class Builder extends FooBuilder {
public FooBuilder setThePrice(double price) {
if (price() != price) { setPrice(price); setPriceModifiedOn(Instant.now()); }
return this;
}
}
}
The problem with this approach is that setThePrice should be called first, as return type of inherited methods would be FooBuilder, so setThePrice would not be visible after first setter invokation returning FooBuilder.
Another solution might be using "normalization" functionality.
@Value.Immutable
public abstract class Foo {
public abstract double price();
@Value.Default
public double newPrice() {
return price();
}
public abstract Instant pricedModifiedOn();
@Value.Check
Foo normalize() {
if (Double.doubleToLongBits(price()) != Double.doubleToLongBits(newPrice())) {
return ImmutableFoo.builder()
.from(this)
.price(newPrice())
.pricedModifiedOn(Instant.now())
.builder();
}
return this;
}
}
// ...
Foo oldFoo = ...
Foo newFoo = ImmutableFoo.builder()
.from(oldFoo)
.newPrice(computeNewPrice())
.build();
// price/timestamp will be auto-updated if differs
Thank you @elucash for you priceless support on this. Much appreciated.
The @Value.Modifiable solution is really not very clean.
The normalisation solution it seems a bit better, but has also issues:
-
Two public methods for updating the price (
setPriceandsetNewPrice). If users usesetPricethepriceModifiedOnis not updated, violating an important requirement. Initially I was thinking that I could hide (package visibility) thesetPricemethod and force users to use always the other, but then I realise that when I am loading from the database I need to usesetPricebecause I need to maintain the originalpriceModifiedOnfield. -
Json representation of
Fooentity can't have another unnecessary property "newPrice".
I still think that the cleanest (and also simplest and easy to use) solution to solve this particular problem is having package level getters in the ImmutableFoo.Builder. It would be great if we could activate then using the @Value.Style settings.
I would also find having getters on generated Builders useful in a particular use case (unrelated to this one; re. "if you set something, then you usually know what you've set, so why need to query?" in my case, I'm initializing imagine like nested structures, and want to get a value I've previously set using a literal, without having to copy/paste it... that can be worked around by assigning the literal to a var and then using it in both setters, of course (that's what I'm currently doing).
Thank you for the tip with Modifiable - I've tried that, and understood that this then creates 3 classes, a @Modifiable *Builder (because of typeModifiable = "*Builder"), and an *Immutable, with a nested public static final class Builder ... that's a bit too confusing, and too expensive a price to pay, for just wanting getters on the Builder. Also the @Modifiable *Builder won't extend an inner Builder class from the hand written @Value.Immutable @Value.Modifiable - I understand why (because it extends that type), just saying this to clarify why the use of @Modifiable is not a straight forward solution to get a (single) Builder with getters..
I also have a use case that having getter on builder is necessary. I'm integrating dtos created with immutables with mapstruct mapping entities with joins. To do that I'm dealing with @AfterMapping methods. Those aftermapping methods provide me as parameter the builder object before finishing the mapping. But to complement the already mapped builder object I would need to know which values was mapped (the id and other values).
But couldn't find the getter on the builder. So I will need to build the object just to get the values and then build again 😞