What are the new features of Java 14?

Yun Qi Hao: https://yqh.aliyun.com
The first-hand cloud information, the selected cloud enterprise case base of different industries, and the best practices extracted from many successful cases help you to make cloud decision!

Records provide java with the ability to implement data classes correctly, and it is no longer necessary to write lengthy code to implement data classes. Let's take a look at the new features of records in Java 14.

By Nathan Esquenazi
Translator and editor: Guo Rui
Product | CSDN (ID: CSDNnews)

Here is the Translation:

Java 14 will be officially released in March 2020. Java takes six months as the release cycle of new versions. Like previous versions, JDK 14 is expected to bring some new features at the language itself and JVM level.

If we look at the feature list, we will notice some language features that developers are looking forward to: records, switch expressions (which already exist in JDK 13, but only preview patterns), pattern matching. Let's take a look at the more interesting recording feature.

Prerequisite

We need a preview of JDK 14 on the OpenJDK website( https://jdk.java.net/14/).

What is a record?

Record represents "data class", which is a special class used to save pure data. Other languages already have records like structures, such as Kotlin's data classes. By declaring a type as a record, you can express your intention by type, that is, only data. The syntax for declaring records is much simpler than using ordinary classes, which usually need to implement core Object methods, such as equals () and hashCode () (often called "boilerplate" code). Records are a good choice when modeling things such as model classes (which may be persisted through ORM) or data transfer objects (DTOs).

If you want to know how records are implemented in the Java language, you can refer to enumeration types. Enumeration is also a class with special semantics and elegant syntax. Since records and enumerations are still classes, many of the features available in classes are preserved, so records balance simplicity and flexibility in design.

Record is a preview language feature, which means that although this feature has been fully supported, it has not yet officially entered the standard JDK, and can only be used by activation flag at present. Preview language features may be updated or removed in future releases. The switch expression is similar to this, and it may last forever in future versions.

An example of a record

Here is an example of a record:

package examples;

record Person (String firstName, String lastName) {}

We define a Person object, which contains two components, firstName and lastName. The body of the record is empty.
Then we compile it. Note the -- enable preview option.

javac --enable-preview --release 14 Person.java

Note: Person.java uses preview language features.
Note: Recompile with -Xlint:preview for details.

Expose its mystery

As mentioned earlier, records are just a class for storing and exposing data.

Next let's look at the bytecode generated by the javap tool:

javap -v -p Person.class

Bytecode:

Classfile examples/Person.class
  Last modified Dec 22, 2019; size 1273 bytes
  SHA-256 checksum 6f1b325121ca32a0b6127180eff29dcac4834f9c138c9613c526a4202fef972f
  Compiled from "Person.java"
final class examples.Person extends java.lang.Record
  minor version: 65535
  major version: 58
  flags: (0x0030) ACC_FINAL, ACC_SUPER
  this_class: #8                          // examples/Person
  super_class: #2                         // java/lang/Record
  interfaces: 0, fields: 2, methods: 6, attributes: 4
Constant pool:
   #1 = Methodref          #2.#3          // java/lang/Record."":()V
   #2 = Class              #4             // java/lang/Record
   #3 = NameAndType        #5:#6          // "":()V
   #4 = Utf8               java/lang/Record
   #5 = Utf8               
   #6 = Utf8               ()V
   #7 = Fieldref           #8.#9          // examples/Person.firstName:Ljava/lang/String;
   #8 = Class              #10            // examples/Person
   #9 = NameAndType        #11:#12        // firstName:Ljava/lang/String;
  #10 = Utf8               examples/Person
  #11 = Utf8               firstName
  #12 = Utf8               Ljava/lang/String;
  #13 = Fieldref           #8.#14         // examples/Person.lastName:Ljava/lang/String;
  #14 = NameAndType        #15:#12        // lastName:Ljava/lang/String;
  #15 = Utf8               lastName
  #16 = Fieldref           #8.#9          // examples/Person.firstName:Ljava/lang/String;
  #17 = Fieldref           #8.#14         // examples/Person.lastName:Ljava/lang/String;
  #18 = InvokeDynamic      #0:#19         // #0:toString:(Lexamples/Person;)Ljava/lang/String;
  #19 = NameAndType        #20:#21        // toString:(Lexamples/Person;)Ljava/lang/String;
  #20 = Utf8               toString
  #21 = Utf8               (Lexamples/Person;)Ljava/lang/String;
  #22 = InvokeDynamic      #0:#23         // #0:hashCode:(Lexamples/Person;)I
  #23 = NameAndType        #24:#25        // hashCode:(Lexamples/Person;)I
  #24 = Utf8               hashCode
  #25 = Utf8               (Lexamples/Person;)I
  #26 = InvokeDynamic      #0:#27         // #0:equals:(Lexamples/Person;Ljava/lang/Object;)Z
  #27 = NameAndType        #28:#29        // equals:(Lexamples/Person;Ljava/lang/Object;)Z
  #28 = Utf8               equals
  #29 = Utf8               (Lexamples/Person;Ljava/lang/Object;)Z
  #30 = Utf8               (Ljava/lang/String;Ljava/lang/String;)V
  #31 = Utf8               Code
  #32 = Utf8               LineNumberTable
  #33 = Utf8               MethodParameters
  #34 = Utf8               ()Ljava/lang/String;
  #35 = Utf8               ()I
  #36 = Utf8               (Ljava/lang/Object;)Z
  #37 = Utf8               SourceFile
  #38 = Utf8               Person.java
  #39 = Utf8               Record
  #40 = Utf8               BootstrapMethods
  #41 = MethodHandle       6:#42          // REF_invokeStatic java/lang/runtime/ObjectMethods.bootstrap:(Ljava/lang/invoke/MethodHandles$Lookup;Ljava/lang/String;Ljava/lang/invoke/TypeDescriptor;Ljava/lang/Class;Ljava/lang/String;[Ljava/lang/invoke/MethodHandle;)Ljava/lang/Object;
  #42 = Methodref          #43.#44        // java/lang/runtime/ObjectMethods.bootstrap:(Ljava/lang/invoke/MethodHandles$Lookup;Ljava/lang/String;Ljava/lang/invoke/TypeDescriptor;Ljava/lang/Class;Ljava/lang/String;[Ljava/lang/invoke/MethodHandle;)Ljava/lang/Object;
  #43 = Class              #45            // java/lang/runtime/ObjectMethods
  #44 = NameAndType        #46:#47        // bootstrap:(Ljava/lang/invoke/MethodHandles$Lookup;Ljava/lang/String;Ljava/lang/invoke/TypeDescriptor;Ljava/lang/Class;Ljava/lang/String;[Ljava/lang/invoke/MethodHandle;)Ljava/lang/Object;
  #45 = Utf8               java/lang/runtime/ObjectMethods
  #46 = Utf8               bootstrap
  #47 = Utf8               (Ljava/lang/invoke/MethodHandles$Lookup;Ljava/lang/String;Ljava/lang/invoke/TypeDescriptor;Ljava/lang/Class;Ljava/lang/String;[Ljava/lang/invoke/MethodHandle;)Ljava/lang/Object;
  #48 = String             #49            // firstName;lastName
  #49 = Utf8               firstName;lastName
  #50 = MethodHandle       1:#7           // REF_getField examples/Person.firstName:Ljava/lang/String;
  #51 = MethodHandle       1:#13          // REF_getField examples/Person.lastName:Ljava/lang/String;
  #52 = Utf8               InnerClasses
  #53 = Class              #54            // java/lang/invoke/MethodHandles$Lookup
  #54 = Utf8               java/lang/invoke/MethodHandles$Lookup
  #55 = Class              #56            // java/lang/invoke/MethodHandles
  #57 = Utf8               Lookup
{
  private final java.lang.String firstName;
    descriptor: Ljava/lang/String;
    flags: (0x0012) ACC_PRIVATE, ACC_FINAL

  private final java.lang.String lastName;
    descriptor: Ljava/lang/String;
    flags: (0x0012) ACC_PRIVATE, ACC_FINAL

  public examples.Person(java.lang.String, java.lang.String);
    descriptor: (Ljava/lang/String;Ljava/lang/String;)V
    flags: (0x0001) ACC_PUBLIC
    Code:
      stack=2, locals=3, args_size=3
         0: aload_0
         1: invokespecial #1                  // Method java/lang/Record."":()V
         4: aload_0
         5: aload_1
         6: putfield      #7                  // Field firstName:Ljava/lang/String;
         9: aload_0
        10: aload_2
        11: putfield      #13                 // Field lastName:Ljava/lang/String;
        14: return
      LineNumberTable:
        line 3: 0
    MethodParameters:
      Name                           Flags
      firstName
      lastName

  public java.lang.String toString();
    descriptor: ()Ljava/lang/String;
    flags: (0x0001) ACC_PUBLIC
    Code:
      stack=1, locals=1, args_size=1
         0: aload_0
         1: invokedynamic #18,  0             // InvokeDynamic #0:toString:(Lexamples/Person;)Ljava/lang/String;
         6: areturn
      LineNumberTable:
        line 3: 0

  public final int hashCode();
    descriptor: ()I
    flags: (0x0011) ACC_PUBLIC, ACC_FINAL
    Code:
      stack=1, locals=1, args_size=1
         0: aload_0
         1: invokedynamic #22,  0             // InvokeDynamic #0:hashCode:(Lexamples/Person;)I
         6: ireturn
      LineNumberTable:
        line 3: 0

  public final boolean equals(java.lang.Object);
    descriptor: (Ljava/lang/Object;)Z
    flags: (0x0011) ACC_PUBLIC, ACC_FINAL
    Code:
      stack=2, locals=2, args_size=2
         0: aload_0
         1: aload_1
         2: invokedynamic #26,  0             // InvokeDynamic #0:equals:(Lexamples/Person;Ljava/lang/Object;)Z
         7: ireturn
      LineNumberTable:
        line 3: 0
  public java.lang.String firstName();
    descriptor: ()Ljava/lang/String;
    flags: (0x0001) ACC_PUBLIC
    Code:
      stack=1, locals=1, args_size=1
         0: aload_0
         1: getfield      #16                 // Field firstName:Ljava/lang/String;
         4: areturn
      LineNumberTable:
        line 3: 0

  public java.lang.String lastName();
    descriptor: ()Ljava/lang/String;
    flags: (0x0001) ACC_PUBLIC
    Code:
      stack=1, locals=1, args_size=1
         0: aload_0
         1: getfield      #17                 // Field lastName:Ljava/lang/String;
         4: areturn
      LineNumberTable:
        line 3: 0
}
SourceFile: "Person.java"
Record:
  java.lang.String firstName;
    descriptor: Ljava/lang/String;

  java.lang.String lastName;
    descriptor: Ljava/lang/String;

BootstrapMethods:
  0: #41 REF_invokeStatic java/lang/runtime/ObjectMethods.bootstrap:(Ljava/lang/invoke/MethodHandles$Lookup;Ljava/lang/String;Ljava/lang/invoke/TypeDescriptor;Ljava/lang/Class;Ljava/lang/String;[Ljava/lang/invoke/MethodHandle;)Ljava/lang/Object;
    Method arguments:
      #8 examples/Person
      #48 firstName;lastName
      #50 REF_getField examples/Person.firstName:Ljava/lang/String;
      #51 REF_getField examples/Person.lastName:Ljava/lang/String;
InnerClasses:
  public static final #57= #53 of #55;    // Lookup=class java/lang/invoke/MethodHandles$Lookup of class java/lang/invoke/MethodHandles

We should pay special attention to the following points:

  • This class is marked final, which means that subclasses cannot be created.
  • Just as all enumerations are based on java.lang.Enum, all records are based on java.lang.Record.
  • Two components: firstName and lastName are private and final.
  • There is a public constructor that provides construction objects: public examples.Person(java.lang.String, java.lang.String). By looking at its bytecode, we can see that this constructor simply assigns two parameters to these two components. The constructor is equivalent to:
public Person(String firstName, String lastName) {
   this.firstName = firstName;
   this.lastName = lastName;
}
  • There are two ways to get the value of an object, firstName() and lastName()
  • Three functions, tostring(), hashcode(), and equals(), are generated automatically. They all rely on invokedynamic to dynamically call methods including implicit implementation functions. As you can see from the bytecode, there is a startup function ObjectMethods.bootstrap to generate the corresponding function according to the name of the record component and its Getter function. Their performance is consistent with our assumptions:
Person john = new Person("John", "Doe");
System.out.println(john.firstName());         // John
System.out.println(john.lastName());          // Doe
System.out.println(john);                     // Person[firstName=John, lastName=Doe]

Person jane = new Person("Jane", "Dae");
Person johnCopy = new Person("John", "Doe");
System.out.println(john.hashCode());          // 71819599
System.out.println(jane.hashCode());          // 71407578
System.out.println(johnCopy.hashCode());      // 71819599
System.out.println(john.equals(jane));        // false
System.out.println(john.equals(johnCopy));    // true

Add members to the declaration of the record

We can't add instance fields to the record, which is expected, because this data should be set as a component. But we can add static fields:

record Person(String firstName, String lastName){
  static int x;
}

We can define static and instance functions to manipulate the state of an object.

record Person (String firstName, String lastName) {
    static int x;
    public static void dox(){
        x++;
    }
    public String getFullName(){
        return firstName + " " + lastName ;
    }
}

We can also add constructors for records, or we can edit the specification constructors (constructors with two string parameters). If you want to override the canonical constructor, you can write a constructor without parameters, without assigning values to properties.

record Person (String firstName, String lastName) {
  public Person {
    if(firstName==null||lastName==null){
    throw new IllegalArgumentException("firstName and lastName must not be null");
    // You can ignore attribute assignment, and the compiler will automatically add assignment code for you
  }
  public Person(String fullName){
      this(fullName.split("")[0], fullName.split("")[1]);
   }
}

conclusion

Records provide Java with the ability to implement data classes correctly, and it is no longer necessary to write lengthy code to implement data classes. This allows writing pure data class code to be reduced from a few lines to a single line. There are other preview language features that can be used with records, such as pattern matching. If you want to learn more about records and related background, please refer to Brian Goetz's OpenJDK documentation( https://cr.openjdk.java.net/~briangoetz/amber/datum.html).

Original text: https://dzone.com/articles/a-first-look-at-records-in-java-14

By Mahmoud Anouti, senior software engineer. Translator: mingmingruyue, senior Java development engineer of well-known Internet company, CSDN blog expert.

This is the translation of CSDN. Please indicate the source of reprint.

Original release time: January 14, 2020
By Nathan Esquenazi
This article is from Alibaba cloud Qihao partner“ CSDN ”, you can pay attention to“ CSDN"

Tags: Java JDK jvm Attribute

Posted on Thu, 16 Jan 2020 03:53:55 -0800 by squariegoes