I have a java record that takes in a list
public record Zoo(List<Animal> animals ) {
public Zoo(Collection<Animal> animals) {
this(new ArrayList<>(animals));
}
...
}
However, the animals are not in sorted order, and I want to create record where animals are sorted. Is this possible in Java record?
In plain java class, I could have
public class Zoo {
...
public Zoo(List<Animal> animals) {
this.animals = animals.sort(someComparator);
}
}
CodePudding user response:
You can do the same as with your "plain Java class".
public record Zoo(List<Animal> animals) {
/*
* This method is used to unify the behavior of both constructors (i.e. they
* now both create a copy) while also avoiding the problem of the list being
* copied twice when the non-canonical constructor is used.
*
* FIXME: In the case of the non-canonical constructor, the list will be sorted
* twice with this implementation. Given the current implementation
* of 'ArrayList.sort', however, the second sort should be relatively
* quick.
*
* If someone can think of a way to also avoid double-sorting, please
* let me know.
*/
private static List<Animal> sort(List<Animal> list, boolean copy) {
if (copy) {
list = new ArrayList<>(list);
}
list.sort(someComparator);
// NOTE: I recommend considering if the list should be modifiable or if
// it should be wrapped in an unmodifiable list.
return list;
}
// explicit canonical constructor
public Zoo(List<Animal> animals) {
this.animals = sort(animals, true);
}
// a non-canonical constructor; must delegate to canonical constructor
public Zoo(Collection<Animal> animals) {
this(sort(new ArrayList<>(animals), false));
}
}
The first constructor is an explicit declaration of the canonical constructor. From the documentation of java.lang.Record:
A record class has the following mandated members: a canonical constructor, which must provide at least as much access as the record class and whose descriptor is the same as the record descriptor; a private final field corresponding to each component, whose name and type are the same as that of the component; a public accessor method corresponding to each component, whose name and return type are the same as that of the component. If not explicitly declared in the body of the record, implicit implementations for these members are provided.
[...]
The primary reasons to provide an explicit declaration for the canonical constructor or accessor methods are to validate constructor arguments, perform defensive copies on mutable components, or normalize groups of components (such as reducing a rational number to lowest terms.)
Note all other constructors must eventually delegate to the canonical constructor.
CodePudding user response:
You could try to use the Stream API:
public record Zoo(List<Animal> animals ) {
public Zoo(Collection<Animal> animals) {
this(animals.stream().sorted(someComparator).collect(Collectors.toList()));
}
...
}