| benf.org : other : cfr : records |
As of Java 14, the 'record' type has entered preview state.
There's a lot written about it, the JEP - https://openjdk.java.net/jeps/359), or this writeup, so I won't go in to what a record is, but it's interesting to see what it's actually composed of.
Spoiler alert - it feels a lot like the way enum was implemented - compiler generated boilerplate ;) )
record RecordTest1 (String firstName, String lastName) {}
Decompiled with CFR (default behaviour), we get back .... (drum roll)
/*
* Decompiled with CFR 0.149.
*/
package org.benf.cfr.tests;
record RecordTest1(String firstName, String lastName) {
}
Ok, that's a bit boring, what if we turn OFF record resugaring? (using --recordtypes false;)
> java -jar cfr-0.149.jar RecordTest1.class --recordtypes false
/*
* Decompiled with CFR 0.149.
*/
package org.benf.cfr.tests;
import java.lang.invoke.MethodHandle;
import java.lang.runtime.ObjectMethods;
final class RecordTest1
extends Record {
private final String firstName;
private final String lastName;
public RecordTest1(String firstName, String lastName) {
this.firstName = firstName;
this.lastName = lastName;
}
@Override
public String toString() {
return ObjectMethods.bootstrap("toString", new MethodHandle[]{RecordTest1.class, "firstName;lastName", "firstName", "lastName"}, this);
}
@Override
public final int hashCode() {
return (int)ObjectMethods.bootstrap("hashCode", new MethodHandle[]{RecordTest1.class, "firstName;lastName", "firstName", "lastName"}, this);
}
@Override
public final boolean equals(Object o) {
return (boolean)ObjectMethods.bootstrap("equals", new MethodHandle[]{RecordTest1.class, "firstName;lastName", "firstName", "lastName"}, this, o);
}
public String firstName() {
return this.firstName;
}
public String lastName() {
return this.lastName;
}
}
It's implemented as 'just' a class, that extends java.lang.Record.
We can override behaviour of any of these methods, add extra code into the constructor, and even chain constructors
record RecordTest9 (String firstName, String lastName, RecordTest2 child) {
static int x = 1;
public RecordTest9 {
if (firstName == lastName) {
lastName = "McPointer";
}
}
public RecordTest9(String one) {
this(one.split(" ")[0], one.split(" ")[1], null);
}
public static void doX() {
x++;
}
@Override
public final int hashCode() {
return 3;
}
@Override
public String toString() {
return lastName + "." + firstName;
}
}
Just to check, CFR does get this back nicely .....
/*
* Decompiled with CFR 0.149.
*/
package org.benf.cfr.tests;
import org.benf.cfr.tests.RecordTest2;
record RecordTest9(String firstName, String lastName, RecordTest2 child) {
static int x = 1;
public RecordTest9 {
if (firstName == lastName) {
lastName = "McPointer";
}
}
public RecordTest9(String one) {
this(one.split(" ")[0], one.split(" ")[1], null);
}
public static void doX() {
++x;
}
@Override
public final int hashCode() {
return 3;
}
@Override
public String toString() {
return this.lastName + "." + this.firstName;
}
}
And again, here's what we get if we turn off record re-sugaring. Not unexpected that the member assignments (those which we don't override) are done at the bottom of the canonical constructor.
>java -jar cfr-0.149.jar RecordTest9.class --recordtypes false
/*
* Decompiled with CFR 0.149.
*/
package org.benf.cfr.tests;
import java.lang.invoke.MethodHandle;
import java.lang.runtime.ObjectMethods;
import org.benf.cfr.tests.RecordTest2;
final class RecordTest9
extends Record {
private final String firstName;
private final String lastName;
private final RecordTest2 child;
static int x = 1;
public RecordTest9(String firstName, String lastName, RecordTest2 child) {
if (firstName == lastName) {
lastName = "McPointer";
}
this.firstName = firstName;
this.lastName = lastName;
this.child = child;
}
public RecordTest9(String one) {
this(one.split(" ")[0], one.split(" ")[1], null);
}
public static void doX() {
++x;
}
@Override
public final int hashCode() {
return 3;
}
@Override
public String toString() {
return this.lastName + "." + this.firstName;
}
@Override
public final boolean equals(Object o) {
return (boolean)ObjectMethods.bootstrap("equals", new MethodHandle[]{RecordTest9.class, "firstName;lastName;child", "firstName", "lastName", "child"}, this, o);
}
public String firstName() {
return this.firstName;
}
public String lastName() {
return this.lastName;
}
public RecordTest2 child() {
return this.child;
}
}
| Last updated 06/2021 |