{"id":"objects-with-lists-instance-variable","slug":"list-as-instance-variable","title":"A list as an instance variable","text":"A class can keep a list in an instance variable, just as it keeps a String or an int. Here a recipe has a name and a list of ingredients:\n\n```java\npublic class Main {\n    public static void main(String[] args) {\n        Recipe pancakes = new Recipe(\"Pancakes\");\n        pancakes.addIngredient(\"flour\");\n        pancakes.addIngredient(\"milk\");\n        pancakes.addIngredient(\"eggs\");\n\n        Recipe toast = new Recipe(\"Toast\");\n        toast.addIngredient(\"bread\");\n        toast.addIngredient(\"butter\");\n\n        System.out.println(pancakes.getName() + \" needs \" + pancakes.ingredientCount() + \" ingredients\");\n        System.out.println(toast.getName() + \" needs \" + toast.ingredientCount() + \" ingredients\");\n    }\n}\n// ==== Recipe.java ====\nimport java.util.ArrayList;\n\npublic class Recipe {\n    private String name;\n    private ArrayList<String> ingredients;\n\n    public Recipe(String name) {\n        this.name = name;\n        this.ingredients = new ArrayList<>();\n    }\n\n    public void addIngredient(String ingredient) {\n        this.ingredients.add(ingredient);\n    }\n\n    public int ingredientCount() {\n        return this.ingredients.size();\n    }\n\n    public String getName() {\n        return this.name;\n    }\n}\n```\n\n```output\nPancakes needs 3 ingredients\nToast needs 2 ingredients\n```\n\n- `import java.util.ArrayList;` is at the top of `Recipe.java`, because Recipe uses ArrayList. Each file imports what it uses. `Main.java` needs no import here: `main` never writes `ArrayList`.\n- `private ArrayList<String> ingredients;` declares the instance variable. Like the others, it's private, so code outside Recipe can't reach the list.\n- The constructor makes the list: `this.ingredients = new ArrayList<>();`. The constructor runs once for each new Recipe, so each recipe gets a new, empty list of its own. The pancakes' three ingredients never mix with the toast's two.\n- The methods use the list through `this.ingredients`, like any other instance variable. `addIngredient` passes the ingredient on to the list's `add`, and `ingredientCount` returns the list's `size()`.\n\n`main` never touches the list itself: it asks the recipe. So the recipe's methods decide what can be done with its ingredients. This Recipe can add and count them, and nothing else. There's no way to remove an ingredient, because Recipe has no method for that.\n\n### Forgetting to make the list\n\nDeclaring the instance variable doesn't make a list. `private ArrayList<String> ingredients;` only says what the variable can refer to, and an instance variable of a class type is `null` until something gives it an object (module 22). If the constructor leaves out `new ArrayList<>()`, the first `add` crashes:\n\n```java\npublic class Main {\n    public static void main(String[] args) {\n        Recipe salad = new Recipe(\"Salad\");\n        salad.addIngredient(\"lettuce\");\n        System.out.println(salad.ingredientCount());\n    }\n}\n// ==== Recipe.java ====\nimport java.util.ArrayList;\n\npublic class Recipe {\n    private String name;\n    private ArrayList<String> ingredients;\n\n    public Recipe(String name) {\n        this.name = name;\n    }\n\n    public void addIngredient(String ingredient) {\n        this.ingredients.add(ingredient);\n    }\n\n    public int ingredientCount() {\n        return this.ingredients.size();\n    }\n}\n```\n\n```crash\nException in thread \"main\" java.lang.NullPointerException: Cannot invoke \"java.util.ArrayList.add(Object)\" because \"this.ingredients\" is null\n\tat Recipe.addIngredient(Recipe.java:12)\n\tat Main.main(Main.java:4)\n```\n\nThe message names the instance variable that was null, `this.ingredients`. The first `at` line says where it happened: in `addIngredient`, line 12 of `Recipe.java`. The line below says who called it: `main`, on line 4. The fix is in neither place, though. It's the missing line in the constructor, where the recipe should have gotten its list.\n","task":"A photo album has a title and a list of photo names. In `PhotoAlbum.java`, add a private instance variable `photos`, a list of Strings, and make a new, empty list for it in the constructor. Then write two methods: `add(String photo)` adds the photo to the list, and `count()` returns how many photos the album holds. The check runs:\n\n```java\nPhotoAlbum trip = new PhotoAlbum(\"Summer trip\");\ntrip.add(\"beach.jpg\");\ntrip.add(\"tent.jpg\");\nPhotoAlbum winter = new PhotoAlbum(\"Winter\");\nSystem.out.println(trip.getTitle() + \": \" + trip.count());\nSystem.out.println(winter.getTitle() + \": \" + winter.count());\n```\n\nand expects:\n\n```\nSummer trip: 2\nWinter: 0\n```\n","kind":"code","seed":"// ==== PhotoAlbum.java ====\nimport java.util.ArrayList;\n\npublic class PhotoAlbum {\n    private String title;\n\n    public PhotoAlbum(String title) {\n        this.title = title;\n    }\n\n    public String getTitle() {\n        return this.title;\n    }\n\n    // Write the methods add and count here.\n}\n\n// ==== Main.java ====\npublic class Main {\n    public static void main(String[] args) {\n        PhotoAlbum trip = new PhotoAlbum(\"Summer trip\");\n        trip.add(\"beach.jpg\");\n        trip.add(\"tent.jpg\");\n        PhotoAlbum winter = new PhotoAlbum(\"Winter\");\n        System.out.println(trip.getTitle() + \": \" + trip.count());\n        System.out.println(winter.getTitle() + \": \" + winter.count());\n    }\n}\n","solution":"// ==== PhotoAlbum.java ====\nimport java.util.ArrayList;\n\npublic class PhotoAlbum {\n    private String title;\n    private ArrayList<String> photos;\n\n    public PhotoAlbum(String title) {\n        this.title = title;\n        this.photos = new ArrayList<>();\n    }\n\n    public String getTitle() {\n        return this.title;\n    }\n\n    public void add(String photo) {\n        this.photos.add(photo);\n    }\n\n    public int count() {\n        return this.photos.size();\n    }\n}\n\n// ==== Main.java ====\npublic class Main {\n    public static void main(String[] args) {\n        PhotoAlbum trip = new PhotoAlbum(\"Summer trip\");\n        trip.add(\"beach.jpg\");\n        trip.add(\"tent.jpg\");\n        PhotoAlbum winter = new PhotoAlbum(\"Winter\");\n        System.out.println(trip.getTitle() + \": \" + trip.count());\n        System.out.println(winter.getTitle() + \": \" + winter.count());\n    }\n}\n","hints":["Declare the list next to `title`: `private ArrayList<String> photos;`. In the constructor, give it a new list: `this.photos = new ArrayList<>();`.","`add` passes the photo on to the list, `this.photos.add(photo);`, and `count` returns `this.photos.size();`."],"tests":[{"name":"Test 1","stdin":"","call":"PhotoAlbum trip = new PhotoAlbum(\"Summer trip\");\ntrip.add(\"beach.jpg\");\ntrip.add(\"tent.jpg\");\nPhotoAlbum winter = new PhotoAlbum(\"Winter\");\nSystem.out.println(trip.getTitle() + \": \" + trip.count());\nSystem.out.println(winter.getTitle() + \": \" + winter.count());\n","expect":"Summer trip: 2\nWinter: 0","hidden":false},{"name":"Test 2","stdin":"","call":"PhotoAlbum a = new PhotoAlbum(\"Cats\");\nPhotoAlbum b = new PhotoAlbum(\"Dogs\");\na.add(\"tom.png\");\nb.add(\"rex.png\");\na.add(\"tom.png\");\na.add(\"kit.png\");\nSystem.out.println(a.count() + \" \" + b.count());\nb.add(\"fido.png\");\nSystem.out.println(a.count() + \" \" + b.count());\n","expect":"3 1\n3 2","hidden":true},{"name":"Test 3","stdin":"","call":"PhotoAlbum empty = new PhotoAlbum(\"Spring\");\nSystem.out.println(empty.count());\nfor (int i = 0; i < 7; i++) {\n    empty.add(\"photo\" + i + \".jpg\");\n}\nSystem.out.println(empty.getTitle() + \" \" + empty.count());\n","expect":"0\nSpring 7","hidden":true}],"require":[{"pattern":"\\bprivate\\s+ArrayList<String>\\s+photos\\s*;","message":"Keep the photos in a private instance variable: `private ArrayList<String> photos;`."},{"pattern":"\\bnew\\s+ArrayList\\s*<","message":"Make the list in the constructor with `new ArrayList<>()`."}],"forbid":[],"more":[{"task":"Now a whole class of your own. `Mailbox.java` is empty: write the class `Mailbox` in it, from the first line. A mailbox has an owner and a list of letters, which are Strings.\n\n- The constructor `Mailbox(String owner)` keeps the owner and makes a new, empty list for the letters.\n- `deliver(String letter)` adds a letter to the list.\n- `count()` returns how many letters there are.\n- `toString()` returns the owner, a colon and the list, like `Ana: [bill, magazine]`. Joining the list to a String writes it out in brackets, just as printing it does.\n\nThe check runs:\n\n```java\nMailbox ana = new Mailbox(\"Ana\");\nMailbox leo = new Mailbox(\"Leo\");\nana.deliver(\"bill\");\nleo.deliver(\"postcard\");\nana.deliver(\"magazine\");\nSystem.out.println(ana.count() + \" \" + leo.count());\nSystem.out.println(ana);\nSystem.out.println(leo);\n```\n\nand expects:\n\n```\n2 1\nAna: [bill, magazine]\nLeo: [postcard]\n```\n","kind":"code","seed":"// ==== Mailbox.java ====\n// Write the Mailbox class here.\n\n// ==== Main.java ====\npublic class Main {\n    public static void main(String[] args) {\n        Mailbox ana = new Mailbox(\"Ana\");\n        Mailbox leo = new Mailbox(\"Leo\");\n        ana.deliver(\"bill\");\n        leo.deliver(\"postcard\");\n        ana.deliver(\"magazine\");\n        System.out.println(ana.count() + \" \" + leo.count());\n        System.out.println(ana);\n        System.out.println(leo);\n    }\n}\n","solution":"// ==== Mailbox.java ====\nimport java.util.ArrayList;\n\npublic class Mailbox {\n    private String owner;\n    private ArrayList<String> letters;\n\n    public Mailbox(String owner) {\n        this.owner = owner;\n        this.letters = new ArrayList<>();\n    }\n\n    public void deliver(String letter) {\n        this.letters.add(letter);\n    }\n\n    public int count() {\n        return this.letters.size();\n    }\n\n    public String toString() {\n        return this.owner + \": \" + this.letters;\n    }\n}\n\n// ==== Main.java ====\npublic class Main {\n    public static void main(String[] args) {\n        Mailbox ana = new Mailbox(\"Ana\");\n        Mailbox leo = new Mailbox(\"Leo\");\n        ana.deliver(\"bill\");\n        leo.deliver(\"postcard\");\n        ana.deliver(\"magazine\");\n        System.out.println(ana.count() + \" \" + leo.count());\n        System.out.println(ana);\n        System.out.println(leo);\n    }\n}\n","hints":["Start with `import java.util.ArrayList;` and `public class Mailbox {`, then two private instance variables: a String for the owner and an `ArrayList<String>` for the letters. The recipe above has the same shape.","The constructor sets `this.owner = owner;` and `this.letters = new ArrayList<>();`. `deliver` calls `this.letters.add(letter);`, `count` returns `this.letters.size();`, and toString returns `this.owner + \": \" + this.letters;`."],"tests":[{"name":"Test 1","stdin":"","call":"Mailbox ana = new Mailbox(\"Ana\");\nMailbox leo = new Mailbox(\"Leo\");\nana.deliver(\"bill\");\nleo.deliver(\"postcard\");\nana.deliver(\"magazine\");\nSystem.out.println(ana.count() + \" \" + leo.count());\nSystem.out.println(ana);\nSystem.out.println(leo);\n","expect":"2 1\nAna: [bill, magazine]\nLeo: [postcard]","hidden":false},{"name":"Test 2","stdin":"","call":"Mailbox kim = new Mailbox(\"Kim\");\nSystem.out.println(kim.count());\nSystem.out.println(kim);\nkim.deliver(\"ad\");\nkim.deliver(\"ad\");\nSystem.out.println(kim + \" \" + kim.count());\n","expect":"0\nKim: []\nKim: [ad, ad] 2","hidden":true},{"name":"Test 3","stdin":"","call":"Mailbox a = new Mailbox(\"A\");\nMailbox b = new Mailbox(\"B\");\nMailbox same = a;\nsame.deliver(\"x\");\nb.deliver(\"y\");\nb.deliver(\"z\");\nSystem.out.println(a + \" / \" + b);\n","expect":"A: [x] / B: [y, z]","hidden":true}],"require":[{"pattern":"\\bprivate\\s+ArrayList<String>\\s+\\w+\\s*;","message":"Keep the letters in a private instance variable, a list of Strings: `private ArrayList<String> letters;`."},{"pattern":"\\bnew\\s+ArrayList\\s*<","message":"Make the list in the constructor with `new ArrayList<>()`."}],"forbid":[]},{"task":"This program crashes with a NullPointerException. Read the message, find the cause and fix it in `Choir.java`. Then add a method `isEmpty()` to Choir: it returns `true` when nobody has joined the choir yet. `Main.java` is ready, and when both are done it prints:\n\n```\ntrue\nfalse\n2\n```\n","kind":"code","seed":"// ==== Choir.java ====\nimport java.util.ArrayList;\n\npublic class Choir {\n    private String name;\n    private ArrayList<String> singers;\n\n    public Choir(String name) {\n        this.name = name;\n    }\n\n    public void join(String singer) {\n        this.singers.add(singer);\n    }\n\n    public int size() {\n        return this.singers.size();\n    }\n\n    // Write the isEmpty method here.\n}\n\n// ==== Main.java ====\npublic class Main {\n    public static void main(String[] args) {\n        Choir choir = new Choir(\"Harbor Voices\");\n        System.out.println(choir.isEmpty());\n        choir.join(\"Mira\");\n        choir.join(\"Tom\");\n        System.out.println(choir.isEmpty());\n        System.out.println(choir.size());\n    }\n}\n","solution":"// ==== Choir.java ====\nimport java.util.ArrayList;\n\npublic class Choir {\n    private String name;\n    private ArrayList<String> singers;\n\n    public Choir(String name) {\n        this.name = name;\n        this.singers = new ArrayList<>();\n    }\n\n    public void join(String singer) {\n        this.singers.add(singer);\n    }\n\n    public int size() {\n        return this.singers.size();\n    }\n\n    public boolean isEmpty() {\n        return this.singers.isEmpty();\n    }\n}\n\n// ==== Main.java ====\npublic class Main {\n    public static void main(String[] args) {\n        Choir choir = new Choir(\"Harbor Voices\");\n        System.out.println(choir.isEmpty());\n        choir.join(\"Mira\");\n        choir.join(\"Tom\");\n        System.out.println(choir.isEmpty());\n        System.out.println(choir.size());\n    }\n}\n","hints":["The message says `this.singers` is null: the constructor never makes the list. Add `this.singers = new ArrayList<>();` to it.","`isEmpty` can ask the list: `public boolean isEmpty() { return this.singers.isEmpty(); }`. `this.singers.size() == 0` works too."],"tests":[{"name":"Test 1","stdin":"","call":"Choir choir = new Choir(\"Harbor Voices\");\nSystem.out.println(choir.isEmpty());\nchoir.join(\"Mira\");\nchoir.join(\"Tom\");\nSystem.out.println(choir.isEmpty());\nSystem.out.println(choir.size());\n","expect":"true\nfalse\n2","hidden":false},{"name":"Test 2","stdin":"","call":"Choir first = new Choir(\"North\");\nChoir second = new Choir(\"South\");\nfirst.join(\"Ann\");\nSystem.out.println(first.isEmpty() + \" \" + second.isEmpty());\nSystem.out.println(first.size() + \" \" + second.size());\n","expect":"false true\n1 0","hidden":true},{"name":"Test 3","stdin":"","call":"Choir choir = new Choir(\"Echo\");\nSystem.out.println(choir.size());\nchoir.join(\"Kai\");\nchoir.join(\"Kai\");\nchoir.join(\"Lin\");\nSystem.out.println(choir.size() + \" \" + choir.isEmpty());\n","expect":"0\n3 false","hidden":true}],"require":[{"pattern":"\\bsingers\\s*=\\s*new\\s+ArrayList\\s*<","message":"Make the list in the constructor: `this.singers = new ArrayList<>();`."},{"pattern":"\\bpublic\\s+boolean\\s+isEmpty\\s*\\(\\s*\\)","message":"Write the method as `public boolean isEmpty()`."}],"forbid":[]}]}