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 |