【问题标题】:Java Record custom constructorJava Record 自定义构造函数
【发布时间】:2021-12-02 20:22:31
【问题描述】:

我有一个包含列表的 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


    【解决方案1】:

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

    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) {
        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));
      }
    }
    

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

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

    [...]

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

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

    【讨论】:

    • 如果要将不可变的List&lt;Animal&gt; 传递给构造函数,则需要将其强制转换为Collection&lt;Animal&gt;
    • 您还可以对列表的副本进行排序,然后将其设置为字段animals。尽管Zoo (Collection&lt;Animal&gt;) 中的副本变得多余。
    • 我发现这是一个很好的问题,可以揭示record 的问题。
    【解决方案2】:

    您可以尝试使用 Stream API:

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

    【讨论】:

    • 隐式构造函数Zoo(List&lt;Animal&gt; animals) 在您的代码中有效。如果直接调用,animals不会被排序。
    【解决方案3】:

    为了避免 List 与 Collection 的问题,为什么不将 Zoo 定义为具有 Collection&lt;Animal&gt; 而不是 List&lt;Animal&gt;

    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();
    

    【讨论】:

      猜你喜欢
      • 2011-04-11
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2015-02-12
      • 2021-11-19
      • 1970-01-01
      • 1970-01-01
      • 2022-01-02
      相关资源
      最近更新 更多