{"id":"object-methods-bigger-class","slug":"a-class-that-remembers","title":"A class that remembers","text":"Objects are good at remembering. A class can collect numbers one at a time and answer questions about them whenever it's asked: how many there have been, their total, their average, the largest. This log of bike trips does that:\n\n```java\npublic class Main {\n    public static void main(String[] args) {\n        TripLog log = new TripLog();\n        log.add(12);\n        log.add(40);\n        log.add(5);\n        System.out.println(\"Trips: \" + log.tripCount());\n        System.out.println(\"Total: \" + log.totalKm() + \" km\");\n        System.out.println(\"Longest: \" + log.longestKm() + \" km\");\n        System.out.println(\"Average: \" + log.averageKm() + \" km\");\n    }\n}\n// ==== TripLog.java ====\npublic class TripLog {\n    private int count;\n    private int total;\n    private int longest;\n\n    public TripLog() {\n        this.count = 0;\n        this.total = 0;\n        this.longest = 0;\n    }\n\n    public void add(int km) {\n        this.count++;\n        this.total = this.total + km;\n        if (km > this.longest) {\n            this.longest = km;\n        }\n    }\n\n    public int tripCount() {\n        return this.count;\n    }\n\n    public int totalKm() {\n        return this.total;\n    }\n\n    public int longestKm() {\n        return this.longest;\n    }\n\n    public double averageKm() {\n        if (this.count == 0) {\n            return 0.0;\n        }\n        return (double) this.total / this.count;\n    }\n}\n```\n\n```output\nTrips: 3\nTotal: 57 km\nLongest: 40 km\nAverage: 19.0 km\n```\n\n- The log doesn't keep the trips themselves, only what it needs for its answers: how many trips, their sum, and the longest so far. Each `add` updates all three. (Later in the course, an object gets a list of its own.)\n- The average isn't stored: `averageKm()` works it out when it's asked, from the count and the total. `(double)` turns the total into a `double` before the division, so the decimals aren't lost (module 4).\n- `averageKm()` checks for an empty log first. With no trips there's no average, and the method returns 0.0 instead of dividing by zero.\n\nThe class does the bookkeeping, and `main` only gives it numbers and asks questions. The same class works whether the numbers are written in the code, as here, or typed in by a user. What a log like this remembers depends on the questions it has to answer: the challenges below keep track of other things than a count and a total.\n","task":"A `RainLog` collects the rainfall of each day, in millimeters. Write its methods: `add(int mm)` records one day's rain, `dryDays()` returns how many days had no rain at all, and `wettest()` returns the most rain on a single day (0 before any rain). The instance variables are ready, and `add` keeps both of them up to date. The check records 4, 0, 11 and 0 mm:\n\n```\n2 dry days, wettest 11 mm\n```\n","kind":"code","seed":"// ==== RainLog.java ====\npublic class RainLog {\n    private int dryDays;\n    private int wettest;\n\n    public RainLog() {\n        this.dryDays = 0;\n        this.wettest = 0;\n    }\n\n    // Write add, dryDays and wettest here.\n}\n\n// ==== Main.java ====\npublic class Main {\n    public static void main(String[] args) {\n        RainLog log = new RainLog();\n        log.add(4);\n        log.add(0);\n        log.add(11);\n        log.add(0);\n        System.out.println(log.dryDays() + \" dry days, wettest \" + log.wettest() + \" mm\");\n    }\n}\n","solution":"// ==== RainLog.java ====\npublic class RainLog {\n    private int dryDays;\n    private int wettest;\n\n    public RainLog() {\n        this.dryDays = 0;\n        this.wettest = 0;\n    }\n\n    public void add(int mm) {\n        if (mm == 0) {\n            this.dryDays++;\n        }\n        if (mm > this.wettest) {\n            this.wettest = mm;\n        }\n    }\n\n    public int dryDays() {\n        return this.dryDays;\n    }\n\n    public int wettest() {\n        return this.wettest;\n    }\n}\n\n// ==== Main.java ====\npublic class Main {\n    public static void main(String[] args) {\n        RainLog log = new RainLog();\n        log.add(4);\n        log.add(0);\n        log.add(11);\n        log.add(0);\n        System.out.println(log.dryDays() + \" dry days, wettest \" + log.wettest() + \" mm\");\n    }\n}\n","hints":["`add` is `void`. It has two `if`s: one counts the day when `mm == 0`, the other stores `mm` in `this.wettest` when it's bigger.","`dryDays()` and `wettest()` return the instance variables: `return this.dryDays;` and `return this.wettest;`"],"tests":[{"name":"Test 1","stdin":"","call":"RainLog log = new RainLog();\nlog.add(4);\nlog.add(0);\nlog.add(11);\nlog.add(0);\nSystem.out.println(log.dryDays() + \" dry days, wettest \" + log.wettest() + \" mm\");\n","expect":"2 dry days, wettest 11 mm","hidden":false},{"name":"Test 2","stdin":"","call":"RainLog log = new RainLog();\nSystem.out.println(log.dryDays());\nSystem.out.println(log.wettest());\n","expect":"0\n0","hidden":true},{"name":"Test 3","stdin":"","call":"RainLog spring = new RainLog();\nRainLog autumn = new RainLog();\nspring.add(25);\nspring.add(7);\nautumn.add(0);\nautumn.add(9);\nautumn.add(0);\nautumn.add(0);\nSystem.out.println(spring.dryDays() + \" \" + spring.wettest());\nSystem.out.println(autumn.dryDays() + \" \" + autumn.wettest());\n","expect":"0 25\n3 9","hidden":true}],"require":[],"forbid":[],"more":[{"task":"A dry spell is a run of dry days in a row. Add a method `longestDrySpell()` that returns the most dry days in a row so far (0 when there hasn't been a dry day). The log has to remember two more things: how many dry days in a row there have been up to now, and the longest run so far. Add instance variables for them, and update them in `add`: a dry day makes the current run one day longer, and a day with rain ends it. The check records 3, 0, 0 and 5 mm and asks, then records 0, 0, 0 and 2 mm and asks again:\n\n```\n2\n3\n```\n","kind":"code","seed":"// ==== RainLog.java ====\npublic class RainLog {\n    private int dryDays;\n    private int wettest;\n\n    public RainLog() {\n        this.dryDays = 0;\n        this.wettest = 0;\n    }\n\n    public void add(int mm) {\n        if (mm == 0) {\n            this.dryDays++;\n        }\n        if (mm > this.wettest) {\n            this.wettest = mm;\n        }\n    }\n\n    public int dryDays() {\n        return this.dryDays;\n    }\n\n    public int wettest() {\n        return this.wettest;\n    }\n\n    // Write longestDrySpell here, and keep what it needs up to date in add.\n}\n\n// ==== Main.java ====\npublic class Main {\n    public static void main(String[] args) {\n        RainLog log = new RainLog();\n        log.add(3);\n        log.add(0);\n        log.add(0);\n        log.add(5);\n        log.add(0);\n        System.out.println(\"Longest dry spell: \" + log.longestDrySpell() + \" days\");\n    }\n}\n","solution":"// ==== RainLog.java ====\npublic class RainLog {\n    private int dryDays;\n    private int wettest;\n    private int currentSpell;\n    private int longestSpell;\n\n    public RainLog() {\n        this.dryDays = 0;\n        this.wettest = 0;\n        this.currentSpell = 0;\n        this.longestSpell = 0;\n    }\n\n    public void add(int mm) {\n        if (mm == 0) {\n            this.dryDays++;\n            this.currentSpell++;\n            if (this.currentSpell > this.longestSpell) {\n                this.longestSpell = this.currentSpell;\n            }\n        } else {\n            this.currentSpell = 0;\n        }\n        if (mm > this.wettest) {\n            this.wettest = mm;\n        }\n    }\n\n    public int dryDays() {\n        return this.dryDays;\n    }\n\n    public int wettest() {\n        return this.wettest;\n    }\n\n    public int longestDrySpell() {\n        return this.longestSpell;\n    }\n}\n\n// ==== Main.java ====\npublic class Main {\n    public static void main(String[] args) {\n        RainLog log = new RainLog();\n        log.add(3);\n        log.add(0);\n        log.add(0);\n        log.add(5);\n        log.add(0);\n        System.out.println(\"Longest dry spell: \" + log.longestDrySpell() + \" days\");\n    }\n}\n","hints":["Add `private int currentSpell;` and `private int longestSpell;`, both 0 at the start. `longestDrySpell()` returns `this.longestSpell`.","In `add`: on a dry day, `this.currentSpell++`, and when it's now longer than `this.longestSpell`, store it there. On a day with rain, `this.currentSpell = 0;`"],"tests":[{"name":"Test 1","stdin":"","call":"RainLog log = new RainLog();\nlog.add(3);\nlog.add(0);\nlog.add(0);\nlog.add(5);\nSystem.out.println(log.longestDrySpell());\nlog.add(0);\nlog.add(0);\nlog.add(0);\nlog.add(2);\nSystem.out.println(log.longestDrySpell());\n","expect":"2\n3","hidden":false},{"name":"Test 2","stdin":"","call":"RainLog log = new RainLog();\nSystem.out.println(log.longestDrySpell());\nlog.add(4);\nlog.add(6);\nSystem.out.println(log.longestDrySpell());\n","expect":"0\n0","hidden":true},{"name":"Test 3","stdin":"","call":"RainLog log = new RainLog();\nlog.add(0);\nlog.add(0);\nlog.add(7);\nlog.add(0);\nSystem.out.println(log.longestDrySpell());\nSystem.out.println(log.dryDays() + \" \" + log.wettest());\n","expect":"2\n3 7","hidden":true},{"name":"Test 4","stdin":"","call":"RainLog log = new RainLog();\nlog.add(1);\nlog.add(0);\nlog.add(0);\nlog.add(0);\nlog.add(0);\nSystem.out.println(log.longestDrySpell());\n","expect":"4","hidden":true}],"require":[],"forbid":[]},{"task":"Now use the finished `RainLog` in a program that gives a one-line verdict on the weather. Read the rain of each day, one number per line, until an empty line, and add each number to a `RainLog`. Then print one line:\n\n- `Drought warning: ` and the longest dry spell followed by ` dry days in a row`, when the longest dry spell is 3 days or more;\n- otherwise `Flood watch: ` and the wettest day's rain followed by ` mm in one day`, when the wettest day had 20 mm or more;\n- otherwise `Normal weather`.\n\nFor the input `0`, `0`, `4`, `0`, `0`, `0`, `1` and an empty line, the program prints:\n\n```\nDrought warning: 3 dry days in a row\n```\n","kind":"code","seed":"// ==== Main.java ====\nimport java.util.Scanner;\n\npublic class Main {\n    public static void main(String[] args) {\n        Scanner scanner = new Scanner(System.in);\n        RainLog log = new RainLog();\n\n    }\n}\n\n// ==== RainLog.java ====\npublic class RainLog {\n    private int dryDays;\n    private int wettest;\n    private int currentSpell;\n    private int longestSpell;\n\n    public RainLog() {\n        this.dryDays = 0;\n        this.wettest = 0;\n        this.currentSpell = 0;\n        this.longestSpell = 0;\n    }\n\n    public void add(int mm) {\n        if (mm == 0) {\n            this.dryDays++;\n            this.currentSpell++;\n            if (this.currentSpell > this.longestSpell) {\n                this.longestSpell = this.currentSpell;\n            }\n        } else {\n            this.currentSpell = 0;\n        }\n        if (mm > this.wettest) {\n            this.wettest = mm;\n        }\n    }\n\n    public int dryDays() {\n        return this.dryDays;\n    }\n\n    public int wettest() {\n        return this.wettest;\n    }\n\n    public int longestDrySpell() {\n        return this.longestSpell;\n    }\n}\n","solution":"// ==== Main.java ====\nimport java.util.Scanner;\n\npublic class Main {\n    public static void main(String[] args) {\n        Scanner scanner = new Scanner(System.in);\n        RainLog log = new RainLog();\n        while (true) {\n            String line = scanner.nextLine();\n            if (line.isEmpty()) {\n                break;\n            }\n            log.add(Integer.valueOf(line));\n        }\n        if (log.longestDrySpell() >= 3) {\n            System.out.println(\"Drought warning: \" + log.longestDrySpell() + \" dry days in a row\");\n        } else if (log.wettest() >= 20) {\n            System.out.println(\"Flood watch: \" + log.wettest() + \" mm in one day\");\n        } else {\n            System.out.println(\"Normal weather\");\n        }\n    }\n}\n\n// ==== RainLog.java ====\npublic class RainLog {\n    private int dryDays;\n    private int wettest;\n    private int currentSpell;\n    private int longestSpell;\n\n    public RainLog() {\n        this.dryDays = 0;\n        this.wettest = 0;\n        this.currentSpell = 0;\n        this.longestSpell = 0;\n    }\n\n    public void add(int mm) {\n        if (mm == 0) {\n            this.dryDays++;\n            this.currentSpell++;\n            if (this.currentSpell > this.longestSpell) {\n                this.longestSpell = this.currentSpell;\n            }\n        } else {\n            this.currentSpell = 0;\n        }\n        if (mm > this.wettest) {\n            this.wettest = mm;\n        }\n    }\n\n    public int dryDays() {\n        return this.dryDays;\n    }\n\n    public int wettest() {\n        return this.wettest;\n    }\n\n    public int longestDrySpell() {\n        return this.longestSpell;\n    }\n}\n","hints":["A `while (true)` loop reads a line, stops with `break` when `line.isEmpty()`, and otherwise calls `log.add(Integer.valueOf(line));`.","After the loop, ask the log: `if (log.longestDrySpell() >= 3) { ... } else if (log.wettest() >= 20) { ... } else { ... }`."],"tests":[{"name":"Test 1","stdin":"0\n0\n4\n0\n0\n0\n1\n\n","expect":"Drought warning: 3 dry days in a row","hidden":false},{"name":"Test 2","stdin":"5\n25\n0\n\n","expect":"Flood watch: 25 mm in one day","hidden":true},{"name":"Test 3","stdin":"3\n0\n2\n0\n0\n\n","expect":"Normal weather","hidden":true},{"name":"Test 4","stdin":"\n","expect":"Normal weather","hidden":true},{"name":"Test 5","stdin":"30\n0\n0\n0\n\n","expect":"Drought warning: 3 dry days in a row","hidden":true},{"name":"Test 6","stdin":"20\n\n","expect":"Flood watch: 20 mm in one day","hidden":true}],"require":[{"pattern":"\\blog\\.add\\s*\\(","message":"Add each day's rain to the log with `log.add(...)`."},{"pattern":"\\blog\\.longestDrySpell\\s*\\(","message":"Let the log answer: use `log.longestDrySpell()` for the drought warning."},{"pattern":"\\blog\\.wettest\\s*\\(","message":"Let the log answer: use `log.wettest()` for the flood watch."}],"forbid":[]}]}