Warm-up

What the compiler wrote for you

5 minjunior16 yrs

One concept, guided. Near-impossible to fail.

What this teaches

  • A record is final and extends java.lang.Record — both slots are spent
  • Accessors are x(), not getX() — a record is not a JavaBean
  • equals on a double component is not ==, because equals must be reflexive

Starter

Starter.javaOpen in playground
import java.lang.reflect.*;
import java.util.*;

/**
 * WARM-UP — 5 minutes. One concept. Hard to fail.
 *
 * A record generates equals, hashCode, toString and an accessor per
 * component. Two of the lines below are the opposite of what == does.
 *
 * TASKS
 *   1. Predict all six printed lines BEFORE running. The last two are the
 *      ones worth writing down.
 *   2. Run it.
 *   3. Add a third component to Point and re-run. Note that you changed
 *      nothing else and equals, hashCode and toString all followed.
 *   4. In a comment: why MUST a record holding NaN equal itself? The answer
 *      is a rule about equals, not a fact about floating point.
 */
public class Starter {

    record Point(double x, double y) {}

    public static void main(String[] args) {
        Point a = new Point(1.5, 2.5);
        Point b = new Point(1.5, 2.5);

        System.out.println("toString                   : " + a);
        System.out.println("accessor is x(), not getX(): " + a.x());
        System.out.println("equal by value             : " + a.equals(b));
        System.out.println("same hashCode              : " + (a.hashCode() == b.hashCode()));

        System.out.println();
        System.out.println("Double.NaN == Double.NaN   : " + (Double.NaN == Double.NaN));
        System.out.println("record NaN .equals         : "
                + new Point(Double.NaN, 0).equals(new Point(Double.NaN, 0)));
        System.out.println("0.0 == -0.0                : " + (0.0 == -0.0));
        System.out.println("record 0.0 vs -0.0 .equals : "
                + new Point(0.0, 0).equals(new Point(-0.0, 0)));

        System.out.println();
        System.out.println("is final                   : "
                + Modifier.isFinal(Point.class.getModifiers()));
        System.out.println("extends                    : "
                + Point.class.getSuperclass().getName());

        // Question to answer in a comment before you move on:
        // A record's superclass is always java.lang.Record. What does that
        // cost you, and when would it change your design?
    }
}

Run it locally:

cd exercises/java/oop/record-vs-class/01-warmup
javac Starter.java -d /tmp/out && java -cp /tmp/out Starter

Done when

  • You predicted the NaN and -0.0 lines before running
  • You added a component and observed equals/hashCode/toString follow automatically
  • A comment says why NaN equalling itself is required rather than a quirk

← Back to When should you use a record instead of a class?