Java Records – A Beginner’s Guide

栏目: IT技术 · 发布时间: 4年前

内容简介:In this article, I’m going to present to you the Records feature that was introduced in Java 14, and which aims to simplify the way we create a POJO (Plain Old Java Objects), DTO, or Value Object.Let’s assume we have the following
Last modified:

Imagine having a tool that can automatically detect if you are using JPA and Hibernate properly. Hypersistence Optimizer is that tool!

Introduction

In this article, I’m going to present to you the Records feature that was introduced in Java 14, and which aims to simplify the way we create a POJO (Plain Old Java Objects), DTO, or Value Object.

Domain Model

Let’s assume we have the following PostInfo and AuditInfo POJO classes:

Java Records – A Beginner’s Guide

Both classes define several properties and provide specific implementations for the equals , hashCode , and toString Java Object methods.

The AuditInfo class is implemented like this:

public class AuditInfo {

    private final LocalDateTime createdOn;

    private final String createdBy;

    private final LocalDateTime updatedOn;

    private final String updatedBy;

    public AuditInfo(
            LocalDateTime createdOn,
            String createdBy,
            LocalDateTime updatedOn,
            String updatedBy) {
        this.createdOn = createdOn;
        this.createdBy = createdBy;
        this.updatedOn = updatedOn;
        this.updatedBy = updatedBy;
    }

    public LocalDateTime getCreatedOn() {
        return createdOn;
    }

    public String getCreatedBy() {
        return createdBy;
    }

    public LocalDateTime getUpdatedOn() {
        return updatedOn;
    }

    public String getUpdatedBy() {
        return updatedBy;
    }

    @Override
    public boolean equals(Object o) {
        if (this == o) return true;
        if (!(o instanceof AuditInfo)) return false;

        AuditInfo auditInfo = (AuditInfo) o;
        
        return createdOn.equals(auditInfo.createdOn) &&
               createdBy.equals(auditInfo.createdBy) &&
               Objects.equals(updatedOn, auditInfo.updatedOn) &&
               Objects.equals(updatedBy, auditInfo.updatedBy);
    }

    @Override
    public int hashCode() {
        return Objects.hash(
            createdOn, 
            createdBy, 
            updatedOn, 
            updatedBy
        );
    }

    @Override
    public String toString() {
        return String.format("""
           AuditInfo {
                createdOn : '%s',
                createdBy : '%s',
                updatedOn : '%s',
                updatedBy : '%s'
            }
            """,
            createdOn,
            createdBy,
            updatedOn,
            updatedBy
        );
    }
}

And the PostInfo class looks as follows:

public class PostInfo {

    private final Long id;

    private final String title;

    private final AuditInfo auditInfo;

    public PostInfo(
            Long id,
            String title,
            AuditInfo auditInfo) {
        this.id = id;
        this.title = title;
        this.auditInfo = auditInfo;
    }

    public Long getId() {
        return id;
    }

    public String getTitle() {
        return title;
    }

    public AuditInfo getAuditInfo() {
        return auditInfo;
    }

    @Override
    public boolean equals(Object o) {
        if (this == o) return true;
        if (!(o instanceof PostInfo)) return false;
        PostInfo postInfo = (PostInfo) o;
        return id.equals(postInfo.id) &&
               title.equals(postInfo.title) &&
               auditInfo.equals(postInfo.auditInfo);
    }

    @Override
    public int hashCode() {
        return Objects.hash(
            id, 
            title, 
            auditInfo
        );
    }

    @Override
    public String toString() {
        return String.format("""
            PostInfo {
                id : '%s',
                title : '%s',
                auditInfo : {
                    createdOn : '%s',
                    createdBy : '%s',
                    updatedOn : '%s',
                    updatedBy : '%s'
                }
            }
            """,
            id,
            title,
            auditInfo.createdOn,
            auditInfo.createdBy,
            auditInfo.updatedOn,
            auditInfo.updatedBy
        );
    }
}

Frankly, that’s a lot of code for such a simple data object.

Java Records

Java 14 introduces a new way of defining such data objects, as Records, that take the burden of defining the fields, getters, equals , hashCode , and toString method implementations.

So, let’s see how the AuditInfo and PostInfo classes look when we define them as Records, instead of Plain Old Java Objects:

public record AuditInfo(
    LocalDateTime createdOn,
    String createdBy,
    LocalDateTime updatedOn,
    String updatedBy
) {}

public record PostInfo(
    Long id,
    String title,
    AuditInfo auditInfo
) {}

That’s it!

Behind the scenes, Java Records are defined as any other Java class. In our case, the decompiled classes look as follows:

public final class PostInfo 
        extends java.lang.Record {
    private final java.lang.Long id;
    private final java.lang.String title;
    private final AuditInfo auditInfo;

    public PostInfo(
            java.lang.Long id, 
            java.lang.String title, 
            AuditInfo auditInfo) { 
        /* compiled code */ 
    }

    public java.lang.String toString() { /* compiled code */ }

    public final int hashCode() { /* compiled code */ }

    public final boolean equals(java.lang.Object o) { /* compiled code */ }

    public java.lang.Long id() { /* compiled code */ }

    public java.lang.String title() { /* compiled code */ }

    public AuditInfo auditInfo() { /* compiled code */ }
}

public final class AuditInfo 
        extends java.lang.Record {
    private final java.time.LocalDateTime createdOn;
    private final java.lang.String createdBy;
    private final java.time.LocalDateTime updatedOn;
    private final java.lang.String updatedBy;

    public AuditInfo(
            java.time.LocalDateTime createdOn, 
            java.lang.String createdBy, 
            java.time.LocalDateTime updatedOn, 
            java.lang.String updatedBy) { 
        /* compiled code */ 
    }

    public java.lang.String toString() { /* compiled code */ }

    public final int hashCode() { /* compiled code */ }

    public final boolean equals(java.lang.Object o) { /* compiled code */ }

    public java.time.LocalDateTime createdOn() { /* compiled code */ }

    public java.lang.String createdBy() { /* compiled code */ }

    public java.time.LocalDateTime updatedOn() { /* compiled code */ }

    public java.lang.String updatedBy() { /* compiled code */ }
}

The generated class is final and extends the Record base class that was introduced by Java 14.

Since Java Records define a single constructor that takes the same arguments we used when defining the Record type, this is how we can create a PostInfo with an AuditInfo object:

PostInfo postInfo = new PostInfo(
    1L,
    "High-Performance Java Persistence",
    new AuditInfo(
        LocalDateTime.of(2016, 11, 2, 12, 0, 0),
        "Vlad Mihalcea",
        LocalDateTime.now(),
        "Vlad Mihalcea"
    )
);

Note that, unlike the POJO specification, Java Records getters don’t follow the Java Bean standard, and the method names match the encapsulated field names:

assertEquals(
    1L, postInfo.id().longValue()
);

assertEquals(
    "High-Performance Java Persistence", postInfo.title()
);

assertEquals(
    LocalDateTime.of(2016, 11, 2, 12, 0, 0), postInfo.auditInfo().createdOn()
);

assertEquals(
    "Vlad Mihalcea", postInfo.auditInfo().createdBy()
);

We can see that a toString method is also generated, and the implementation is based on the Record properties. So, when calling the toString methods of the AuditInfo and PostInfo Records:

LOGGER.info("Audit info:\n{}", postInfo.auditInfo());
LOGGER.info("Post info:\n{}", postInfo);

We get the following log entries:

Audit info:
AuditInfo[createdOn=2016-11-02T12:00, createdBy=Vlad Mihalcea, updatedOn=2020-04-14T12:29:29.534875700, updatedBy=Vlad Mihalcea]

Post info:
PostInfo[id=1, title=High-Performance Java Persistence, auditInfo=AuditInfo[createdOn=2016-11-02T12:00, createdBy=Vlad Mihalcea, updatedOn=2020-04-14T12:29:29.534875700, updatedBy=Vlad Mihalcea]]

Customizing Java Records

Even if the generated classes are final , we can still override the default methods. For instance, let’s say we want to provide a custom toString implementation that matches the one we defined previously in our POJO classes.

To override the toString method, we just have to provide the new method definition when declaring Java Records:

public record AuditInfo(
    LocalDateTime createdOn,
    String createdBy,
    LocalDateTime updatedOn,
    String updatedBy
) {
    @Override
    public String toString() {
        return String.format("""
            AuditInfo {
                createdOn : '%s',
                createdBy : '%s',
                updatedOn : '%s',
                updatedBy : '%s'
            }
            """,
            createdOn,
            createdBy,
            updatedOn,
            updatedBy
        );
    }
}

public record PostInfo(
    Long id,
    String title,
    AuditInfo auditInfo
) {
    @Override
    public String toString() {
        return String.format("""
            PostInfo {
                id : '%s',
                title : '%s',
                auditInfo : {
                    createdOn : '%s',
                    createdBy : '%s',
                    updatedOn : '%s',
                    updatedBy : '%s'
                }
            }
            """,
            id,
            title,
            auditInfo.createdOn,
            auditInfo.createdBy,
            auditInfo.updatedOn,
            auditInfo.updatedBy
        );
    }
}

Now, when the Logger framework calls the toString method, this is what we get in the application log:

Audit info:
AuditInfo {
    createdOn : '2016-11-02T12:00',
    createdBy : 'Vlad Mihalcea',
    updatedOn : '2020-04-14T12:45:09.569632400',
    updatedBy : 'Vlad Mihalcea'
}

Post info:
PostInfo {
    id : '1',
    title : 'High-Performance Java Persistence',
    auditInfo : {
        createdOn : '2016-11-02T12:00',
        createdBy : 'Vlad Mihalcea',
        updatedOn : '2020-04-14T12:45:09.569632400',
        updatedBy : 'Vlad Mihalcea'
    }
}

Cool, right?

I'm running an online workshop on the 14th of May about The Best Way to Fetch Data with Java Persistence and Hibernate.

Java Records – A Beginner’s Guide

If you enjoyed this article, I bet you are going to love my Book and Video Courses as well.

Java Records – A Beginner’s Guide Java Records – A Beginner’s Guide

Conclusion

The new Java Records feature is very handy, as it simplifies the way we build value objects. Just like Multiline String Text Blocks , this is a preview language feature in Java 14.

So, if you want to try it out, not that you need to use the enable-preview to both the Java compiler and the JVM when running the program.


以上就是本文的全部内容,希望本文的内容对大家的学习或者工作能带来一定的帮助,也希望大家多多支持 码农网

查看所有标签

猜你喜欢:

本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们

小程序大时代

小程序大时代

即速应用 / 哈尔滨工业大学出版社 / 2018-10 / 58元

2017年1月9日微信小程序的问世,是中国互联网发展史上的又一个里程碑。支付宝、百度、今日头条等各大巨头的陆续进场,无不证明小程序这种轻应用形态已经成为移动互联网的发展趋势。我们希望可以通过这本书,帮助零基础的小程序爱好者,了解小程序的市场走向和生态发展,学会小程序的制作与运营,读懂小程序这个全新物种。 全书分为入门篇、制作篇、运营篇三大篇章。 入门篇首先揭开小程序的神秘面纱,盘点小程......一起来看看 《小程序大时代》 这本书的介绍吧!

Base64 编码/解码
Base64 编码/解码

Base64 编码/解码

XML、JSON 在线转换
XML、JSON 在线转换

在线XML、JSON转换工具

XML 在线格式化
XML 在线格式化

在线 XML 格式化压缩工具