{"id":"records-declaring","slug":"declaring-a-record","title":"Declaring a record","text":"This module is an extra: the MOOC doesn't cover it. Many classes you've written only hold a few values that never change after the object is made: a place on a map, an amount of money, an entry in a timetable. For each, you wrote instance variables, a constructor, getters, a `toString`, and for HashMap keys an `equals` and a `hashCode` too. A **record**, part of Java since Java 16, declares such a class in one line, and Java writes all of those parts for you. Records are common in newer Java code, so they're worth being able to read and write.\n\n### A timetable entry as a class\n\nA station's departure board shows, for each train, its name, where it's going and its platform. As a class in the style of the course, that's:\n\n```java\npublic class Departure {\n    private final String train;\n    private final String destination;\n    private final int platform;\n\n    public Departure(String train, String destination, int platform) {\n        this.train = train;\n        this.destination = destination;\n        this.platform = platform;\n    }\n\n    public String getTrain() {\n        return this.train;\n    }\n\n    public String getDestination() {\n        return this.destination;\n    }\n\n    public int getPlatform() {\n        return this.platform;\n    }\n}\n```\n\nTwenty-odd lines, and there's still no `toString`, `equals` or `hashCode`.\n\n### The same as a record\n\n```java\npublic class Main {\n    public static void main(String[] args) {\n        Departure express = new Departure(\"IC 27\", \"Tampere\", 8);\n        System.out.println(express.train() + \" to \" + express.destination() + \", platform \" + express.platform());\n        System.out.println(express);\n    }\n}\n// ==== Departure.java ====\npublic record Departure(String train, String destination, int platform) {\n}\n```\n\n```output\nIC 27 to Tampere, platform 8\nDeparture[train=IC 27, destination=Tampere, platform=8]\n```\n\nThe word `record` takes the place of `class`, and after the name, in parentheses, come the record's **components**: the values each Departure holds, written like a constructor's parameters. From that one line, Java makes a class with:\n\n- a `private final` instance variable for each component;\n- a constructor that takes the components in the same order, `new Departure(\"IC 27\", \"Tampere\", 8)`, and stores them;\n- an **accessor** method for each component, named exactly like the component: `train()`, `destination()`, `platform()`. There's no `get` in front;\n- a `toString` that shows the record's name and each component with its value, in brackets: `Departure[train=IC 27, destination=Tampere, platform=8]`;\n- an `equals` and a `hashCode` that use the components (the next step).\n\nThe braces after the header hold the record's body. It's empty here; step 4 puts methods in it. A public record goes in a file of its name, `Departure.java`, just like a public class.\n\n### No getters, no setters\n\nThe accessors are named after the components, so a getter's name doesn't compile:\n\n```java\npublic class Main {\n    public static void main(String[] args) {\n        Departure express = new Departure(\"IC 27\", \"Tampere\", 8);\n        System.out.println(express.getPlatform());\n    }\n}\n// ==== Departure.java ====\npublic record Departure(String train, String destination, int platform) {\n}\n```\n\n```javac\nMain.java:4: error: cannot find symbol\n        System.out.println(express.getPlatform());\n                                  ^\n  symbol:   method getPlatform()\n  location: variable express of type Departure\n1 error\n```\n\nThere are no setters either, and the instance variables are `final`: once a record is made, its values stay as they are, like a String's characters. When a train moves to another platform, the program makes a new Departure (step 4 shows a neat way to do that).\n","task":"A city map app marks places with pins, each at a column `x` and a row `y` of the map. Fill in the blanks: the word that makes `Pin` a record, its second component, the record's name after `new`, and the accessor that reads the bakery's column. The program prints:\n\n```\nBakery at 3, 5\nLibrary at 8, 1\n```\n","kind":"fill","seed":"public class Main {\n    public static void main(String[] args) {\n        Pin bakery = new [[Pin]](3, 5);\n        Pin library = new Pin(8, 1);\n        System.out.println(\"Bakery at \" + bakery.[[x]]() + \", \" + bakery.y());\n        System.out.println(\"Library at \" + library.x() + \", \" + library.y());\n    }\n}\n// ==== Pin.java ====\npublic [[record]] Pin(int x, int [[y]]) {\n}\n","solution":"public class Main {\n    public static void main(String[] args) {\n        Pin bakery = new Pin(3, 5);\n        Pin library = new Pin(8, 1);\n        System.out.println(\"Bakery at \" + bakery.x() + \", \" + bakery.y());\n        System.out.println(\"Library at \" + library.x() + \", \" + library.y());\n    }\n}\n// ==== Pin.java ====\npublic record Pin(int x, int y) {\n}\n","hints":["A record is declared like a class, with `record` in place of `class`, and its components in parentheses after the name: `public record Pin(int x, int y)`.","The accessor of the component `x` is `x()`, with no `get` in front. A record is created with `new` and its name, like any object."],"tests":[{"name":"Output","stdin":"","expect":"Bakery at 3, 5\nLibrary at 8, 1","hidden":false}],"require":[],"forbid":[],"more":[{"task":"A weather service keeps temperature readings, each with the station's name and the temperature in degrees Celsius. `Reading` is a record, in `Reading.java`. In `Main.java`, write the method `report(Reading reading)`, which prints one line: the station, a colon, the temperature, the word `degrees` and a comma, and then `below freezing` when it's under 0, `at freezing` when it's exactly 0, and `above freezing` otherwise. Read the values with the record's accessors. The check runs:\n\n```java\nreport(new Reading(\"Oulu\", -4.5));\nreport(new Reading(\"Turku\", 0.0));\nreport(new Reading(\"Helsinki\", 3.2));\n```\n\nand expects:\n\n```\nOulu: -4.5 degrees, below freezing\nTurku: 0.0 degrees, at freezing\nHelsinki: 3.2 degrees, above freezing\n```\n","kind":"code","seed":"// ==== Main.java ====\npublic class Main {\n    public static void main(String[] args) {\n        report(new Reading(\"Oulu\", -4.5));\n        report(new Reading(\"Turku\", 0.0));\n        report(new Reading(\"Helsinki\", 3.2));\n    }\n\n    public static void report(Reading reading) {\n\n    }\n}\n\n// ==== Reading.java ====\npublic record Reading(String station, double celsius) {\n}\n","solution":"// ==== Main.java ====\npublic class Main {\n    public static void main(String[] args) {\n        report(new Reading(\"Oulu\", -4.5));\n        report(new Reading(\"Turku\", 0.0));\n        report(new Reading(\"Helsinki\", 3.2));\n    }\n\n    public static void report(Reading reading) {\n        String feel = \"above freezing\";\n        if (reading.celsius() < 0) {\n            feel = \"below freezing\";\n        } else if (reading.celsius() == 0) {\n            feel = \"at freezing\";\n        }\n        System.out.println(reading.station() + \": \" + reading.celsius() + \" degrees, \" + feel);\n    }\n}\n\n// ==== Reading.java ====\npublic record Reading(String station, double celsius) {\n}\n","hints":["The accessors are `reading.station()` and `reading.celsius()`. They're methods, so they need the parentheses.","Choose the words first with an if, else if and else on `reading.celsius()`, then print the line: `reading.station() + \": \" + reading.celsius() + \" degrees, \" + feel`."],"tests":[{"name":"Test 1","stdin":"","call":"report(new Reading(\"Oulu\", -4.5));\nreport(new Reading(\"Turku\", 0.0));\nreport(new Reading(\"Helsinki\", 3.2));\n","expect":"Oulu: -4.5 degrees, below freezing\nTurku: 0.0 degrees, at freezing\nHelsinki: 3.2 degrees, above freezing","hidden":false},{"name":"Test 2","stdin":"","call":"report(new Reading(\"Inari\", -31.0));\nreport(new Reading(\"Vaasa\", 0.0));\nreport(new Reading(\"Pori\", 17.25));\nreport(new Reading(\"Kemi\", -0.5));\n","expect":"Inari: -31.0 degrees, below freezing\nVaasa: 0.0 degrees, at freezing\nPori: 17.25 degrees, above freezing\nKemi: -0.5 degrees, below freezing","hidden":true}],"require":[{"pattern":"\\.celsius\\s*\\(\\s*\\)","message":"Read the temperature with the accessor `celsius()`."},{"pattern":"\\.station\\s*\\(\\s*\\)","message":"Read the station's name with the accessor `station()`."}],"forbid":[]},{"task":"A record can have another record as a component. A map app's `Cafe` holds its name, its `Pin` and its rating. What does this program print?\n","kind":"predict","seed":"public class Main {\n    public static void main(String[] args) {\n        Cafe corner = new Cafe(\"Blue Door\", new Pin(2, 7), 4.5);\n        System.out.println(corner);\n        System.out.println(corner.pin());\n        System.out.println(corner.name().length() + corner.pin().y());\n    }\n}\n// ==== Pin.java ====\npublic record Pin(int x, int y) {\n}\n// ==== Cafe.java ====\npublic record Cafe(String name, Pin pin, double rating) {\n}\n","solution":"public class Main {\n    public static void main(String[] args) {\n        Cafe corner = new Cafe(\"Blue Door\", new Pin(2, 7), 4.5);\n        System.out.println(corner);\n        System.out.println(corner.pin());\n        System.out.println(corner.name().length() + corner.pin().y());\n    }\n}\n// ==== Pin.java ====\npublic record Pin(int x, int y) {\n}\n// ==== Cafe.java ====\npublic record Cafe(String name, Pin pin, double rating) {\n}\n","hints":["A record's `toString` writes the record's name, then each component as `name=value` in square brackets. A component that is itself a record is written with its own `toString`.","A String component is written without quotes. `\"Blue Door\".length()` is 9 (the space counts), and the pin's `y()` is 7, two ints that add up."],"tests":[{"name":"Output","stdin":"","expect":"Cafe[name=Blue Door, pin=Pin[x=2, y=7], rating=4.5]\nPin[x=2, y=7]\n16","hidden":false}],"require":[],"forbid":[],"lines":["Cafe[name=Blue Door, pin=Pin[x=2, y=7], rating=4.5]","Pin[x=2, y=7]","16"]}]}