Tengo un registro de Java que toma en una lista
public record Zoo(List<Animal> animals ) { public Zoo(Collection<Animal> animals) { this(new ArrayList<>(animals)); } ... }Sin embargo, los animales no están ordenados y quiero crear un registro donde se ordenen los animales. ¿Es esto posible en el registro de Java?
En la clase simple de Java, podría haber
public class Zoo { ... public Zoo(List<Animal> animals) { this.animals = animals.sort(someComparator); } }Puede hacer lo mismo que con su "clase simple de Java".
public record Zoo(List<Animal> animals) { /* * The canonical constructor copies the 'animals' list so that both * constructors have consistent behavior (ie they both result in * the list being copied). Plus, since you want to sort the list, it's * a good idea to create a copy anyway to avoid side-effects. * * FIXME: When the non-canonical constructor is used, the 'animals' list * will be copied twice. If anyone can think of a way to avoid * this, please let me know. */ // explicit canonical constructor public Zoo(List<Animal> animals) { this.animals = new ArrayList<>(animals); this.animals.sort(/* comparator */) } // a non-canonical constructor; must delegate to canonical constructor public Zoo(Collection<Animal> animals) { this(new ArrayList<>(animals)); } }El primer constructor es una declaración explícita del constructor canónico. De la documentación de java.lang.Record :
Una clase de registro tiene los siguientes miembros obligatorios: un constructor canónico , que debe proporcionar al menos tanto acceso como la clase de registro y cuyo descriptor es el mismo que el descriptor de registro; un campo final privado correspondiente a cada componente, cuyo nombre y tipo es el mismo que el del componente; un método de acceso público correspondiente a cada componente, cuyo nombre y tipo de devolución son los mismos que los del componente. Si no se declara explícitamente en el cuerpo del registro, se proporcionan implementaciones implícitas para estos miembros.
[...]
Las razones principales para proporcionar una declaración explícita para el constructor canónico o los métodos de acceso son para validar los argumentos del constructor, realizar copias defensivas en componentes mutables o normalizar grupos de componentes (como reducir un número racional a los términos más bajos).
Tenga en cuenta que todos los demás constructores eventualmente deben delegar al constructor canónico.
Para evitar los problemas con List vs. Collection, ¿por qué no simplemente definir el Zoo con Collection<Animal> en lugar de List<Animal> ?
public record Zoo(Collection<Animal> animals) { public Zoo(Collection<Animal> animals) { Objects.requireNonNull(animals, "animals is null"); List<Animal> list = new ArrayList<>(animals); Collections.sort(list); this.animals = list; } } Si hay un código de cliente que realmente necesita animals() para devolver una List en lugar de una Collection , puede agregar un acceso personalizado:
public List<Animal> asList() { return (List<Animal>) animals; } Esta conversión es segura ya que el constructor canónico nunca asignará ningún tipo a animals que no sea un ArrayList . Me quedé con ArrayList ya que estaba en su ejemplo original, pero los registros son inmutables de forma predeterminada, por lo que tener un miembro que sea mutable es algo cuestionable. Si no se necesita mutabilidad, puede reemplazar la última línea del constructor con
this.animals = List.copyOf(list);O incluso reemplazar las últimas tres líneas del constructor con
this.animals = animals.stream().sorted().toList();Podría intentar usar la API Stream:
public record Zoo(List<Animal> animals ) { public Zoo(Collection<Animal> animals) { this(animals.stream().sorted(someComparator).collect(Collectors.toList())); } ... }