{"id":"interfaces-collection","slug":"collection-interface","title":"Collection, and choosing between an interface and an abstract class","text":"### Collection: what lists and sets share\n\nLists and sets have a lot in common: both hold values, and both have `add`, `remove`, `contains`, `size`, `isEmpty` and `clear`. Java puts what they share in one more interface, **Collection**. An interface can extend another interface, and List and Set both extend Collection. So an ArrayList is a List and a Collection, and a HashSet is a Set and a Collection.\n\nCollection has no `get(index)`, since a set's values have no positions. But a for-each loop can go through any Collection. So a method whose parameter is a Collection works with lists and sets alike. A smart home's energy meter shows it:\n\n```java\nimport java.util.ArrayList;\nimport java.util.Collection;\nimport java.util.HashMap;\nimport java.util.HashSet;\nimport java.util.List;\nimport java.util.Map;\nimport java.util.Set;\n\npublic class Main {\n    public static void main(String[] args) {\n        List<Integer> hourly = new ArrayList<>();\n        hourly.add(300);\n        hourly.add(450);\n        hourly.add(300);\n        System.out.println(total(hourly));\n\n        Set<Integer> different = new HashSet<>(hourly);\n        System.out.println(different.size() + \" different amounts, adding up to \" + total(different));\n\n        Map<String, Integer> watts = new HashMap<>();\n        watts.put(\"heater\", 1500);\n        watts.put(\"fan\", 60);\n        watts.put(\"garden light\", 12);\n        System.out.println(total(watts.values()) + \" watts with everything on\");\n    }\n\n    public static int total(Collection<Integer> numbers) {\n        int sum = 0;\n        for (int number : numbers) {\n            sum += number;\n        }\n        return sum;\n    }\n}\n```\n\n```output\n1050\n2 different amounts, adding up to 750\n1572 watts with everything on\n```\n\n- `total` takes a `Collection<Integer>`, so it accepts the list, the set, and `watts.values()`, which is a Collection too. With a `List<Integer>` parameter, it couldn't take the set, and with a `Set<Integer>` parameter, not the list.\n- The set keeps 300 only once, so its total is smaller. A sum doesn't depend on the order of the values, so going through a set is fine here.\n- A map's `keySet()` is a Set and its `values()` a Collection, so both fit a Collection parameter. A map itself isn't a Collection: it holds pairs, not single values.\n- Collection needs `import java.util.Collection;`. Mind the names: `Collection` is the interface, and `Collections`, with an s, is the class with `sort` and other helpers.\n\n### Interface or abstract class?\n\nModule 34's abstract classes and this module's interfaces both list methods that other classes must write, and neither can be made with `new`. Which should you use? The differences decide:\n\n| | Interface | Abstract class |\n| --- | --- | --- |\n| How many a class can have | any number: `implements A, B` | one: `extends A` |\n| Instance variables and constructors | none | yes |\n| Methods with code | none, in this course | yes, shared by every subclass |\n| Says of an object | what it can do | what it is |\n\n- Choose an **interface** when classes that are otherwise unrelated share an ability. A heater and a scooter can both be switched on; a product and a repair both have a price. Code that needs only that ability, such as `printLine` or the app's buttons, takes the interface, and works with every class that has it, including classes written later.\n- Choose an **abstract class** when the classes are kinds of one thing, and share data and code: every instrument in module 34 had a name, and `play` was written once, in Instrument.\n- The two combine: a class can extend an abstract class and implement interfaces. Java's own ArrayList does both: it extends an abstract class, `AbstractList`, which holds code that lists share, and it implements List.\n\nWhen in doubt, start from what the code that *uses* the objects needs. If it needs only a few methods, an interface says exactly that, and leaves each class free to extend whatever else it needs. Module 36 comes back to this choice.\n","task":"Write the method `countAtLeast(Collection<Integer> numbers, int limit)`, which returns how many of the numbers are at least `limit`. The check calls it with a list, a set and a map's values:\n\n```java\nList<Integer> hourly = new ArrayList<>();\nhourly.add(300);\nhourly.add(450);\nhourly.add(300);\nhourly.add(120);\nSystem.out.println(countAtLeast(hourly, 300));\nSystem.out.println(countAtLeast(new HashSet<>(hourly), 300));\nMap<String, Integer> watts = new HashMap<>();\nwatts.put(\"heater\", 1500);\nwatts.put(\"fan\", 60);\nwatts.put(\"kettle\", 2000);\nSystem.out.println(countAtLeast(watts.values(), 1000));\n```\n\nand expects:\n\n```\n3\n2\n2\n```\n","kind":"code","seed":"import java.util.ArrayList;\nimport java.util.Collection;\nimport java.util.HashMap;\nimport java.util.HashSet;\nimport java.util.List;\nimport java.util.Map;\n\npublic class Main {\n    public static void main(String[] args) {\n        List<Integer> hourly = new ArrayList<>();\n        hourly.add(300);\n        hourly.add(450);\n        hourly.add(300);\n        hourly.add(120);\n        System.out.println(countAtLeast(hourly, 300));\n        System.out.println(countAtLeast(new HashSet<>(hourly), 300));\n        Map<String, Integer> watts = new HashMap<>();\n        watts.put(\"heater\", 1500);\n        watts.put(\"fan\", 60);\n        watts.put(\"kettle\", 2000);\n        System.out.println(countAtLeast(watts.values(), 1000));\n    }\n\n    // Write the method countAtLeast here.\n}\n","solution":"import java.util.ArrayList;\nimport java.util.Collection;\nimport java.util.HashMap;\nimport java.util.HashSet;\nimport java.util.List;\nimport java.util.Map;\n\npublic class Main {\n    public static void main(String[] args) {\n        List<Integer> hourly = new ArrayList<>();\n        hourly.add(300);\n        hourly.add(450);\n        hourly.add(300);\n        hourly.add(120);\n        System.out.println(countAtLeast(hourly, 300));\n        System.out.println(countAtLeast(new HashSet<>(hourly), 300));\n        Map<String, Integer> watts = new HashMap<>();\n        watts.put(\"heater\", 1500);\n        watts.put(\"fan\", 60);\n        watts.put(\"kettle\", 2000);\n        System.out.println(countAtLeast(watts.values(), 1000));\n    }\n\n    public static int countAtLeast(Collection<Integer> numbers, int limit) {\n        int count = 0;\n        for (int number : numbers) {\n            if (number >= limit) {\n                count++;\n            }\n        }\n        return count;\n    }\n}\n","hints":["The header is `public static int countAtLeast(Collection<Integer> numbers, int limit)`. A Collection has no `get(index)`, so go through it with a for-each loop.","Count the numbers for which `number >= limit`, and return the count. The set holds 300 only once, so it gives one less than the list."],"tests":[{"name":"Test 1","stdin":"","call":"List<Integer> hourly = new ArrayList<>();\nhourly.add(300);\nhourly.add(450);\nhourly.add(300);\nhourly.add(120);\nSystem.out.println(countAtLeast(hourly, 300));\nSystem.out.println(countAtLeast(new HashSet<>(hourly), 300));\nMap<String, Integer> watts = new HashMap<>();\nwatts.put(\"heater\", 1500);\nwatts.put(\"fan\", 60);\nwatts.put(\"kettle\", 2000);\nSystem.out.println(countAtLeast(watts.values(), 1000));\n","expect":"3\n2\n2","hidden":false},{"name":"Test 2","stdin":"","call":"Set<Integer> none = new HashSet<>();\nSystem.out.println(countAtLeast(none, 0));\nList<Integer> numbers = new ArrayList<>();\nfor (int i = 1; i <= 10; i++) {\n    numbers.add(i * i);\n}\nSystem.out.println(countAtLeast(numbers, 50) + \" \" + countAtLeast(numbers, 1) + \" \" + countAtLeast(numbers, 101));\n","expect":"0\n3 10 0","hidden":true},{"name":"Test 3","stdin":"","call":"Map<Integer, Integer> squares = new HashMap<>();\nfor (int i = -3; i <= 3; i++) {\n    squares.put(i, i * i);\n}\nSystem.out.println(countAtLeast(squares.keySet(), 0) + \" \" + countAtLeast(squares.values(), 4) + \" \" + countAtLeast(new HashSet<>(squares.values()), 4));\n","expect":"4 4 2","hidden":true}],"require":[{"pattern":"\\bpublic\\s+static\\s+int\\s+countAtLeast\\s*\\(\\s*Collection<Integer>\\s+\\w+\\s*,\\s*int\\s+\\w+\\s*\\)","message":"Write the method as `public static int countAtLeast(Collection<Integer> numbers, int limit)`, so that it takes lists and sets alike."}],"forbid":[],"more":[{"task":"A party's guest list has a name twice. What does this program print?\n","kind":"predict","seed":"import java.util.ArrayList;\nimport java.util.Collection;\nimport java.util.HashSet;\nimport java.util.List;\nimport java.util.Set;\n\npublic class Main {\n    public static void main(String[] args) {\n        List<String> guests = new ArrayList<>();\n        guests.add(\"Ada\");\n        guests.add(\"Ben\");\n        guests.add(\"Ada\");\n        Set<String> names = new HashSet<>(guests);\n        System.out.println(describe(guests) + \" / \" + describe(names));\n        names.add(\"Cy\");\n        System.out.println(guests.size() + \" \" + names.size());\n    }\n\n    public static String describe(Collection<String> people) {\n        int letters = 0;\n        for (String person : people) {\n            letters += person.length();\n        }\n        return people.size() + \" people, \" + letters + \" letters\";\n    }\n}\n","solution":"import java.util.ArrayList;\nimport java.util.Collection;\nimport java.util.HashSet;\nimport java.util.List;\nimport java.util.Set;\n\npublic class Main {\n    public static void main(String[] args) {\n        List<String> guests = new ArrayList<>();\n        guests.add(\"Ada\");\n        guests.add(\"Ben\");\n        guests.add(\"Ada\");\n        Set<String> names = new HashSet<>(guests);\n        System.out.println(describe(guests) + \" / \" + describe(names));\n        names.add(\"Cy\");\n        System.out.println(guests.size() + \" \" + names.size());\n    }\n\n    public static String describe(Collection<String> people) {\n        int letters = 0;\n        for (String person : people) {\n            letters += person.length();\n        }\n        return people.size() + \" people, \" + letters + \" letters\";\n    }\n}\n","hints":["A HashSet made from the list keeps each name once, and `describe` counts whatever collection it's given.","The list keeps Ada twice: 3 names, 9 letters. The set has Ada and Ben, 6 letters. The set is a new object, made from the list's values, so adding Cy to the set doesn't change the list."],"tests":[{"name":"Output","stdin":"","expect":"3 people, 9 letters / 2 people, 6 letters\n3 3","hidden":false}],"require":[],"forbid":[],"lines":["3 people, 9 letters / 2 people, 6 letters","3 3"]},{"task":"The app's devices now share an abstract class, `Appliance`: every appliance has a name, and an abstract method `watts()` for the power it uses. Some appliances also run on a battery, and that's what the interface Chargeable is for. `Appliance.java`, `Chargeable.java` and `Toaster.java` are ready. In `CordlessVacuum.java`, write the class `CordlessVacuum`, which extends Appliance and implements Chargeable, with:\n\n- a constructor `CordlessVacuum(int battery)`; the appliance's name is always `Cordless vacuum`\n- `watts()`, which returns 150\n- `batteryPercent()`, which returns the battery's percentage\n- `charge(int minutes)`, which adds 3 percent for every minute, but never goes above 100\n\nThe check runs:\n\n```java\nCordlessVacuum vacuum = new CordlessVacuum(40);\nSystem.out.println(vacuum);\nvacuum.charge(10);\nAppliance appliance = vacuum;\nChargeable chargeable = vacuum;\nSystem.out.println(appliance.getName() + \": \" + chargeable.batteryPercent() + \"%\");\nSystem.out.println(new Toaster());\n```\n\nand expects:\n\n```\nCordless vacuum, 150 W\nCordless vacuum: 70%\nToaster, 800 W\n```\n","kind":"code","seed":"// ==== CordlessVacuum.java ====\npublic class CordlessVacuum {\n\n}\n\n// ==== Appliance.java ====\npublic abstract class Appliance {\n    private String name;\n\n    public Appliance(String name) {\n        this.name = name;\n    }\n\n    public String getName() {\n        return this.name;\n    }\n\n    public abstract int watts();\n\n    @Override\n    public String toString() {\n        return this.name + \", \" + this.watts() + \" W\";\n    }\n}\n\n// ==== Chargeable.java ====\npublic interface Chargeable {\n    int batteryPercent();\n    void charge(int minutes);\n}\n\n// ==== Toaster.java ====\npublic class Toaster extends Appliance {\n    public Toaster() {\n        super(\"Toaster\");\n    }\n\n    @Override\n    public int watts() {\n        return 800;\n    }\n}\n\n// ==== Main.java ====\npublic class Main {\n    public static void main(String[] args) {\n        CordlessVacuum vacuum = new CordlessVacuum(40);\n        System.out.println(vacuum);\n        vacuum.charge(10);\n        Appliance appliance = vacuum;\n        Chargeable chargeable = vacuum;\n        System.out.println(appliance.getName() + \": \" + chargeable.batteryPercent() + \"%\");\n        System.out.println(new Toaster());\n    }\n}\n","solution":"// ==== CordlessVacuum.java ====\npublic class CordlessVacuum extends Appliance implements Chargeable {\n    private int battery;\n\n    public CordlessVacuum(int battery) {\n        super(\"Cordless vacuum\");\n        this.battery = battery;\n    }\n\n    @Override\n    public int watts() {\n        return 150;\n    }\n\n    @Override\n    public int batteryPercent() {\n        return this.battery;\n    }\n\n    @Override\n    public void charge(int minutes) {\n        this.battery += 3 * minutes;\n        if (this.battery > 100) {\n            this.battery = 100;\n        }\n    }\n}\n\n// ==== Appliance.java ====\npublic abstract class Appliance {\n    private String name;\n\n    public Appliance(String name) {\n        this.name = name;\n    }\n\n    public String getName() {\n        return this.name;\n    }\n\n    public abstract int watts();\n\n    @Override\n    public String toString() {\n        return this.name + \", \" + this.watts() + \" W\";\n    }\n}\n\n// ==== Chargeable.java ====\npublic interface Chargeable {\n    int batteryPercent();\n    void charge(int minutes);\n}\n\n// ==== Toaster.java ====\npublic class Toaster extends Appliance {\n    public Toaster() {\n        super(\"Toaster\");\n    }\n\n    @Override\n    public int watts() {\n        return 800;\n    }\n}\n\n// ==== Main.java ====\npublic class Main {\n    public static void main(String[] args) {\n        CordlessVacuum vacuum = new CordlessVacuum(40);\n        System.out.println(vacuum);\n        vacuum.charge(10);\n        Appliance appliance = vacuum;\n        Chargeable chargeable = vacuum;\n        System.out.println(appliance.getName() + \": \" + chargeable.batteryPercent() + \"%\");\n        System.out.println(new Toaster());\n    }\n}\n","hints":["The header is `public class CordlessVacuum extends Appliance implements Chargeable {`: `extends` first. The constructor's first line hands the name to Appliance: `super(\"Cordless vacuum\");`.","The class keeps the battery in an instance variable of its own. It writes `watts()` for Appliance, and `batteryPercent()` and `charge(int minutes)` for Chargeable, each with `@Override`. Appliance's `toString` already prints the name and the watts."],"tests":[{"name":"Test 1","stdin":"","call":"CordlessVacuum vacuum = new CordlessVacuum(40);\nSystem.out.println(vacuum);\nvacuum.charge(10);\nAppliance appliance = vacuum;\nChargeable chargeable = vacuum;\nSystem.out.println(appliance.getName() + \": \" + chargeable.batteryPercent() + \"%\");\nSystem.out.println(new Toaster());\n","expect":"Cordless vacuum, 150 W\nCordless vacuum: 70%\nToaster, 800 W","hidden":false},{"name":"Test 2","stdin":"","call":"List<Appliance> kitchen = new ArrayList<>();\nkitchen.add(new Toaster());\nkitchen.add(new CordlessVacuum(0));\nint watts = 0;\nfor (Appliance appliance : kitchen) {\n    watts += appliance.watts();\n}\nSystem.out.println(watts + \" \" + kitchen.get(1).getName());\n","expect":"950 Cordless vacuum","hidden":true},{"name":"Test 3","stdin":"","call":"List<Chargeable> batteries = new ArrayList<>();\nbatteries.add(new CordlessVacuum(95));\nbatteries.add(new CordlessVacuum(10));\nfor (Chargeable battery : batteries) {\n    battery.charge(2);\n    System.out.println(battery.batteryPercent());\n}\n","expect":"100\n16","hidden":true}],"require":[{"pattern":"\\bclass\\s+CordlessVacuum\\s+extends\\s+Appliance\\s+implements\\s+Chargeable\\b","message":"Write the header as `public class CordlessVacuum extends Appliance implements Chargeable`."},{"pattern":"\\bsuper\\s*\\(","message":"Hand the name to Appliance's constructor with `super(\"Cordless vacuum\");`."}],"forbid":[]}]}