{"id":"recap-classes","slug":"classes-and-objects","title":"A class of your own","text":"A class bundles values that belong together with the methods that work on them. Most classes in this course have had the same parts: private instance variables, a constructor, getters, methods that change the object, and a `toString`. Some also have an `equals`.\n\n### The tools\n\n| Tool | What it does | Taught in |\n| --- | --- | --- |\n| `public class Plank` in the file `Plank.java`, with `private` instance variables | a class of your own | module 17, Classes and objects |\n| a constructor, with `this.length = length;` | sets up a new object | module 17, Classes and objects |\n| a method that changes an instance variable | changes the object | module 17, Classes and objects |\n| getters such as `getLength()`, and `toString()` | give values out, and the text `println` shows | module 18, Getters, toString and bigger classes |\n| `==` on two objects | are they the very same object? | module 22, Primitive and reference variables |\n| `public boolean equals(Object compared)` | what \"equal\" means for this class | module 23, Objects working together |\n\n### A worked example\n\nAt a climbing gym, each wall has one route of each color, so a route is known by its wall and the color of its holds. It also has a grade, and the gym counts its ascents: how many times someone has climbed it to the top.\n\n```java\npublic class Main {\n    public static void main(String[] args) {\n        ClimbingRoute route = new ClimbingRoute(\"north wall\", \"red\", 5);\n        route.climb();\n        route.climb();\n        System.out.println(route);\n\n        ClimbingRoute written = new ClimbingRoute(\"north wall\", \"red\", 5);\n        System.out.println(route == written);\n        System.out.println(route.equals(written));\n        System.out.println(route.equals(new ClimbingRoute(\"north wall\", \"blue\", 5)));\n    }\n}\n// ==== ClimbingRoute.java ====\npublic class ClimbingRoute {\n    private String wall;\n    private String color;\n    private int grade;\n    private int ascents;\n\n    public ClimbingRoute(String wall, String color, int grade) {\n        this.wall = wall;\n        this.color = color;\n        this.grade = grade;\n        this.ascents = 0;\n    }\n\n    public String getWall() {\n        return this.wall;\n    }\n\n    public String getColor() {\n        return this.color;\n    }\n\n    public int getGrade() {\n        return this.grade;\n    }\n\n    public int getAscents() {\n        return this.ascents;\n    }\n\n    public void climb() {\n        this.ascents++;\n    }\n\n    public boolean equals(Object compared) {\n        if (this == compared) {\n            return true;\n        }\n        if (!(compared instanceof ClimbingRoute)) {\n            return false;\n        }\n        ClimbingRoute other = (ClimbingRoute) compared;\n        return this.wall.equals(other.wall) && this.color.equals(other.color);\n    }\n\n    public String toString() {\n        return this.color + \" route on the \" + this.wall + \", grade \" + this.grade + \", ascents: \" + this.ascents;\n    }\n}\n```\n\n```output\nred route on the north wall, grade 5, ascents: 2\nfalse\ntrue\nfalse\n```\n\n- The constructor's parameters have the same names as the instance variables. `this.wall` is the object's variable, and `wall` alone is the parameter (module 18).\n- `climb()` returns nothing: it changes the object's state. The getters only hand values out.\n- `route == written` is false: two `new`s make two objects, whatever they hold. `equals` compares what's in them, in the way the class decides.\n- The `equals` is module 23's, step by step: the exact header `public boolean equals(Object compared)`, the check for the very same object, the `instanceof` check, the cast, and then the comparison. Strings are compared with `equals`, and numbers with `==`.\n- This `equals` compares only the wall and the color, the values that tell routes apart. The ascents change every day, and they don't make it another route.\n- The list methods `contains`, `indexOf` and `remove(value)` use `equals` too (module 23), so a list of routes can find a route by its wall and color.\n\nModule 32 comes back to `equals`, and adds its partner, `hashCode`.\n","task":"A woodworking class keeps its planks in a rack. In `Plank.java`, write the class `Plank`, with private instance variables for the wood and the length:\n\n- a constructor `Plank(String wood, int length)`: the kind of wood, such as `oak`, and the length in centimeters,\n- the getters `getWood()` and `getLength()`,\n- a method `cut(int piece)` that saws a piece of that many centimeters off the plank, so the plank gets shorter by that much. If the piece is longer than the plank, nothing happens.\n- `toString()`, which returns the wood and the length, like `oak, 120 cm`.\n\n`Main.java` is a test drive. Until Plank has the constructor, `cut` and both getters, Main.java doesn't compile: each error names a part that's still missing. When it's done, the test drive prints:\n\n```\noak, 120 cm\noak, 75 cm\noak, 75 cm\n75 oak\n```\n","kind":"code","seed":"// ==== Plank.java ====\npublic class Plank {\n\n}\n\n// ==== Main.java ====\npublic class Main {\n    public static void main(String[] args) {\n        Plank plank = new Plank(\"oak\", 120);\n        System.out.println(plank);\n        plank.cut(45);\n        System.out.println(plank);\n        plank.cut(80);\n        System.out.println(plank);\n        System.out.println(plank.getLength() + \" \" + plank.getWood());\n    }\n}\n","solution":"// ==== Plank.java ====\npublic class Plank {\n    private String wood;\n    private int length;\n\n    public Plank(String wood, int length) {\n        this.wood = wood;\n        this.length = length;\n    }\n\n    public String getWood() {\n        return this.wood;\n    }\n\n    public int getLength() {\n        return this.length;\n    }\n\n    public void cut(int piece) {\n        if (piece <= this.length) {\n            this.length -= piece;\n        }\n    }\n\n    public String toString() {\n        return this.wood + \", \" + this.length + \" cm\";\n    }\n}\n\n// ==== Main.java ====\npublic class Main {\n    public static void main(String[] args) {\n        Plank plank = new Plank(\"oak\", 120);\n        System.out.println(plank);\n        plank.cut(45);\n        System.out.println(plank);\n        plank.cut(80);\n        System.out.println(plank);\n        System.out.println(plank.getLength() + \" \" + plank.getWood());\n    }\n}\n","hints":["Two private instance variables, `private String wood;` and `private int length;`, set in the constructor with `this.wood = wood;` and `this.length = length;`. The getters return them.","`cut` changes the length only `if (piece <= this.length)`, with `this.length -= piece;`. `toString` returns `this.wood + \", \" + this.length + \" cm\"`."],"tests":[{"name":"The test drive","stdin":"","expect":"oak, 120 cm\noak, 75 cm\noak, 75 cm\n75 oak","hidden":false},{"name":"Test 2","stdin":"","call":"Plank pine = new Plank(\"pine\", 200);\nPlank birch = new Plank(\"birch\", 60);\npine.cut(200);\nbirch.cut(61);\nbirch.cut(0);\nSystem.out.println(pine + \" / \" + birch);\nSystem.out.println(pine.getLength() + birch.getLength());\n","expect":"pine, 0 cm / birch, 60 cm\n60","hidden":true},{"name":"Test 3","stdin":"","call":"Plank ash = new Plank(\"ash\", 95);\nash.cut(30);\nash.cut(30);\nash.cut(30);\nash.cut(30);\nSystem.out.println(ash.getWood() + \" \" + ash.getLength());\n","expect":"ash 5","hidden":true}],"require":[{"pattern":"\\bprivate\\s+int\\s+\\w+\\s*[;=]","message":"Keep the length in a private `int` instance variable."}],"forbid":[],"more":[{"task":"The gym's staff have regraded a route, so a second object for it has a different grade. The class is the lesson's `ClimbingRoute`, shortened to the parts this program uses. What does this program print?\n","kind":"predict","seed":"public class Main {\n    public static void main(String[] args) {\n        ClimbingRoute a = new ClimbingRoute(\"overhang\", \"green\", 4);\n        ClimbingRoute b = new ClimbingRoute(\"overhang\", \"green\", 6);\n        ClimbingRoute c = a;\n        a.climb();\n        c.climb();\n        b.climb();\n        System.out.println(a == c);\n        System.out.println(a.equals(b));\n        System.out.println(a.getAscents() + \" \" + b.getAscents());\n    }\n}\n// ==== ClimbingRoute.java ====\npublic class ClimbingRoute {\n    private String wall;\n    private String color;\n    private int grade;\n    private int ascents;\n\n    public ClimbingRoute(String wall, String color, int grade) {\n        this.wall = wall;\n        this.color = color;\n        this.grade = grade;\n        this.ascents = 0;\n    }\n\n    public int getAscents() {\n        return this.ascents;\n    }\n\n    public void climb() {\n        this.ascents++;\n    }\n\n    public boolean equals(Object compared) {\n        if (this == compared) {\n            return true;\n        }\n        if (!(compared instanceof ClimbingRoute)) {\n            return false;\n        }\n        ClimbingRoute other = (ClimbingRoute) compared;\n        return this.wall.equals(other.wall) && this.color.equals(other.color);\n    }\n}\n","solution":"public class Main {\n    public static void main(String[] args) {\n        ClimbingRoute a = new ClimbingRoute(\"overhang\", \"green\", 4);\n        ClimbingRoute b = new ClimbingRoute(\"overhang\", \"green\", 6);\n        ClimbingRoute c = a;\n        a.climb();\n        c.climb();\n        b.climb();\n        System.out.println(a == c);\n        System.out.println(a.equals(b));\n        System.out.println(a.getAscents() + \" \" + b.getAscents());\n    }\n}\n// ==== ClimbingRoute.java ====\npublic class ClimbingRoute {\n    private String wall;\n    private String color;\n    private int grade;\n    private int ascents;\n\n    public ClimbingRoute(String wall, String color, int grade) {\n        this.wall = wall;\n        this.color = color;\n        this.grade = grade;\n        this.ascents = 0;\n    }\n\n    public int getAscents() {\n        return this.ascents;\n    }\n\n    public void climb() {\n        this.ascents++;\n    }\n\n    public boolean equals(Object compared) {\n        if (this == compared) {\n            return true;\n        }\n        if (!(compared instanceof ClimbingRoute)) {\n            return false;\n        }\n        ClimbingRoute other = (ClimbingRoute) compared;\n        return this.wall.equals(other.wall) && this.color.equals(other.color);\n    }\n}\n","hints":["`ClimbingRoute c = a;` copies the reference, not the object: `a` and `c` are two names for one route. So `a == c`, and both `climb()` calls count on that one route.","`equals` compares only the wall and the color, not the grade, so `a` and `b` are equal even though they're two objects with different grades."],"tests":[{"name":"Output","stdin":"","expect":"true\ntrue\n2 1","hidden":false}],"require":[],"forbid":[],"lines":["true","true","2 1"]},{"task":"A school play keeps its cast in a list of `Role` objects: the play, the character, and the student who plays the character. Two roles are equal when they're the same character in the same play, whoever plays it. (Some names are in more than one play: Shakespeare has a Sebastian in both *Twelfth Night* and *The Tempest*.) In `Role.java`, write the method `equals`, the way module 23 did. The check runs:\n\n```java\nArrayList<Role> cast = new ArrayList<>();\ncast.add(new Role(\"The Tempest\", \"Ariel\", \"Noor\"));\ncast.add(new Role(\"The Tempest\", \"Miranda\", \"Liv\"));\nRole understudy = new Role(\"The Tempest\", \"Ariel\", \"Theo\");\nSystem.out.println(understudy.equals(cast.get(0)));\nSystem.out.println(cast.contains(new Role(\"Twelfth Night\", \"Viola\", \"Liv\")));\nSystem.out.println(cast.indexOf(new Role(\"The Tempest\", \"Miranda\", \"Sam\")));\n```\n\nand expects:\n\n```\ntrue\nfalse\n1\n```\n","kind":"code","seed":"// ==== Role.java ====\npublic class Role {\n    private String play;\n    private String character;\n    private String student;\n\n    public Role(String play, String character, String student) {\n        this.play = play;\n        this.character = character;\n        this.student = student;\n    }\n\n    public String getStudent() {\n        return this.student;\n    }\n\n    // Write the equals method here.\n\n    public String toString() {\n        return this.character + \" in \" + this.play + \": \" + this.student;\n    }\n}\n\n// ==== Main.java ====\nimport java.util.ArrayList;\n\npublic class Main {\n    public static void main(String[] args) {\n        ArrayList<Role> cast = new ArrayList<>();\n        cast.add(new Role(\"The Tempest\", \"Ariel\", \"Noor\"));\n        cast.add(new Role(\"The Tempest\", \"Miranda\", \"Liv\"));\n        Role understudy = new Role(\"The Tempest\", \"Ariel\", \"Theo\");\n        System.out.println(understudy.equals(cast.get(0)));\n        System.out.println(cast.contains(new Role(\"Twelfth Night\", \"Viola\", \"Liv\")));\n        System.out.println(cast.indexOf(new Role(\"The Tempest\", \"Miranda\", \"Sam\")));\n    }\n}\n","solution":"// ==== Role.java ====\npublic class Role {\n    private String play;\n    private String character;\n    private String student;\n\n    public Role(String play, String character, String student) {\n        this.play = play;\n        this.character = character;\n        this.student = student;\n    }\n\n    public String getStudent() {\n        return this.student;\n    }\n\n    public boolean equals(Object compared) {\n        if (this == compared) {\n            return true;\n        }\n        if (!(compared instanceof Role)) {\n            return false;\n        }\n        Role other = (Role) compared;\n        return this.play.equals(other.play) && this.character.equals(other.character);\n    }\n\n    public String toString() {\n        return this.character + \" in \" + this.play + \": \" + this.student;\n    }\n}\n\n// ==== Main.java ====\nimport java.util.ArrayList;\n\npublic class Main {\n    public static void main(String[] args) {\n        ArrayList<Role> cast = new ArrayList<>();\n        cast.add(new Role(\"The Tempest\", \"Ariel\", \"Noor\"));\n        cast.add(new Role(\"The Tempest\", \"Miranda\", \"Liv\"));\n        Role understudy = new Role(\"The Tempest\", \"Ariel\", \"Theo\");\n        System.out.println(understudy.equals(cast.get(0)));\n        System.out.println(cast.contains(new Role(\"Twelfth Night\", \"Viola\", \"Liv\")));\n        System.out.println(cast.indexOf(new Role(\"The Tempest\", \"Miranda\", \"Sam\")));\n    }\n}\n","hints":["Follow the `equals` of `ClimbingRoute` above, with Role in its place: the header `public boolean equals(Object compared)`, the check `this == compared`, the `instanceof Role` check, and the cast `Role other = (Role) compared;`.","The last line compares the play and the character, both Strings, with `equals`: `return this.play.equals(other.play) && this.character.equals(other.character);`. Leave the student out."],"tests":[{"name":"Test 1","stdin":"","call":"ArrayList<Role> cast = new ArrayList<>();\ncast.add(new Role(\"The Tempest\", \"Ariel\", \"Noor\"));\ncast.add(new Role(\"The Tempest\", \"Miranda\", \"Liv\"));\nRole understudy = new Role(\"The Tempest\", \"Ariel\", \"Theo\");\nSystem.out.println(understudy.equals(cast.get(0)));\nSystem.out.println(cast.contains(new Role(\"Twelfth Night\", \"Viola\", \"Liv\")));\nSystem.out.println(cast.indexOf(new Role(\"The Tempest\", \"Miranda\", \"Sam\")));\n","expect":"true\nfalse\n1","hidden":false},{"name":"Test 2","stdin":"","call":"Role twins = new Role(\"Twelfth Night\", \"Sebastian\", \"Oskar\");\nRole brother = new Role(\"The Tempest\", \"Sebastian\", \"Oskar\");\nSystem.out.println(twins.equals(brother) + \" \" + brother.equals(twins));\nSystem.out.println(twins.equals(twins) + \" \" + twins.equals(\"Sebastian\") + \" \" + twins.equals(null));\nString play = \"Twelfth\";\nSystem.out.println(twins.equals(new Role(play + \" Night\", \"Sebastian\", \"Ida\")));\n","expect":"false false\ntrue false false\ntrue","hidden":true},{"name":"Test 3","stdin":"","call":"ArrayList<Role> cast = new ArrayList<>();\ncast.add(new Role(\"Macbeth\", \"Banquo\", \"Eli\"));\ncast.add(new Role(\"Macbeth\", \"Macduff\", \"Fay\"));\ncast.remove(new Role(\"Macbeth\", \"Banquo\", \"Gus\"));\nSystem.out.println(cast);\nSystem.out.println(cast.contains(new Role(\"Hamlet\", \"Macduff\", \"Fay\")));\n","expect":"[Macduff in Macbeth: Fay]\nfalse","hidden":true}],"require":[{"pattern":"\\bpublic\\s+boolean\\s+equals\\s*\\(\\s*Object\\s+\\w+\\s*\\)","message":"Write the header exactly as `public boolean equals(Object compared)`, so that the list methods use your method."},{"pattern":"\\binstanceof\\s+Role\\b","message":"Check with `instanceof Role` that the compared object is a role, before the cast."}],"forbid":[]}]}