Destructuring in Java and Kotlin
Destructuring a data class in Kotlin is intuitive. Consider a data class “Address”:
data class Address(val street: String, val city: String, val state: String)
val address = Address(street = "1 Bowerman Dr", city = "Beaverton", state = "OR")
The “street” and “state” fields can be deconstructed from this variable the following way:
val (street, _, state) = address
See how the city can be ignored using “_” as the placeholder.
Now, Java 21, enables a similar deconstruction of fields of a Java Record along these lines, a wee bit less intuitive than the Kotlin version as it works with the instanceof
operator(or with switch/case) and not outside of it:
record Address(String street, String city, String state) {
}
Address address = new Address("1 Bowerman Dr", "Beaverton", "OR");
if (address instanceof Address(var street, var city, var state)) {
assertThat(street).isEqualTo("1 Bowerman Dr");
assertThat(state).isEqualTo("OR");
}
// OR
switch (address) {
case Address(var street, var city, var state) -> {
assertThat(street).isEqualTo("1 Bowerman Dr");
assertThat(city).isEqualTo("Beaverton");
assertThat(state).isEqualTo("OR");
}
}
There are a few wrinkles here:
- Deconstructing requires the `instanceof` clause
- I may not be interested in deconstructing the city field but I am forced to provide a variable to hold the value of the field.
Point number 2 will be fixed with a new preview feature called “Unnamed patterns and variables”, using this the deconstructing looks similar to the Kotlin approach of using “_” for a placeholder for such fields:
if (address instanceof Address(var st, _, var state)) {
assertThat(st).isEqualTo("1 Bowerman Dr");
assertThat(state).isEqualTo("OR");
}
Nested Record Patterns
Consider a nested data class in Kotlin along these lines:
data class Address(val street: String, val city: String, val state: String)
data class Person(val name: String, val phone: String, val address: Address)
val person = Person(
name = "John Smith",
phone = "000-000-0000",
address = Address(street = "1 Bowerman Dr", city = "Beaverton", state = "OR")
)
Now say I want to get to the street and state for a person’s address, the way to do it in Kotlin would be something along these lines:
val (street, _, state) = person.address
Which works perfectly well, a similar approach works for Java 21 as well.
However, Java 21 expands on this with a way to get to the nested fields using a pattern and it looks something like this:
record Address(String street, String city, String state) {
}
record Person(String name, String phone, Address address) {
}
Person person = new Person("John Smith", "",
new Address("1 Bowerman Dr", "Beaverton", "OR"));
if (person instanceof Person(_, _, Address(var st, _, var state))) {
assertThat(st).isEqualTo("1 Bowerman Dr");
assertThat(state).isEqualTo("OR");
}
I have no big opinion on this, for my own code I would likely prefer to get to the nested instances using containing class fields.