{"id":"gui-events-effectively-final","slug":"effectively-final","title":"Handlers that remember","text":"A pomodoro is 25 minutes of focused work, then a short break. A window counts the pomodoros finished today: a label with the count, and a button to add one. The first try keeps the count in a variable of `start`:\n\n```java\nimport javafx.application.Application;\nimport javafx.scene.Scene;\nimport javafx.scene.control.Button;\nimport javafx.scene.control.Label;\nimport javafx.scene.layout.VBox;\nimport javafx.stage.Stage;\n\npublic class Main extends Application {\n\n    @Override\n    public void start(Stage stage) {\n        Label done = new Label(\"Pomodoros today: 0\");\n        Button finish = new Button(\"Finished one\");\n        int count = 0;\n        finish.setOnAction(event -> {\n            count++;\n            done.setText(\"Pomodoros today: \" + count);\n        });\n\n        stage.setScene(new Scene(new VBox(done, finish)));\n        stage.setTitle(\"Pomodoros\");\n        stage.show();\n    }\n\n    public static void main(String[] args) {\n        launch(Main.class);\n    }\n}\n```\n\n```javac\nMain.java:16: error: local variables referenced from a lambda expression must be final or effectively final\n            count++;\n            ^\nMain.java:17: error: local variables referenced from a lambda expression must be final or effectively final\n            done.setText(\"Pomodoros today: \" + count);\n                                               ^\n2 errors\n```\n\njavac complains twice: once where the lambda changes `count`, and once where it reads it. Module 37 met this message: a lambda may use the local variables of its method (and its parameters) only when they are **effectively final**, which means never changed after they get their value. `count++` changes `count`.\n\nHere's why Java has the rule. `start` runs once and returns before anyone clicks, and its local variables are gone after that. So a lambda doesn't keep the variable itself: it keeps a copy of its value, made when the lambda was made. If the variable could change later, the copy and the variable would disagree. A variable that never changes has a copy that is always right.\n\nThe lambda uses `done` too, and javac doesn't mind: `done` never changes, since it always refers to the same Label. The label's text changes, but that's a change inside the Label object, not a change of the variable.\n\nA count that changes needs a place that outlives `start`. There are three usual ones.\n\n### An instance variable\n\nAn instance variable belongs to the Main object, which JavaFX made before it called `start`, and which lives as long as the program does. The rule about effectively final variables is only for local variables and parameters, so a lambda can change an instance variable:\n\n```java\nimport javafx.application.Application;\nimport javafx.scene.Scene;\nimport javafx.scene.control.Button;\nimport javafx.scene.control.Label;\nimport javafx.scene.layout.HBox;\nimport javafx.scene.layout.VBox;\nimport javafx.stage.Stage;\n\npublic class Main extends Application {\n    private int pomodoros;\n\n    @Override\n    public void start(Stage stage) {\n        Label done = new Label(\"Pomodoros today: 0\");\n        Button finish = new Button(\"Finished one\");\n        Button reset = new Button(\"Reset\");\n        finish.setOnAction(event -> {\n            this.pomodoros++;\n            done.setText(\"Pomodoros today: \" + this.pomodoros);\n        });\n        reset.setOnAction(event -> {\n            this.pomodoros = 0;\n            done.setText(\"Pomodoros today: 0\");\n        });\n\n        stage.setScene(new Scene(new VBox(done, new HBox(finish, reset))));\n        stage.setTitle(\"Pomodoros\");\n        stage.show();\n    }\n\n    public static void main(String[] args) {\n        launch(Main.class);\n    }\n}\n```\n\n```window\n{\"events\":[\"click Button \\\"Finished one\\\"\",\"click Button \\\"Finished one\\\"\",\"click Button \\\"Finished one\\\"\"],\"window\":{\"windows\":[{\"title\":\"Pomodoros\",\"scene\":{\"root\":{\"n\":1,\"type\":\"VBox\",\"children\":[{\"n\":2,\"type\":\"Label\",\"text\":\"Pomodoros today: 3\"},{\"n\":3,\"type\":\"HBox\",\"children\":[{\"n\":4,\"type\":\"Button\",\"text\":\"Finished one\",\"onAction\":true},{\"n\":5,\"type\":\"Button\",\"text\":\"Reset\",\"onAction\":true}]}]}}}],\"problems\":[]},\"outline\":\"Window \\\"Pomodoros\\\"\\n  VBox\\n    Label \\\"Pomodoros today: 3\\\"\\n    HBox\\n      Button \\\"Finished one\\\"\\n      Button \\\"Reset\\\"\"}\n```\n\nThe lambdas are written inside `start`, so `this` in them is the object whose `start` is running: the Main object. `this.pomodoros` is the same variable at every click, and it keeps its value between them.\n\n### An object that counts\n\nAn object can hold the count instead. `PomodoroLog` is a small class of its own, and `start` makes one:\n\n```java\nimport javafx.application.Application;\nimport javafx.scene.Scene;\nimport javafx.scene.control.Button;\nimport javafx.scene.control.Label;\nimport javafx.scene.layout.HBox;\nimport javafx.scene.layout.VBox;\nimport javafx.stage.Stage;\n\npublic class Main extends Application {\n\n    @Override\n    public void start(Stage stage) {\n        PomodoroLog log = new PomodoroLog();\n        Label done = new Label(\"Pomodoros today: 0\");\n        Button finish = new Button(\"Finished one\");\n        Button reset = new Button(\"Reset\");\n        finish.setOnAction(event -> {\n            log.finishOne();\n            done.setText(\"Pomodoros today: \" + log.count());\n        });\n        reset.setOnAction(event -> {\n            log.reset();\n            done.setText(\"Pomodoros today: \" + log.count());\n        });\n\n        stage.setScene(new Scene(new VBox(done, new HBox(finish, reset))));\n        stage.setTitle(\"Pomodoros\");\n        stage.show();\n    }\n\n    public static void main(String[] args) {\n        launch(Main.class);\n    }\n}\n// ==== PomodoroLog.java ====\npublic class PomodoroLog {\n    private int count;\n\n    public void finishOne() {\n        this.count++;\n    }\n\n    public void reset() {\n        this.count = 0;\n    }\n\n    public int count() {\n        return this.count;\n    }\n}\n```\n\n```window\n{\"events\":[\"click Button \\\"Finished one\\\"\",\"click Button \\\"Finished one\\\"\",\"click Button \\\"Reset\\\"\",\"click Button \\\"Finished one\\\"\"],\"window\":{\"windows\":[{\"title\":\"Pomodoros\",\"scene\":{\"root\":{\"n\":1,\"type\":\"VBox\",\"children\":[{\"n\":2,\"type\":\"Label\",\"text\":\"Pomodoros today: 1\"},{\"n\":3,\"type\":\"HBox\",\"children\":[{\"n\":4,\"type\":\"Button\",\"text\":\"Finished one\",\"onAction\":true},{\"n\":5,\"type\":\"Button\",\"text\":\"Reset\",\"onAction\":true}]}]}}}],\"problems\":[]},\"outline\":\"Window \\\"Pomodoros\\\"\\n  VBox\\n    Label \\\"Pomodoros today: 1\\\"\\n    HBox\\n      Button \\\"Finished one\\\"\\n      Button \\\"Reset\\\"\"}\n```\n\n`log` is a local variable, but an effectively final one: it refers to the same PomodoroLog object all the time. What changes is the object's instance variable, as with the label's text. Step 4 builds on this: a class of its own that does the work, and handlers that only call it.\n\n### The window itself\n\nWhen a label shows nothing but the number, the handler can read the number back from the label:\n\n```java\nimport javafx.application.Application;\nimport javafx.scene.Scene;\nimport javafx.scene.control.Button;\nimport javafx.scene.control.Label;\nimport javafx.scene.layout.HBox;\nimport javafx.stage.Stage;\n\npublic class Main extends Application {\n\n    @Override\n    public void start(Stage stage) {\n        Label visitors = new Label(\"0\");\n        Button plus = new Button(\"+1\");\n        plus.setOnAction(event -> {\n            int now = Integer.valueOf(visitors.getText());\n            visitors.setText(\"\" + (now + 1));\n        });\n\n        stage.setScene(new Scene(new HBox(10, plus, visitors)));\n        stage.setTitle(\"Door counter\");\n        stage.show();\n    }\n\n    public static void main(String[] args) {\n        launch(Main.class);\n    }\n}\n```\n\n```window\n{\"events\":[\"click Button \\\"+1\\\"\",\"click Button \\\"+1\\\"\",\"click Button \\\"+1\\\"\",\"click Button \\\"+1\\\"\"],\"window\":{\"windows\":[{\"title\":\"Door counter\",\"scene\":{\"root\":{\"n\":1,\"type\":\"HBox\",\"spacing\":10,\"children\":[{\"n\":2,\"type\":\"Button\",\"text\":\"+1\",\"onAction\":true},{\"n\":3,\"type\":\"Label\",\"text\":\"4\"}]}}}],\"problems\":[]},\"outline\":\"Window \\\"Door counter\\\"\\n  HBox spacing 10\\n    Button \\\"+1\\\"\\n    Label \\\"4\\\"\"}\n```\n\nThat's fine for a small window like this one. When the label says more than the number, or several handlers share the count, an instance variable or an object is clearer.\n\n### A value read too early\n\nA lambda's copy of a variable has the value the variable had when the lambda was made. That matters when the value comes from the window. A hotel's wake-up call service:\n\n```java\nimport javafx.application.Application;\nimport javafx.scene.Scene;\nimport javafx.scene.control.Button;\nimport javafx.scene.control.Label;\nimport javafx.scene.control.TextField;\nimport javafx.scene.layout.VBox;\nimport javafx.stage.Stage;\n\npublic class Main extends Application {\n\n    @Override\n    public void start(Stage stage) {\n        TextField room = new TextField();\n        room.setPromptText(\"Room number\");\n        Button book = new Button(\"Wake me at 7\");\n        Label booked = new Label(\"\");\n        String number = room.getText();\n        book.setOnAction(event -> booked.setText(\"Wake-up call at 7 for room \" + number));\n\n        stage.setScene(new Scene(new VBox(booked, room, book)));\n        stage.setTitle(\"Wake-up calls\");\n        stage.show();\n    }\n\n    public static void main(String[] args) {\n        launch(Main.class);\n    }\n}\n```\n\n```window\n{\"events\":[\"type TextField 1 \\\"214\\\"\",\"click Button \\\"Wake me at 7\\\"\"],\"window\":{\"windows\":[{\"title\":\"Wake-up calls\",\"scene\":{\"root\":{\"n\":1,\"type\":\"VBox\",\"children\":[{\"n\":2,\"type\":\"Label\",\"text\":\"Wake-up call at 7 for room \"},{\"n\":3,\"type\":\"TextField\",\"text\":\"214\",\"promptText\":\"Room number\"},{\"n\":4,\"type\":\"Button\",\"text\":\"Wake me at 7\",\"onAction\":true}]}}}],\"problems\":[]},\"outline\":\"Window \\\"Wake-up calls\\\"\\n  VBox\\n    Label \\\"Wake-up call at 7 for room \\\"\\n    TextField \\\"214\\\", prompt \\\"Room number\\\"\\n    Button \\\"Wake me at 7\\\"\"}\n```\n\nThe room number is missing. `number` got its value once, when `start` ran, and the field was empty then. To use what the user typed, read the field inside the handler: `booked.setText(\"Wake-up call at 7 for room \" + room.getText())`.\n","task":"A water tracker doesn't compile: it keeps the number of glasses in a local variable, which its lambdas change. Keep the count in a `private` instance variable of Main instead, so that `Drink one` adds a glass and `New day` starts again from 0. Don't change the window or the texts. The task card shows the window after two clicks on Drink one.\n","kind":"code","seed":"import javafx.application.Application;\nimport javafx.scene.Scene;\nimport javafx.scene.control.Button;\nimport javafx.scene.control.Label;\nimport javafx.scene.layout.HBox;\nimport javafx.scene.layout.VBox;\nimport javafx.stage.Stage;\n\npublic class Main extends Application {\n\n    @Override\n    public void start(Stage stage) {\n        Label glasses = new Label(\"Glasses of water: 0\");\n        Button drink = new Button(\"Drink one\");\n        Button newDay = new Button(\"New day\");\n        int count = 0;\n        drink.setOnAction(event -> {\n            count++;\n            glasses.setText(\"Glasses of water: \" + count);\n        });\n        newDay.setOnAction(event -> {\n            count = 0;\n            glasses.setText(\"Glasses of water: \" + count);\n        });\n\n        stage.setScene(new Scene(new VBox(glasses, new HBox(drink, newDay))));\n        stage.setTitle(\"Water tracker\");\n        stage.show();\n    }\n\n    public static void main(String[] args) {\n        launch(Main.class);\n    }\n}\n","solution":"import javafx.application.Application;\nimport javafx.scene.Scene;\nimport javafx.scene.control.Button;\nimport javafx.scene.control.Label;\nimport javafx.scene.layout.HBox;\nimport javafx.scene.layout.VBox;\nimport javafx.stage.Stage;\n\npublic class Main extends Application {\n    private int count;\n\n    @Override\n    public void start(Stage stage) {\n        Label glasses = new Label(\"Glasses of water: 0\");\n        Button drink = new Button(\"Drink one\");\n        Button newDay = new Button(\"New day\");\n        drink.setOnAction(event -> {\n            this.count++;\n            glasses.setText(\"Glasses of water: \" + this.count);\n        });\n        newDay.setOnAction(event -> {\n            this.count = 0;\n            glasses.setText(\"Glasses of water: \" + this.count);\n        });\n\n        stage.setScene(new Scene(new VBox(glasses, new HBox(drink, newDay))));\n        stage.setTitle(\"Water tracker\");\n        stage.show();\n    }\n\n    public static void main(String[] args) {\n        launch(Main.class);\n    }\n}\n","hints":["A lambda can change an instance variable of Main but not a local variable of `start`, so move the count out of `start`.","Declare the count in the class, above `start`: `private int count;`. Then remove the line `int count = 0;` from `start`. In the lambdas, `this.count` is the instance variable: `this.count++;` and `this.count = 0;`."],"tests":[{"name":"Test 1","stdin":"","expect":"","hidden":false,"events":["click Button \"Drink one\"","click Button \"Drink one\""],"window":{"windows":[{"title":"Water tracker","scene":{"root":{"n":1,"type":"VBox","children":[{"n":2,"type":"Label","text":"Glasses of water: 2"},{"n":3,"type":"HBox","children":[{"n":4,"type":"Button","text":"Drink one","onAction":true},{"n":5,"type":"Button","text":"New day","onAction":true}]}]}}}],"problems":[]},"outline":"Window \"Water tracker\"\n  VBox\n    Label \"Glasses of water: 2\"\n    HBox\n      Button \"Drink one\"\n      Button \"New day\""},{"name":"Test 2","stdin":"","expect":"","hidden":true,"events":["click Button \"Drink one\"","click Button \"Drink one\"","click Button \"Drink one\"","click Button \"New day\"","click Button \"Drink one\""],"window":{"windows":[{"title":"Water tracker","scene":{"root":{"n":1,"type":"VBox","children":[{"n":2,"type":"Label","text":"Glasses of water: 1"},{"n":3,"type":"HBox","children":[{"n":4,"type":"Button","text":"Drink one","onAction":true},{"n":5,"type":"Button","text":"New day","onAction":true}]}]}}}],"problems":[]},"outline":"Window \"Water tracker\"\n  VBox\n    Label \"Glasses of water: 1\"\n    HBox\n      Button \"Drink one\"\n      Button \"New day\""},{"name":"Test 3","stdin":"","expect":"","hidden":true,"events":["click Button \"New day\"","click Button \"Drink one\"","click Button \"Drink one\"","click Button \"Drink one\"","click Button \"Drink one\"","click Button \"Drink one\""],"window":{"windows":[{"title":"Water tracker","scene":{"root":{"n":1,"type":"VBox","children":[{"n":2,"type":"Label","text":"Glasses of water: 5"},{"n":3,"type":"HBox","children":[{"n":4,"type":"Button","text":"Drink one","onAction":true},{"n":5,"type":"Button","text":"New day","onAction":true}]}]}}}],"problems":[]},"outline":"Window \"Water tracker\"\n  VBox\n    Label \"Glasses of water: 5\"\n    HBox\n      Button \"Drink one\"\n      Button \"New day\""}],"require":[{"pattern":"\\bprivate\\s+int\\s+\\w+\\s*(=\\s*0\\s*)?;","message":"Keep the count in a private instance variable of Main, such as `private int count;`."}],"forbid":[],"more":[{"task":"A delivery note gets the name of whoever signs for a parcel. Watch which value each part of the handler uses, and when the label changes. What does the program print?\n","kind":"predict","seed":"import javafx.application.Application;\nimport javafx.scene.Scene;\nimport javafx.scene.control.Button;\nimport javafx.scene.control.Label;\nimport javafx.scene.control.TextField;\nimport javafx.scene.layout.VBox;\nimport javafx.stage.Stage;\n\npublic class Main extends Application {\n\n    @Override\n    public void start(Stage stage) {\n        TextField name = new TextField(\"Ada\");\n        Label note = new Label(\"\");\n        Button sign = new Button(\"Sign\");\n        String early = name.getText();\n        sign.setOnAction(event -> note.setText(early + \" / \" + name.getText()));\n        name.setText(\"Grace\");\n        sign.fire();\n        System.out.println(note.getText());\n        name.appendText(\" Hopper\");\n        System.out.println(note.getText());\n        sign.fire();\n        System.out.println(note.getText());\n\n        stage.setScene(new Scene(new VBox(name, sign, note)));\n        stage.setTitle(\"Delivery note\");\n        stage.show();\n    }\n\n    public static void main(String[] args) {\n        launch(Main.class);\n    }\n}\n","solution":"import javafx.application.Application;\nimport javafx.scene.Scene;\nimport javafx.scene.control.Button;\nimport javafx.scene.control.Label;\nimport javafx.scene.control.TextField;\nimport javafx.scene.layout.VBox;\nimport javafx.stage.Stage;\n\npublic class Main extends Application {\n\n    @Override\n    public void start(Stage stage) {\n        TextField name = new TextField(\"Ada\");\n        Label note = new Label(\"\");\n        Button sign = new Button(\"Sign\");\n        String early = name.getText();\n        sign.setOnAction(event -> note.setText(early + \" / \" + name.getText()));\n        name.setText(\"Grace\");\n        sign.fire();\n        System.out.println(note.getText());\n        name.appendText(\" Hopper\");\n        System.out.println(note.getText());\n        sign.fire();\n        System.out.println(note.getText());\n\n        stage.setScene(new Scene(new VBox(name, sign, note)));\n        stage.setTitle(\"Delivery note\");\n        stage.show();\n    }\n\n    public static void main(String[] args) {\n        launch(Main.class);\n    }\n}\n","hints":["`early` got its value once, before the handler was even set: `Ada`. `name.getText()` in the handler reads the field each time the handler runs.","The label changes only when the handler runs. Changing the field after a `fire()` doesn't change the label until the next `fire()`."],"tests":[{"name":"Output","stdin":"","expect":"Ada / Grace\nAda / Grace\nAda / Grace Hopper","hidden":false}],"require":[],"forbid":[],"lines":["Ada / Grace","Ada / Grace","Ada / Grace Hopper"]},{"task":"A bike's handlebar display shows its gear. The window is built already: the label `Gear 1`, and under it the buttons `Down` and `Up`. Give the buttons their handlers. **Up** shifts one gear up and **Down** one gear down, and the label shows the gear, like `Gear 4`. The bike has gears 1 to 7: Up does nothing in gear 7, and Down does nothing in gear 1 (keep both buttons enabled). The task card shows the window after three clicks on Up.\n","kind":"code","seed":"import javafx.application.Application;\nimport javafx.scene.Scene;\nimport javafx.scene.control.Button;\nimport javafx.scene.control.Label;\nimport javafx.scene.layout.HBox;\nimport javafx.scene.layout.VBox;\nimport javafx.stage.Stage;\n\npublic class Main extends Application {\n\n    @Override\n    public void start(Stage stage) {\n        Label gear = new Label(\"Gear 1\");\n        Button down = new Button(\"Down\");\n        Button up = new Button(\"Up\");\n\n        stage.setScene(new Scene(new VBox(gear, new HBox(down, up))));\n        stage.setTitle(\"Gears\");\n        stage.show();\n    }\n\n    public static void main(String[] args) {\n        launch(Main.class);\n    }\n}\n","solution":"import javafx.application.Application;\nimport javafx.scene.Scene;\nimport javafx.scene.control.Button;\nimport javafx.scene.control.Label;\nimport javafx.scene.layout.HBox;\nimport javafx.scene.layout.VBox;\nimport javafx.stage.Stage;\n\npublic class Main extends Application {\n    private int current = 1;\n\n    @Override\n    public void start(Stage stage) {\n        Label gear = new Label(\"Gear 1\");\n        Button down = new Button(\"Down\");\n        Button up = new Button(\"Up\");\n        up.setOnAction(event -> {\n            if (this.current < 7) {\n                this.current++;\n            }\n            gear.setText(\"Gear \" + this.current);\n        });\n        down.setOnAction(event -> {\n            if (this.current > 1) {\n                this.current--;\n            }\n            gear.setText(\"Gear \" + this.current);\n        });\n\n        stage.setScene(new Scene(new VBox(gear, new HBox(down, up))));\n        stage.setTitle(\"Gears\");\n        stage.show();\n    }\n\n    public static void main(String[] args) {\n        launch(Main.class);\n    }\n}\n","hints":["The gear changes at every click, so keep it in an instance variable that starts at 1: `private int current = 1;`.","In Up's handler, add one only while the gear is below 7: `if (this.current < 7) { this.current++; }`, then set the label to `\"Gear \" + this.current`. Down is the same the other way around."],"tests":[{"name":"Test 1","stdin":"","expect":"","hidden":false,"events":["click Button \"Up\"","click Button \"Up\"","click Button \"Up\""],"window":{"windows":[{"title":"Gears","scene":{"root":{"n":1,"type":"VBox","children":[{"n":2,"type":"Label","text":"Gear 4"},{"n":3,"type":"HBox","children":[{"n":4,"type":"Button","text":"Down","onAction":true},{"n":5,"type":"Button","text":"Up","onAction":true}]}]}}}],"problems":[]},"outline":"Window \"Gears\"\n  VBox\n    Label \"Gear 4\"\n    HBox\n      Button \"Down\"\n      Button \"Up\""},{"name":"Test 2","stdin":"","expect":"","hidden":true,"events":["click Button \"Up\"","click Button \"Up\"","click Button \"Up\"","click Button \"Up\"","click Button \"Up\"","click Button \"Up\"","click Button \"Up\"","click Button \"Up\"","click Button \"Up\""],"window":{"windows":[{"title":"Gears","scene":{"root":{"n":1,"type":"VBox","children":[{"n":2,"type":"Label","text":"Gear 7"},{"n":3,"type":"HBox","children":[{"n":4,"type":"Button","text":"Down","onAction":true},{"n":5,"type":"Button","text":"Up","onAction":true}]}]}}}],"problems":[]},"outline":"Window \"Gears\"\n  VBox\n    Label \"Gear 7\"\n    HBox\n      Button \"Down\"\n      Button \"Up\""},{"name":"Test 3","stdin":"","expect":"","hidden":true,"events":["click Button \"Down\"","click Button \"Up\"","click Button \"Up\"","click Button \"Down\"","click Button \"Down\"","click Button \"Down\"","click Button \"Up\""],"window":{"windows":[{"title":"Gears","scene":{"root":{"n":1,"type":"VBox","children":[{"n":2,"type":"Label","text":"Gear 2"},{"n":3,"type":"HBox","children":[{"n":4,"type":"Button","text":"Down","onAction":true},{"n":5,"type":"Button","text":"Up","onAction":true}]}]}}}],"problems":[]},"outline":"Window \"Gears\"\n  VBox\n    Label \"Gear 2\"\n    HBox\n      Button \"Down\"\n      Button \"Up\""},{"name":"Test 4","stdin":"","expect":"","hidden":true,"events":["click Button \"Up\"","click Button \"Up\"","click Button \"Up\"","click Button \"Up\"","click Button \"Up\"","click Button \"Up\"","click Button \"Up\"","click Button \"Down\"","click Button \"Up\"","click Button \"Up\""],"window":{"windows":[{"title":"Gears","scene":{"root":{"n":1,"type":"VBox","children":[{"n":2,"type":"Label","text":"Gear 7"},{"n":3,"type":"HBox","children":[{"n":4,"type":"Button","text":"Down","onAction":true},{"n":5,"type":"Button","text":"Up","onAction":true}]}]}}}],"problems":[]},"outline":"Window \"Gears\"\n  VBox\n    Label \"Gear 7\"\n    HBox\n      Button \"Down\"\n      Button \"Up\""}],"require":[],"forbid":[]}]}