Have you considered generating Builders which allow some nullable attributes yet enforce the presence of all other mandatory (required) attributes at compile time, instead of by exception at runtime by validation on build() ?
See e.g. my Person.java for a (hand-written, example) of the pattern I mean. -- According to @eamonnmcmanus apparently it may even be possible to use Java generics to lift the requirement that properties need to be set in a particular order, with the generics keep track of which properties have been set (I do not know how to do this).
FTR: I've originally proposed the same idea to google/auto as well, but it's less of a fit there because with AutoValue the user still has to write the public facing API as it never generate APIs, only implement APIs that the user has written.
Hi @vorburger, thank you for proposal! You may find interesting to read previous discussions on this: https://groups.google.com/forum/#!topic/immutables/4VqoSvB0IRQ
The trade of seems to be a lot of generated classes/interfaces for large amount of attributes, for small number, the advantage of using builder rather than constructor is questionable. There was related ideas to generate lots of interfaces (needed for compile-safety) and remove redundant ones (rewriting bytecode) as post compile optimization.
IMO this would be a better style of implementation (as referenced on https://github.com/immutables/immutables/issues/450)
http://blog.crisp.se/2013/10/09/perlundholm/another-builder-pattern-for-java
@elucash one of the comments from @androidfred provided link mention an even older block post Step Builder pattern with the same idea. Currently with @Value.Style(strictBuilder = true) a strict builder is generated. What do you think about @Value.Style(stepBuilder = true) to generate such a step builder?
Also @svlada has a blog post on this topic of step builder pattern http://www.svlada.com/step-builder-pattern.
So far, I haven't found a plug-in for Eclipse that provides Step Builder code generation feature.
I have created a github repository with the intention of creating an Eclipse plug-in that will provide
support for Step Builder pattern generation: https://github.com/svlada/alyx
IMO a plugin isn't the right tool :wink:
@agebhar1 @androidfred @vorburger org.immutables:value:2.3.3 @Value.Style(stagedBuilder = true)
@elucash :+1: - thx
@elucash Can't thank you enough! Works exactly as desired as far as I can tell!
import org.immutables.value.Value;
import java.util.Optional;
@Value.Immutable
@Value.Style(strictBuilder = true)
interface Person {
String name();
String title();
Optional<String> department();
}
import org.immutables.value.Value;
import java.util.Optional;
@Value.Immutable
@Value.Style(stagedBuilder = true)
interface Individual {
String name();
String title();
Optional<String> department();
}
import org.junit.Assert;
import org.junit.Test;
import java.util.Optional;
public class PersonAndIndividualTests {
@Test(expected = IllegalStateException.class)
public void personBuildCalledWithoutRequiredArguments() throws Exception {
ImmutablePerson.builder().build();
}
@Test(expected = NullPointerException.class)
public void personBuildCalledWithRequiredArgumentsNull() throws Exception {
ImmutablePerson.builder().name(null).title(null).build();
}
// @Test
// public void personBuildCalledWithNonRequiredArgumentsNull() throws Exception {
// ImmutablePerson.builder().name("John").title("Dev").department(null).build(); doesn't compile
// }
@Test
public void personBuildCalledWithoutNonrequiredArguments() throws Exception {
ImmutablePerson john = ImmutablePerson.builder().name("John").title("Dev").build();
Assert.assertEquals("John", john.name());
Assert.assertEquals("Dev", john.title());
Assert.assertEquals(Optional.empty(), john.department());
}
@Test
public void personBuildCalledWithAllArguments() throws Exception {
ImmutablePerson john = ImmutablePerson.builder().name("John").title("Dev").department("IT").build();
Assert.assertEquals("John", john.name());
Assert.assertEquals("Dev", john.title());
Assert.assertEquals(Optional.of("IT"), john.department());
}
// @Test
// public void individualBuildCalledWithoutRequiredArguments() throws Exception {
// ImmutableIndividual.builder().build(); doesn't compile (YES THANK YOU SO MUCH)
// }
@Test(expected = NullPointerException.class)
public void individualBuildCalledWithRequiredArgumentsNull() throws Exception {
ImmutableIndividual.builder().name(null).title(null).build();
}
// @Test
// public void individualBuildCalledWithNonRequiredArgumentsNull() throws Exception {
// ImmutableIndividual.builder().name("John").title("Dev").department(null).build(); doesn't compile
// }
@Test
public void individualBuildCalledWithoutNonrequiredArguments() throws Exception {
ImmutableIndividual john = ImmutableIndividual.builder().name("John").title("Dev").build();
Assert.assertEquals("John", john.name());
Assert.assertEquals("Dev", john.title());
Assert.assertEquals(Optional.empty(), john.department());
}
@Test
public void individualBuildCalledWithAllArguments() throws Exception {
ImmutableIndividual john = ImmutableIndividual.builder().name("John").title("Dev").department("IT").build();
Assert.assertEquals("John", john.name());
Assert.assertEquals("Dev", john.title());
Assert.assertEquals(Optional.of("IT"), john.department());
}
}
@elucash you rock! Tx for implementing this. Trying out Immutables more seriously now.. ;-)
Most helpful comment
@elucash :+1: - thx