{"id":"useful-techniques-enum-fields","slug":"enum-fields","title":"Enums with fields, a constructor and methods","text":"An enum is a class, one whose objects are all made in advance: its constants. So it can have what a class has: instance variables, a constructor and methods.\n\nModule 21's CoffeeOrder kept its size in a String, `\"medium\"` unless the order said otherwise, and its constructor turned any size other than small, medium or large into a medium. As an enum, those three sizes are the only ones there are, and each one can carry its own volume and price:\n\n```java\nimport java.util.ArrayList;\n\npublic class Main {\n    public static void main(String[] args) {\n        CupSize size = CupSize.MEDIUM;\n        System.out.println(size + \": \" + size.getMilliliters() + \" ml, \" + size.getPrice() + \" dollars\");\n\n        ArrayList<CupSize> order = new ArrayList<>();\n        order.add(CupSize.LARGE);\n        order.add(CupSize.SMALL);\n        order.add(CupSize.LARGE);\n        int total = 0;\n        for (CupSize cup : order) {\n            total += cup.getPrice();\n        }\n        System.out.println(order + \" cost \" + total + \" dollars\");\n    }\n}\n// ==== CupSize.java ====\npublic enum CupSize {\n    SMALL(250, 3),\n    MEDIUM(350, 4),\n    LARGE(450, 5);\n\n    private int milliliters;\n    private int price;\n\n    private CupSize(int milliliters, int price) {\n        this.milliliters = milliliters;\n        this.price = price;\n    }\n\n    public int getMilliliters() {\n        return this.milliliters;\n    }\n\n    public int getPrice() {\n        return this.price;\n    }\n}\n```\n\n```output\nMEDIUM: 350 ml, 4 dollars\n[LARGE, SMALL, LARGE] cost 13 dollars\n```\n\n- The constants come first, and each one gives the constructor its values in parentheses: `SMALL(250, 3)` makes the constant SMALL with the constructor call `CupSize(250, 3)`.\n- A semicolon ends the list of constants. After it, the enum goes on like a class: instance variables, the constructor, methods.\n- The constructor is `private`: only the constants use it. The first time the program uses the enum, all three constants are made together, in the order they're written, and never again. An enum's constructor can't be public, and `new CupSize(300, 4)` doesn't compile (javac says `enum classes may not be instantiated`). The three constants are all the CupSizes there will ever be.\n- A method is called on a constant, or on a variable that holds one: `CupSize.LARGE.getPrice()`, `size.getMilliliters()`.\n- The instance variables have getters and no setters, which is usual for an enum: a constant is shared by the whole program, so changing LARGE's price would change it everywhere at once.\n\n### Methods that do more\n\nAn enum's methods can do anything a class's methods can. They can use the instance variables, `name()` and `ordinal()`, and `toString` can be overridden:\n\n```java\npublic class Main {\n    public static void main(String[] args) {\n        for (CupSize size : CupSize.values()) {\n            System.out.println(size + \": two cost \" + size.priceFor(2) + \" dollars\");\n        }\n        System.out.println(CupSize.SMALL.holds(300) + \" \" + CupSize.LARGE.holds(300));\n        System.out.println(CupSize.LARGE.name());\n    }\n}\n// ==== CupSize.java ====\npublic enum CupSize {\n    SMALL(250, 3),\n    MEDIUM(350, 4),\n    LARGE(450, 5);\n\n    private int milliliters;\n    private int price;\n\n    private CupSize(int milliliters, int price) {\n        this.milliliters = milliliters;\n        this.price = price;\n    }\n\n    public int priceFor(int cups) {\n        return cups * this.price;\n    }\n\n    public boolean holds(int milliliters) {\n        return milliliters <= this.milliliters;\n    }\n\n    @Override\n    public String toString() {\n        return this.name().toLowerCase() + \" (\" + this.milliliters + \" ml)\";\n    }\n}\n```\n\n```output\nsmall (250 ml): two cost 6 dollars\nmedium (350 ml): two cost 8 dollars\nlarge (450 ml): two cost 10 dollars\nfalse true\nLARGE\n```\n\n- With its own `toString`, a constant prints as `small (250 ml)` instead of `SMALL`.\n- `name()` still returns the constant's name as it's written in the enum, and the new `toString` uses it.\n\n### The order of the constants\n\nEvery enum has a natural order, as in module 38: the order in which its constants are written. Enums implement `Comparable`, so `Collections.sort` puts a list of CupSizes from SMALL to LARGE, and `size.compareTo(CupSize.MEDIUM) > 0` asks whether a size comes after MEDIUM. Write the constants in the order that makes sense, such as smallest first.\n","task":"Here are the planets, in order from the sun. `Planet.java` lists the eight constants, each with its diameter at the equator in kilometers, but it doesn't compile yet: the enum has no constructor for those values. Add:\n\n- a private instance variable for the diameter, and a private constructor that sets it\n- `getDiameter()`, which returns the diameter\n- `positionFromSun()`, which returns 1 for Mercury, 2 for Venus, and so on (the constants are in that order)\n\nThe check runs:\n\n```java\nSystem.out.println(Planet.EARTH.getDiameter());\nSystem.out.println(Planet.MARS.positionFromSun());\nfor (Planet planet : Planet.values()) {\n    if (planet.getDiameter() > 100000) {\n        System.out.println(planet);\n    }\n}\n```\n\nand expects:\n\n```\n12756\n4\nJUPITER\nSATURN\n```\n","kind":"code","seed":"// ==== Planet.java ====\npublic enum Planet {\n    MERCURY(4879),\n    VENUS(12104),\n    EARTH(12756),\n    MARS(6792),\n    JUPITER(142984),\n    SATURN(120536),\n    URANUS(51118),\n    NEPTUNE(49528);\n\n    // Add the instance variable, the constructor and the methods here.\n}\n\n// ==== Main.java ====\npublic class Main {\n    public static void main(String[] args) {\n        System.out.println(Planet.EARTH.getDiameter());\n        System.out.println(Planet.MARS.positionFromSun());\n        for (Planet planet : Planet.values()) {\n            if (planet.getDiameter() > 100000) {\n                System.out.println(planet);\n            }\n        }\n    }\n}\n","solution":"// ==== Planet.java ====\npublic enum Planet {\n    MERCURY(4879),\n    VENUS(12104),\n    EARTH(12756),\n    MARS(6792),\n    JUPITER(142984),\n    SATURN(120536),\n    URANUS(51118),\n    NEPTUNE(49528);\n\n    private int diameter;\n\n    private Planet(int diameter) {\n        this.diameter = diameter;\n    }\n\n    public int getDiameter() {\n        return this.diameter;\n    }\n\n    public int positionFromSun() {\n        return this.ordinal() + 1;\n    }\n}\n\n// ==== Main.java ====\npublic class Main {\n    public static void main(String[] args) {\n        System.out.println(Planet.EARTH.getDiameter());\n        System.out.println(Planet.MARS.positionFromSun());\n        for (Planet planet : Planet.values()) {\n            if (planet.getDiameter() > 100000) {\n                System.out.println(planet);\n            }\n        }\n    }\n}\n","hints":["Like CupSize: `private int diameter;`, then `private Planet(int diameter) { this.diameter = diameter; }`, and a getter.","`ordinal()` counts from 0 in the order of the constants, and Mercury comes first, so `positionFromSun()` returns `this.ordinal() + 1`."],"tests":[{"name":"Test 1","stdin":"","call":"System.out.println(Planet.EARTH.getDiameter());\nSystem.out.println(Planet.MARS.positionFromSun());\nfor (Planet planet : Planet.values()) {\n    if (planet.getDiameter() > 100000) {\n        System.out.println(planet);\n    }\n}\n","expect":"12756\n4\nJUPITER\nSATURN","hidden":false},{"name":"Test 2","stdin":"","call":"System.out.println(Planet.NEPTUNE.positionFromSun() + \" \" + Planet.MERCURY.positionFromSun());\nint bigger = 0;\nfor (Planet planet : Planet.values()) {\n    if (planet.getDiameter() > Planet.EARTH.getDiameter()) {\n        bigger++;\n    }\n}\nSystem.out.println(bigger + \" \" + Planet.VENUS.getDiameter() + \" \" + Planet.URANUS.positionFromSun());\n","expect":"8 1\n4 12104 7","hidden":true}],"require":[{"pattern":"\\bPlanet\\s*\\(\\s*int\\s+\\w+\\s*\\)","message":"Give Planet a constructor, `private Planet(int diameter)`, which the constants call with their values."},{"pattern":"\\bpublic\\s+int\\s+positionFromSun\\s*\\(\\s*\\)","message":"Write the method `public int positionFromSun()`."}],"forbid":[],"more":[{"task":"The café's cup sizes, with a method that gives the next size up. What does this program print?\n","kind":"predict","seed":"import java.util.ArrayList;\nimport java.util.Collections;\n\npublic class Main {\n    public static void main(String[] args) {\n        CupSize cup = CupSize.SMALL;\n        cup = cup.larger();\n        System.out.println(cup + \" \" + cup.getPrice());\n        cup = cup.larger().larger();\n        System.out.println(cup.name() + \" \" + (cup == CupSize.LARGE));\n\n        ArrayList<CupSize> sizes = new ArrayList<>();\n        sizes.add(CupSize.LARGE);\n        sizes.add(CupSize.SMALL);\n        sizes.add(CupSize.MEDIUM);\n        Collections.sort(sizes);\n        System.out.println(sizes);\n    }\n}\n// ==== CupSize.java ====\npublic enum CupSize {\n    SMALL(250, 3),\n    MEDIUM(350, 4),\n    LARGE(450, 5);\n\n    private int milliliters;\n    private int price;\n\n    private CupSize(int milliliters, int price) {\n        this.milliliters = milliliters;\n        this.price = price;\n    }\n\n    public int getPrice() {\n        return this.price;\n    }\n\n    public CupSize larger() {\n        CupSize[] sizes = CupSize.values();\n        if (this.ordinal() + 1 < sizes.length) {\n            return sizes[this.ordinal() + 1];\n        }\n        return this;\n    }\n}\n","solution":"import java.util.ArrayList;\nimport java.util.Collections;\n\npublic class Main {\n    public static void main(String[] args) {\n        CupSize cup = CupSize.SMALL;\n        cup = cup.larger();\n        System.out.println(cup + \" \" + cup.getPrice());\n        cup = cup.larger().larger();\n        System.out.println(cup.name() + \" \" + (cup == CupSize.LARGE));\n\n        ArrayList<CupSize> sizes = new ArrayList<>();\n        sizes.add(CupSize.LARGE);\n        sizes.add(CupSize.SMALL);\n        sizes.add(CupSize.MEDIUM);\n        Collections.sort(sizes);\n        System.out.println(sizes);\n    }\n}\n// ==== CupSize.java ====\npublic enum CupSize {\n    SMALL(250, 3),\n    MEDIUM(350, 4),\n    LARGE(450, 5);\n\n    private int milliliters;\n    private int price;\n\n    private CupSize(int milliliters, int price) {\n        this.milliliters = milliliters;\n        this.price = price;\n    }\n\n    public int getPrice() {\n        return this.price;\n    }\n\n    public CupSize larger() {\n        CupSize[] sizes = CupSize.values();\n        if (this.ordinal() + 1 < sizes.length) {\n            return sizes[this.ordinal() + 1];\n        }\n        return this;\n    }\n}\n","hints":["`larger()` returns the constant after this one, or this one when there's none after it. SMALL's next is MEDIUM, which costs 4.","From MEDIUM, two calls give LARGE and then LARGE again, since there's nothing larger. `Collections.sort` uses the natural order, the order in which the constants are written."],"tests":[{"name":"Output","stdin":"","expect":"MEDIUM 4\nLARGE true\n[SMALL, MEDIUM, LARGE]","hidden":false}],"require":[],"forbid":[],"lines":["MEDIUM 4","LARGE true","[SMALL, MEDIUM, LARGE]"]},{"task":"A cash register gives change in four kinds of US bills: ones, fives, tens and twenties. Write:\n\n- in `Bill.java`, the enum `Bill`, with the constants `ONE`, `FIVE`, `TEN` and `TWENTY`, in that order, each with its value in dollars, and the method `getDollars()`\n- in `Main.java`, the method `change(int dollars)`, which returns an `ArrayList<Bill>` that makes up the amount with as few bills as possible: as many twenties as fit, then as many tens as fit in what's left, then fives, then ones\n\nGo through `Bill.values()` backwards with an index loop, as in step 1, so the largest bill comes first. The check runs:\n\n```java\nSystem.out.println(change(68));\nSystem.out.println(change(30));\n```\n\nand expects:\n\n```\n[TWENTY, TWENTY, TWENTY, FIVE, ONE, ONE, ONE]\n[TWENTY, TEN]\n```\n","kind":"code","seed":"// ==== Bill.java ====\npublic enum Bill {\n\n}\n\n// ==== Main.java ====\nimport java.util.ArrayList;\n\npublic class Main {\n    public static void main(String[] args) {\n        System.out.println(change(68));\n        System.out.println(change(30));\n    }\n\n    // Write the method change here.\n}\n","solution":"// ==== Bill.java ====\npublic enum Bill {\n    ONE(1),\n    FIVE(5),\n    TEN(10),\n    TWENTY(20);\n\n    private int dollars;\n\n    private Bill(int dollars) {\n        this.dollars = dollars;\n    }\n\n    public int getDollars() {\n        return this.dollars;\n    }\n}\n\n// ==== Main.java ====\nimport java.util.ArrayList;\n\npublic class Main {\n    public static void main(String[] args) {\n        System.out.println(change(68));\n        System.out.println(change(30));\n    }\n\n    public static ArrayList<Bill> change(int dollars) {\n        ArrayList<Bill> bills = new ArrayList<>();\n        Bill[] kinds = Bill.values();\n        for (int i = kinds.length - 1; i >= 0; i--) {\n            while (dollars >= kinds[i].getDollars()) {\n                bills.add(kinds[i]);\n                dollars -= kinds[i].getDollars();\n            }\n        }\n        return bills;\n    }\n}\n","hints":["The enum is like CupSize with one value: `ONE(1), FIVE(5), TEN(10), TWENTY(20);`, a private `int dollars`, a private constructor and `getDollars()`.","In `change`, take `Bill[] kinds = Bill.values();` and loop `for (int i = kinds.length - 1; i >= 0; i--)`. Inside it, a `while` loop adds `kinds[i]` to the list and takes its dollars off the amount, as long as the bill still fits."],"tests":[{"name":"Test 1","stdin":"","call":"System.out.println(change(68));\nSystem.out.println(change(30));\n","expect":"[TWENTY, TWENTY, TWENTY, FIVE, ONE, ONE, ONE]\n[TWENTY, TEN]","hidden":false},{"name":"Test 2","stdin":"","call":"System.out.println(change(0));\nSystem.out.println(change(4));\nSystem.out.println(change(99).size() + \" \" + change(50));\nSystem.out.println(Bill.TEN.getDollars() + \" \" + Bill.TWENTY.getDollars() + \" \" + Bill.values().length);\n","expect":"[]\n[ONE, ONE, ONE, ONE]\n10 [TWENTY, TWENTY, TEN]\n10 20 4","hidden":true},{"name":"Test 3","stdin":"","call":"ArrayList<Bill> bills = change(41);\nint total = 0;\nfor (Bill bill : bills) {\n    total += bill.getDollars();\n}\nSystem.out.println(bills + \" \" + total);\nSystem.out.println(change(15) + \" \" + change(125).size());\n","expect":"[TWENTY, TWENTY, ONE] 41\n[TEN, FIVE] 7","hidden":true}],"require":[{"pattern":"\\benum\\s+Bill\\b","message":"Write `Bill` as an enum: `public enum Bill { ... }`."},{"pattern":"\\bBill\\s*\\(\\s*int\\s+\\w+\\s*\\)","message":"Give Bill a constructor, `private Bill(int dollars)`, which the constants call with their values."},{"pattern":"\\.values\\s*\\(\\s*\\)","message":"Go through the bills with `Bill.values()`, largest first."}],"forbid":[]}]}