Java Record 自定义构造函数

问题描述 投票:0回答:5

我有一个包含列表的java记录

public record Zoo(List<Animal> animals ) {

     public Zoo(Collection<Animal> animals) {
         this(new ArrayList<>(animals));
     }

     ...
}

但是,动物没有按顺序排列,我想创建动物排序的记录。这在Java记录中可能吗?

在普通的java类中,我可以有

public class Zoo {
   ...
   public Zoo(List<Animal> animals) {
     this.animals = animals.sort(someComparator);
   } 

}
java record
5个回答
11
投票

您可以对“普通 Java 类”执行相同的操作。

import java.util.Collections;

public record Zoo(List<Animal> animals) {

  /*
   * The canonical constructor copies the 'animals' list so that both
   * constructors have consistent behavior (i.e. 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) {
    animals = new ArrayList<>(animals);
    animals.sort(/* comparator */);
    this.animals = Collections.unmodifiableList(animals);
  }

  // a non-canonical constructor; must delegate to canonical constructor
  public Zoo(Collection<Animal> animals) {
    this(new ArrayList<>(animals));
  }
}

第一个构造函数是规范构造函数的显式声明。来自java.lang.Record的文档:

记录类具有以下强制成员: 规范构造函数,它必须至少提供与记录类一样多的访问权限,并且其描述符与记录描述符相同;每个组件对应一个私有的final字段,其名称和类型与组件相同;每个组件对应的公共访问方法,其名称和返回类型与组件相同。如果未在记录正文中显式声明,则会提供这些成员的隐式实现。

[...]

为规范构造函数或访问器方法提供显式声明的主要原因是验证构造函数参数、对可变组件执行防御性副本或规范化组件组(例如将有理数减少到最低项。)

注意所有其他构造函数最终必须委托给规范构造函数。


4
投票

为了避免列表与集合的问题,为什么不直接将 Zoo 定义为具有

Collection<Animal>
而不是
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;
    }
}

如果客户端代码确实需要

animals()
返回
List
而不是
Collection
,您可以添加自定义访问器:

    public List<Animal> asList() {
        return (List<Animal>) animals;
    }

此转换是安全的,因为规范构造函数永远不会将任何不是

animals
的类型分配给
ArrayList
。我坚持使用
ArrayList
,因为那是在你原来的例子中,但默认情况下记录是不可变的,所以拥有一个可变的成员有点值得怀疑。如果不需要可变性,可以将构造函数的最后一行替换为

this.animals = List.copyOf(list);

或者甚至将构造函数的最后三行替换为

this.animals = animals.stream().sorted().toList();

1
投票

建议使用

this(..)
的答案有一个限制,即
this()
必须是第一个语句。

要克服该限制,您可以使用静态构造函数,例如:

public record Zoo(List<Animal> animals) {

     public static Zoo fromAnimals(Collection<Animal> animals) {
         List<Animal> listAnimals = new ArrayList<Animal>(animals);
         listAnimals.sort(Animal::compareTo);

         // ... any other pre-processing, possibly using additional parameters

         return new Zoo(listAnimals);
     }

     ...
}

0
投票

您可以尝试使用Stream API:

public record Zoo(List<Animal> animals ) {

     public Zoo(Collection<Animal> animals) {
         this(animals.stream().sorted(someComparator).collect(Collectors.toList()));
     }

     ...
}

0
投票

请记住,在构造函数完成运行之前,您的输入不是最终的,因此您可以按如下方式覆盖记录类构造函数:

public record Zoo(List<animal> animals ) {
     public Zoo {
        Collections.sort(animals, your_comparator);
    }
}
© www.soinside.com 2019 - 2024. All rights reserved.