{"id":"polymorphism-end-of-part-9","slug":"end-of-part-9","title":"Choosing a design, and the end of part 9","text":"This last step of part 9 puts the pieces together: first how to choose between them when you design classes of your own, then a look back at the whole part.\n\n### extends, abstract, or an interface?\n\nModule 34 asked \"is a, or has a?\", and module 35 compared interfaces with abstract classes. With whole hierarchies in view, the choices look like this, with this module's examples:\n\n| When | Use | Example |\n| --- | --- | --- |\n| Every X is a Y, and should get Y's code and data | `class X extends Y` | A Taxi is a Car, and gets its plate and its toll rule |\n| Shared code, but no objects of the general kind | an abstract class | Employee keeps the name and the `toString`, and leaves `monthlyPay()` to each kind |\n| Classes share an ability, but no code, or aren't otherwise related | an interface | Taxis and trucks are Commercial, and plain cars aren't |\n| One class needs several roles | one superclass, and any number of interfaces | `Bat extends Enemy implements Flying` |\n\nFor the types of variables, parameters and lists, pick the most general type that has everything the code needs. Payroll takes any Employee, a company's bill needs only Commercial, and step 2's spear only needs to know whether an enemy is Flying. Code written in that way works with more classes, including ones that don't exist yet, like the interns of step 3.\n\n### The end of part 9\n\nThis step ends the material of the MOOC's ninth part. Module 34 started from the class Object, which every class extends, and built classes on other classes with `extends`: `super(...)` for the superclass's constructor, overriding with `@Override` and `super.method()`, `protected`, \"is a or has a\", and abstract classes. Module 35 brought interfaces, lists of methods that a class promises with `implements`, used as the types of variables, parameters, return values and lists, and Java's own List, Map, Set and Collection. This module put them together. An object has every type of its hierarchy, `instanceof` asks which, and a cast gives a reference of the more specific type. A program built on a general type works with every kind of object, even kinds added later. Part 10 comes next, starting with streams and lambdas. Part 9 ends with [the MOOC's summary page](https://java-programming.mooc.fi/part-9/4-summary) on its own site.\n","task":"The game gets hot-air balloons that drift across the sky. A balloon flies, but it isn't an enemy: it has no health, and nobody fights it. So `Balloon` implements Flying and has no `extends`: its only superclass is Object. `Flying.java`, `Enemy.java` and `Bat.java` are step 2's. Write:\n\n- in `Balloon.java`, the class `Balloon`, with a constructor `Balloon(String color, int altitude)`, the method `altitude()` from Flying, and a `toString` that returns the color and the word `balloon`, like `red balloon`\n- in `Main.java`, the method `lowest(ArrayList<Flying> flyers)`, which returns the flyer with the lowest altitude, the first of them when several are equally low, or `null` for an empty list\n\nThe check runs the following. A bat prints as every Enemy does, with its name and its health:\n\n```java\nArrayList<Flying> sky = new ArrayList<>();\nsky.add(new Bat(\"Squeak\", 9));\nsky.add(new Balloon(\"red\", 40));\nsky.add(new Balloon(\"green\", 6));\nsky.add(new Bat(\"Flit\", 12));\nSystem.out.println(sky);\nSystem.out.println(lowest(sky));\n```\n\nand expects:\n\n```\n[Squeak (8), red balloon, green balloon, Flit (8)]\ngreen balloon\n```\n","kind":"code","seed":"// ==== Balloon.java ====\npublic class Balloon {\n\n}\n\n// ==== Main.java ====\nimport java.util.ArrayList;\n\npublic class Main {\n    public static void main(String[] args) {\n        ArrayList<Flying> sky = new ArrayList<>();\n        sky.add(new Bat(\"Squeak\", 9));\n        sky.add(new Balloon(\"red\", 40));\n        sky.add(new Balloon(\"green\", 6));\n        sky.add(new Bat(\"Flit\", 12));\n        System.out.println(sky);\n        System.out.println(lowest(sky));\n    }\n\n    // Write the method lowest here.\n}\n\n// ==== Flying.java ====\npublic interface Flying {\n    int altitude();\n}\n\n// ==== Enemy.java ====\npublic abstract class Enemy {\n    private String name;\n    private int health;\n\n    public Enemy(String name, int health) {\n        this.name = name;\n        this.health = health;\n    }\n\n    public String getName() {\n        return this.name;\n    }\n\n    public int getHealth() {\n        return this.health;\n    }\n\n    public void heal(int amount) {\n        this.health += amount;\n    }\n\n    @Override\n    public String toString() {\n        return this.name + \" (\" + this.health + \")\";\n    }\n}\n\n// ==== Bat.java ====\npublic class Bat extends Enemy implements Flying {\n    private int altitude;\n\n    public Bat(String name, int altitude) {\n        super(name, 8);\n        this.altitude = altitude;\n    }\n\n    @Override\n    public int altitude() {\n        return this.altitude;\n    }\n}\n","solution":"// ==== Balloon.java ====\npublic class Balloon implements Flying {\n    private String color;\n    private int altitude;\n\n    public Balloon(String color, int altitude) {\n        this.color = color;\n        this.altitude = altitude;\n    }\n\n    @Override\n    public int altitude() {\n        return this.altitude;\n    }\n\n    @Override\n    public String toString() {\n        return this.color + \" balloon\";\n    }\n}\n\n// ==== Main.java ====\nimport java.util.ArrayList;\n\npublic class Main {\n    public static void main(String[] args) {\n        ArrayList<Flying> sky = new ArrayList<>();\n        sky.add(new Bat(\"Squeak\", 9));\n        sky.add(new Balloon(\"red\", 40));\n        sky.add(new Balloon(\"green\", 6));\n        sky.add(new Bat(\"Flit\", 12));\n        System.out.println(sky);\n        System.out.println(lowest(sky));\n    }\n\n    public static Flying lowest(ArrayList<Flying> flyers) {\n        Flying lowest = null;\n        for (Flying flyer : flyers) {\n            if (lowest == null || flyer.altitude() < lowest.altitude()) {\n                lowest = flyer;\n            }\n        }\n        return lowest;\n    }\n}\n\n// ==== Flying.java ====\npublic interface Flying {\n    int altitude();\n}\n\n// ==== Enemy.java ====\npublic abstract class Enemy {\n    private String name;\n    private int health;\n\n    public Enemy(String name, int health) {\n        this.name = name;\n        this.health = health;\n    }\n\n    public String getName() {\n        return this.name;\n    }\n\n    public int getHealth() {\n        return this.health;\n    }\n\n    public void heal(int amount) {\n        this.health += amount;\n    }\n\n    @Override\n    public String toString() {\n        return this.name + \" (\" + this.health + \")\";\n    }\n}\n\n// ==== Bat.java ====\npublic class Bat extends Enemy implements Flying {\n    private int altitude;\n\n    public Bat(String name, int altitude) {\n        super(name, 8);\n        this.altitude = altitude;\n    }\n\n    @Override\n    public int altitude() {\n        return this.altitude;\n    }\n}\n","hints":["`public class Balloon implements Flying {` with a private color and altitude, a constructor, `altitude()` with `@Override`, and `toString` returning `this.color + \" balloon\"`.","In `lowest`, keep the lowest flyer so far in a `Flying` variable that starts as `null`. Replace it when it's still `null`, or when a flyer's `altitude()` is smaller, with `<` so that the first of equally low ones stays."],"tests":[{"name":"Test 1","stdin":"","call":"ArrayList<Flying> sky = new ArrayList<>();\nsky.add(new Bat(\"Squeak\", 9));\nsky.add(new Balloon(\"red\", 40));\nsky.add(new Balloon(\"green\", 6));\nsky.add(new Bat(\"Flit\", 12));\nSystem.out.println(sky);\nSystem.out.println(lowest(sky));\n","expect":"[Squeak (8), red balloon, green balloon, Flit (8)]\ngreen balloon","hidden":false},{"name":"Test 2","stdin":"","call":"ArrayList<Flying> sky = new ArrayList<>();\nSystem.out.println(lowest(sky));\nsky.add(new Balloon(\"blue\", 15));\nSystem.out.println(lowest(sky));\nsky.add(new Bat(\"Dusk\", 15));\nsky.add(new Bat(\"Low\", 2));\nsky.add(new Balloon(\"white\", 2));\nFlying found = lowest(sky);\nSystem.out.println(found + \" \" + found.altitude() + \" \" + (found instanceof Enemy));\n","expect":"null\nblue balloon\nLow (8) 2 true","hidden":true},{"name":"Test 3","stdin":"","call":"Balloon yellow = new Balloon(\"yellow\", 300);\nFlying flyer = yellow;\nObject thing = yellow;\nSystem.out.println(flyer.altitude() + \" \" + thing);\nSystem.out.println((thing instanceof Enemy) + \" \" + (thing instanceof Flying));\nArrayList<Flying> sky = new ArrayList<>();\nsky.add(new Bat(\"Top\", 500));\nsky.add(yellow);\nSystem.out.println(lowest(sky));\n","expect":"300 yellow balloon\nfalse true\nyellow balloon","hidden":true}],"require":[{"pattern":"\\bclass\\s+Balloon\\s+implements\\s+Flying\\b","message":"Write the header as `public class Balloon implements Flying`."},{"pattern":"\\bpublic\\s+static\\s+Flying\\s+lowest\\s*\\(\\s*ArrayList\\s*<\\s*Flying\\s*>\\s+\\w+\\s*\\)","message":"Write the method as `public static Flying lowest(ArrayList<Flying> flyers)`."}],"forbid":[{"pattern":"\\bclass\\s+Balloon\\s+extends\\b","message":"A balloon isn't an enemy, or any other kind of class here: Balloon implements Flying, and needs no `extends`."}],"more":[{"task":"The toll booth from step 1, with step 1's TankerTruck, which pays 10 more than a Truck. What does this program print?\n","kind":"predict","seed":"import java.util.ArrayList;\n\npublic class Main {\n    public static void main(String[] args) {\n        ArrayList<Vehicle> passed = new ArrayList<>();\n        passed.add(new Vehicle(\"MC-40\"));\n        passed.add(new TankerTruck(\"TK-4\", \"Coastal Fuels\", 2));\n        passed.add(new Truck(\"TR-88\", \"Coastal Fuels\", 1));\n        passed.add(new Truck(\"TR-21\", \"Birch Haulage\", 3));\n\n        int trucks = 0;\n        int coastal = 0;\n        for (Vehicle vehicle : passed) {\n            if (vehicle instanceof Truck) {\n                trucks++;\n            }\n            if (vehicle instanceof Commercial) {\n                Commercial customer = (Commercial) vehicle;\n                if (customer.company().equals(\"Coastal Fuels\")) {\n                    coastal += vehicle.toll();\n                }\n            }\n        }\n        System.out.println(trucks + \" \" + coastal);\n        System.out.println(passed.get(1));\n        Object last = passed.get(3);\n        System.out.println(last);\n    }\n}\n// ==== Vehicle.java ====\npublic class Vehicle {\n    private String plate;\n\n    public Vehicle(String plate) {\n        this.plate = plate;\n    }\n\n    public int toll() {\n        return 3;\n    }\n\n    @Override\n    public String toString() {\n        return this.plate + \" pays \" + this.toll();\n    }\n}\n// ==== Truck.java ====\npublic class Truck extends Vehicle implements Commercial {\n    private String company;\n    private int axles;\n\n    public Truck(String plate, String company, int axles) {\n        super(plate);\n        this.company = company;\n        this.axles = axles;\n    }\n\n    @Override\n    public int toll() {\n        return 5 * this.axles;\n    }\n\n    @Override\n    public String company() {\n        return this.company;\n    }\n}\n// ==== TankerTruck.java ====\npublic class TankerTruck extends Truck {\n    public TankerTruck(String plate, String company, int axles) {\n        super(plate, company, axles);\n    }\n\n    @Override\n    public int toll() {\n        return super.toll() + 10;\n    }\n}\n// ==== Commercial.java ====\npublic interface Commercial {\n    String company();\n}\n","solution":"import java.util.ArrayList;\n\npublic class Main {\n    public static void main(String[] args) {\n        ArrayList<Vehicle> passed = new ArrayList<>();\n        passed.add(new Vehicle(\"MC-40\"));\n        passed.add(new TankerTruck(\"TK-4\", \"Coastal Fuels\", 2));\n        passed.add(new Truck(\"TR-88\", \"Coastal Fuels\", 1));\n        passed.add(new Truck(\"TR-21\", \"Birch Haulage\", 3));\n\n        int trucks = 0;\n        int coastal = 0;\n        for (Vehicle vehicle : passed) {\n            if (vehicle instanceof Truck) {\n                trucks++;\n            }\n            if (vehicle instanceof Commercial) {\n                Commercial customer = (Commercial) vehicle;\n                if (customer.company().equals(\"Coastal Fuels\")) {\n                    coastal += vehicle.toll();\n                }\n            }\n        }\n        System.out.println(trucks + \" \" + coastal);\n        System.out.println(passed.get(1));\n        Object last = passed.get(3);\n        System.out.println(last);\n    }\n}\n// ==== Vehicle.java ====\npublic class Vehicle {\n    private String plate;\n\n    public Vehicle(String plate) {\n        this.plate = plate;\n    }\n\n    public int toll() {\n        return 3;\n    }\n\n    @Override\n    public String toString() {\n        return this.plate + \" pays \" + this.toll();\n    }\n}\n// ==== Truck.java ====\npublic class Truck extends Vehicle implements Commercial {\n    private String company;\n    private int axles;\n\n    public Truck(String plate, String company, int axles) {\n        super(plate);\n        this.company = company;\n        this.axles = axles;\n    }\n\n    @Override\n    public int toll() {\n        return 5 * this.axles;\n    }\n\n    @Override\n    public String company() {\n        return this.company;\n    }\n}\n// ==== TankerTruck.java ====\npublic class TankerTruck extends Truck {\n    public TankerTruck(String plate, String company, int axles) {\n        super(plate, company, axles);\n    }\n\n    @Override\n    public int toll() {\n        return super.toll() + 10;\n    }\n}\n// ==== Commercial.java ====\npublic interface Commercial {\n    String company();\n}\n","hints":["A TankerTruck is a Truck, so `instanceof Truck` counts it, and it's Commercial through Truck. The motorbike is neither.","Coastal Fuels has the tanker, with 2 axles, 10 + 10, and a truck with 1 axle. Printing an element runs Vehicle's `toString` with the object's own `toll()`, even through an Object variable."],"tests":[{"name":"Output","stdin":"","expect":"3 25\nTK-4 pays 20\nTR-21 pays 15","hidden":false}],"require":[],"forbid":[],"lines":["3 25","TK-4 pays 20","TR-21 pays 15"]},{"task":"To end part 9: the toll booth's records, which bring together a class hierarchy, an interface, `instanceof`, and a hash map from part 8. `Vehicle.java`, `Truck.java` and `Commercial.java` are step 1's. Write:\n\n- In `Van.java`, the class `Van`. A delivery van is a Vehicle, and it works for a company. Its constructor is `Van(String plate, String company)`, it pays a toll of 7, and it's Commercial.\n- In `TollBooth.java`:\n  - the constructor,\n  - `pass(Vehicle vehicle)`, which records a vehicle going through the booth,\n  - `total()`, which returns all the tolls paid so far, added up,\n  - `billFor(String company)`, which returns the tolls of that company's vehicles added up, or 0 for a company with none,\n  - `companies()`, which returns a new list of the companies that have a bill, each once, in alphabetical order.\n\nA `HashMap<String, Integer>` from a company to its bill is one good way to keep the bills. `Main.java` is a test drive, and it prints:\n\n```\n49\n21\n0\n[Birch Haulage, Nordic Freight, Swift Parcels]\n```\n","kind":"code","seed":"// ==== TollBooth.java ====\nimport java.util.ArrayList;\nimport java.util.Collections;\nimport java.util.HashMap;\n\npublic class TollBooth {\n\n    public TollBooth() {\n\n    }\n\n    public void pass(Vehicle vehicle) {\n\n    }\n\n    public int total() {\n        return 0;\n    }\n\n    public int billFor(String company) {\n        return 0;\n    }\n\n    public ArrayList<String> companies() {\n        return null;\n    }\n}\n\n// ==== Van.java ====\npublic class Van {\n\n}\n\n// ==== Vehicle.java ====\npublic class Vehicle {\n    private String plate;\n\n    public Vehicle(String plate) {\n        this.plate = plate;\n    }\n\n    public String getPlate() {\n        return this.plate;\n    }\n\n    public int toll() {\n        return 3;\n    }\n\n    @Override\n    public String toString() {\n        return this.plate + \" pays \" + this.toll();\n    }\n}\n\n// ==== Truck.java ====\npublic class Truck extends Vehicle implements Commercial {\n    private String company;\n    private int axles;\n\n    public Truck(String plate, String company, int axles) {\n        super(plate);\n        this.company = company;\n        this.axles = axles;\n    }\n\n    @Override\n    public int toll() {\n        return 5 * this.axles;\n    }\n\n    @Override\n    public String company() {\n        return this.company;\n    }\n}\n\n// ==== Commercial.java ====\npublic interface Commercial {\n    String company();\n}\n\n// ==== Main.java ====\npublic class Main {\n    public static void main(String[] args) {\n        TollBooth booth = new TollBooth();\n        booth.pass(new Van(\"VN-7\", \"Swift Parcels\"));\n        booth.pass(new Vehicle(\"MC-40\"));\n        booth.pass(new Truck(\"TR-88\", \"Nordic Freight\", 3));\n        booth.pass(new Van(\"VN-9\", \"Swift Parcels\"));\n        booth.pass(new Truck(\"TR-21\", \"Birch Haulage\", 2));\n        booth.pass(new Van(\"VN-7\", \"Swift Parcels\"));\n        System.out.println(booth.total());\n        System.out.println(booth.billFor(\"Swift Parcels\"));\n        System.out.println(booth.billFor(\"Metro Cabs\"));\n        System.out.println(booth.companies());\n    }\n}\n","solution":"// ==== TollBooth.java ====\nimport java.util.ArrayList;\nimport java.util.Collections;\nimport java.util.HashMap;\n\npublic class TollBooth {\n    private int total;\n    private HashMap<String, Integer> bills;\n\n    public TollBooth() {\n        this.total = 0;\n        this.bills = new HashMap<>();\n    }\n\n    public void pass(Vehicle vehicle) {\n        int toll = vehicle.toll();\n        this.total += toll;\n        if (vehicle instanceof Commercial) {\n            Commercial customer = (Commercial) vehicle;\n            String company = customer.company();\n            this.bills.put(company, this.bills.getOrDefault(company, 0) + toll);\n        }\n    }\n\n    public int total() {\n        return this.total;\n    }\n\n    public int billFor(String company) {\n        return this.bills.getOrDefault(company, 0);\n    }\n\n    public ArrayList<String> companies() {\n        ArrayList<String> companies = new ArrayList<>(this.bills.keySet());\n        Collections.sort(companies);\n        return companies;\n    }\n}\n\n// ==== Van.java ====\npublic class Van extends Vehicle implements Commercial {\n    private String company;\n\n    public Van(String plate, String company) {\n        super(plate);\n        this.company = company;\n    }\n\n    @Override\n    public int toll() {\n        return 7;\n    }\n\n    @Override\n    public String company() {\n        return this.company;\n    }\n}\n\n// ==== Vehicle.java ====\npublic class Vehicle {\n    private String plate;\n\n    public Vehicle(String plate) {\n        this.plate = plate;\n    }\n\n    public String getPlate() {\n        return this.plate;\n    }\n\n    public int toll() {\n        return 3;\n    }\n\n    @Override\n    public String toString() {\n        return this.plate + \" pays \" + this.toll();\n    }\n}\n\n// ==== Truck.java ====\npublic class Truck extends Vehicle implements Commercial {\n    private String company;\n    private int axles;\n\n    public Truck(String plate, String company, int axles) {\n        super(plate);\n        this.company = company;\n        this.axles = axles;\n    }\n\n    @Override\n    public int toll() {\n        return 5 * this.axles;\n    }\n\n    @Override\n    public String company() {\n        return this.company;\n    }\n}\n\n// ==== Commercial.java ====\npublic interface Commercial {\n    String company();\n}\n\n// ==== Main.java ====\npublic class Main {\n    public static void main(String[] args) {\n        TollBooth booth = new TollBooth();\n        booth.pass(new Van(\"VN-7\", \"Swift Parcels\"));\n        booth.pass(new Vehicle(\"MC-40\"));\n        booth.pass(new Truck(\"TR-88\", \"Nordic Freight\", 3));\n        booth.pass(new Van(\"VN-9\", \"Swift Parcels\"));\n        booth.pass(new Truck(\"TR-21\", \"Birch Haulage\", 2));\n        booth.pass(new Van(\"VN-7\", \"Swift Parcels\"));\n        System.out.println(booth.total());\n        System.out.println(booth.billFor(\"Swift Parcels\"));\n        System.out.println(booth.billFor(\"Metro Cabs\"));\n        System.out.println(booth.companies());\n    }\n}\n","hints":["Van is step 1's Truck without the axles: `public class Van extends Vehicle implements Commercial`, a constructor that hands the plate on with `super(plate)`, and `toll()` and `company()` with `@Override`.","In TollBooth, keep the total in an `int` and the bills in a `HashMap<String, Integer>`, both set in the constructor. `pass` adds the vehicle's `toll()` to the total and, when `vehicle instanceof Commercial`, casts it, and adds the toll to its company's bill with `getOrDefault(company, 0)`. `companies()` sorts a copy of the keys, as in module 33."],"tests":[{"name":"The test drive","stdin":"","expect":"49\n21\n0\n[Birch Haulage, Nordic Freight, Swift Parcels]","hidden":false},{"name":"Test 2","stdin":"","call":"TollBooth booth = new TollBooth();\nSystem.out.println(booth.total() + \" \" + booth.billFor(\"Swift Parcels\") + \" \" + booth.companies());\nbooth.pass(new Vehicle(\"MC-1\"));\nbooth.pass(new Vehicle(\"MC-2\"));\nSystem.out.println(booth.total() + \" \" + booth.companies());\nVan van = new Van(\"VN-1\", \"Zeta Couriers\");\nCommercial customer = van;\nbooth.pass(van);\nbooth.pass(new Truck(\"TR-5\", \"Alder Logistics\", 4));\nbooth.pass(van);\nSystem.out.println(booth.total() + \" \" + booth.billFor(customer.company()) + \" \" + booth.billFor(\"Alder Logistics\"));\nSystem.out.println(booth.companies());\n","expect":"0 0 []\n6 []\n40 14 20\n[Alder Logistics, Zeta Couriers]","hidden":true},{"name":"Test 3","stdin":"","call":"TollBooth booth = new TollBooth();\nfor (int i = 1; i <= 10; i++) {\n    booth.pass(new Truck(\"TR-\" + i, \"Fleet \" + (i % 3), i));\n}\nSystem.out.println(booth.total());\nSystem.out.println(booth.billFor(\"Fleet 0\") + \" \" + booth.billFor(\"fleet 0\"));\nSystem.out.println(booth.companies());\nVehicle van = new Van(\"VN-2\", \"Fleet 1\");\nbooth.pass(van);\nSystem.out.println(van.toll() + \" \" + booth.billFor(\"Fleet 1\") + \" \" + van);\n","expect":"275\n90 0\n[Fleet 0, Fleet 1, Fleet 2]\n7 117 VN-2 pays 7","hidden":true}],"require":[{"pattern":"\\bclass\\s+Van\\s+extends\\s+Vehicle\\s+implements\\s+Commercial\\b","message":"Write the header as `public class Van extends Vehicle implements Commercial`."},{"pattern":"\\binstanceof\\s+Commercial\\b","message":"In `pass`, check with `instanceof Commercial` whether the vehicle has a company to bill."}],"forbid":[{"pattern":"\\bclass\\s+TollBooth\\b[\\s\\S]*\\bSystem\\s*\\.\\s*out\\b[\\s\\S]*\\bclass\\s+Van\\b","message":"TollBooth doesn't print. Its methods return their answers, and `main` prints them."}]}]}