4 ms·
You'd probably have to manually write a Builder in the Person record class. Then the callsite might look something like this: val emily2 = emily1.newBuilde
by Benjammer 7y ago
You'd probably have to manually write a Builder in the Person record class. Then the callsite might look something like this:
val emily2 = emily1.newBuilder()
.lastName("Wells")
.age(26)
.build()
I think the inner Builder pattern is common enough in immutable java object implementations that they might want to include that fully in the Record class generation at some point. Most immutable object libraries that I've seen include a lot more functionality than Records, like builders.
- jeswin 7y agoWhy not do what JS does? val emily2 = { ...emily1, age: 30 }; This has many advantages, one being that it provably and declaratively creates a copy, which is not the case with the builder. In fact the obsession with methods (and hence the implied state) is what makes Java a terrible misfit for functional paradigms such as immutability.
- riquito 7y agoJS is not any better in that regard, that simple example is ignoring nested objects, where the outer one is copied and the inner are referenced (unless you use triple dots all the way down - tedious and error prone, or even simply inefficient if done by some recursive algorithm). The syntax is cool though
- sa46 7y agoI don't understand what you mean by provably. The Java example does the exact same thing as the JS version: create a shallow clone with a different age. Do you mean the builder might do something different? // Java val emily2 = emily1.toBuilder().age(30).build() As far as adding the spread operator to Java, I think you'd have to require a Spreadable interface to use it or limit usage of the spread operators to records. It's not clear to me that the expressive power is worth it over a builder.
- jeswin 7y agoIf a tool analyzed the JS AST, it could conclude (with fairly trivial analysis) that emily2 is a shallow clone of emily1. No such guarantee is possible with the Java version.
- hedora 7y agoBuilders are really only useful for small programs or classes that aren’t broadly used. Otherwise, at some point you need to add a field to Person, and then the compiler can’t tell you about all the places where your code fails to initialize the field. Worse, some people pass builders around. At that point, the problem of checking that you’ve passed a legal set of arguments when constructing objects is as hard as the halting problem. You can write tests to make sure your program is typesafe, but that takes time away from writing more useful tests.
- jfengel 7y agoI find Builders most useful when you've got a lot of optional arguments. For mandatory arguments (and most arguments should be mandatory), you should definitely use constructors instead, so that your tool kit can tell you when they change. Optional arguments with reasonable defaults don't present a problem for refactoring. It does make it hard to spot the cases where the reasonable defaults don't work. One trick is to temporarily add it to the constructor, review all the errors, and then remove the constructor arg.