Java新特性-JDK16中的Record类(怎么精简地表达不可变数据?)

Posted 袁新栋-jeff.yuan

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Java新特性-JDK16中的Record类(怎么精简地表达不可变数据?)相关的知识,希望对你有一定的参考价值。

Java新特性-JDK16中的Record类

1. 背景

从18年开始了解到java就用的就是jdk8,经历了两家公司,也都是JDK8的项目,这是故步自封还是稳中求胜呢,对于商业项目来讲需要考虑到的地方太多了,更新的价值点和风险点 ,最终的结果导向还是价值,升级后对于我们现在到底能带来多少送价值。但是对于我个人来讲我不去学习新的东西,那必然是故步自封了。

给你一个保守、粗暴的估计,你如果从 JDK 8 迁移到 JDK 17,并且能够恰当使用 JDK 8 以后的新特性的话,产品的代码量可以减少 20%,代码错误可以减少 20%,产品性能可以提高 20%,维护成本可以降低 20%。这些,都是实实在在的收益。拥抱 Java 新特性,掌握主动权------- 范学雷 (Oracle 首席软件工程师)

2. JAVA新特性-档案类

2.1 什么是档案类?

2.1.1 概括

  1. (Record)使用档案类增强 Java 编程语言,档案类(Record)是充当不可变数据的透明载体的类。记录可以被认为是名义元组

  2. 记录类是 Java 语言中的一种新类。记录类有助于用比普通类更少的仪式对普通数据聚合进行建模。

    记录类的声明主要由其状态的声明组成 ;然后记录类提交到与该状态匹配的 API。这意味着记录类放弃了类通常享有的自由——将类的 API 与其内部表示分离的能力——但作为回报,记录类声明变得更加简洁。

    更准确地说,记录类声明由名称、可选类型参数、标题和正文组成。标题列出了记录类的组件,它们是构成其状态的变量。(此组件列表有时称为状态描述。)

2.1.2 目标

  1. 设计一个面向对象的构造来表达简单的值聚合。
  2. 帮助开发人员专注于建模不可变数据而不是可扩展行为。
  3. 自动实现数据驱动的方法,例如equals和访问器。
  4. 保留长期存在的 Java 原则,例如名义类型和迁移兼容性。

2.2 为什么引入档案类

2.2.1 动机

人们普遍抱怨“Java 太冗长”或“仪式太多”。一些最严重的违规者是那些只不过是少数值的不可变 数据载体的类。正确编写这样一个数据载体类涉及许多低价值、重复、容易出错的代码:构造函数、访问器equalshashCodetoString、 等。

2.2.2 案例

设计一个圆形的对象:

package com.yuanxindong.study.record;

public final class Circle implements Shape 
    public final double radius;

    public Circle(double radius) 
        this.radius = radius;
    
    
    @Override
    public double area() 
        return Math.PI * radius * radius;
    


设计一个方形的对象:

package com.yuanxindong.study.record;

public final class Square implements Shape 
    public final double side;

    public Square(double side) 
        this.side = side;
    
    
    @Override
    public double area() 
        return side * side;
    


本着DRY原则(dont repeat yourself),我们是否可以进一步抽象,和简化代码呢?当你看到的时候会不会想到枚举类呢?

那这个时候record类就来了:

package com.yuanxindong.study.record;

public record Circle(double radius) implements Shape 
    @Override
    public double area() 
        return Math.PI * radius * radius;
    

可以发现我们不用,声明成员变量,也不需要构造方法了,就是多了一个类上面多了一个入参,并且其中档案类内置了缺省的 equals 方法、hashCode 方法以及 toString 方法的实现。一般情况下,我们就再也不用担心这三个方法的重载问题了。这不仅减少了代码数量,提高了编码的效率;还减少了编码错误,提高了产品的质量。

2.3 档案类的使用

2.3.1 如何声明档案类

在上文的代码中我们已经看到了record类的声明和使用,这里就不再赘述

2.3.2 java 档案类的限制(不可变数据)

  1. Java 档案类不支持扩展子句,用户不能定制它的父类。隐含的,它的父类是 java.lang.Record。父类不能定制,也就意味着我们不能通过修改父类来影响 Java 档案的行为。
  2. Java 档案类是个终极(final)类,不支持子类,也不能是抽象类。没有子类,也就意味着我们不能通过修改子类来改变 Java 档案的行为。
  3. Java 档案类声明的变量是不可变的变量。这就是我们前面反复强调的,一旦实例化就不能再修改的关键所在。
  4. Java 档案类不能声明可变的变量,也不能支持实例初始化的方法。这就保证了,我们只能使用档案类形式的构造方法,避免额外的初始化对可变性的影响。
  5. Java 档案类不能声明本地(native)方法。如果允许了本地方法,也就意味着打开了修改不可变变量的后门。

2.3.3 透明的载体

2.3.3.1 透明载体的含义

在上面我们也有提到过档案类内置了下面的这些方法缺省(默认)实现:

  1. 构造方法
  2. equals 方法
  3. hashCode 方法
  4. toString 方法
  5. 不可变数据的读取方法

透明载体的意思,通俗地说,就是档案类承载有缺省实现的方法,这些方法可以直接使用,也可以替换。

2.3.3.2 那在什么时候可以去重载这些方法呢?

  1. 重载构造方法:最常见的替换,是要在构造方法里对档案类声明的变量添加必要的检查。
  2. 重载 equals 方法:如果缺省的 equals 方法或者 hashCode 方法不能正常工作或者存在安全的问题,就需要替换掉缺省的方法。
  3. 不推荐的重载:我们有时候也需要重载 toString 方法。但是,我们通常不建议重载不可变数据的读取方法。因为,这样的重载往往意味着需要变更缺省的不可变数值,从而打破实例的状态,进而造成许多无法预料的、让人费解的后果。

3. 总结

  • 知道 Java 支持档案类,并且能够有意识地使用档案类,提高编码效率,降低编码错误;

  • 了解档案类的原理和它要解决的问题,知道使用不可变的对象优势;

  • 了解档案类的缺省方法,掌握缺省方法的好处和不足,知道什么时候要重载这些方法。

4. 引用

  • https://openjdk.java.net/jeps/395 jdk官方文档
  • https://time.geekbang.org/column/article/446610 -极客时间深入剖析java新特性

以上是关于Java新特性-JDK16中的Record类(怎么精简地表达不可变数据?)的主要内容,如果未能解决你的问题,请参考以下文章

Java新特性-JDK16中的Record类(怎么精简地表达不可变数据?)

Java 16 新特性:record类

Java 16 新特性:record类

JDK的主要新特性 - Java 14

一个视频带你看尽JDK14新特性

JDK14的新特性:Lombok的终结者record