{"id":"packages-in-practice","slug":"in-practice","title":"Packages in practice","text":"This last step looks at packages outside this site: how real projects arrange their folders, how packages are named, and the messages javac gives when something about packages goes wrong.\n\n### The folders of a real project\n\nOn this site, a program's package folders start at the top, next to `Main.java`. A project on your own computer has a few more folders around them. Many Java projects are built with a build tool such as Maven or Gradle (a later module of this course sets up Maven), and both expect this layout:\n\n- `src/main/java/quiz/domain/Question.java`\n- `src/main/java/quiz/logic/Quiz.java`\n- `src/main/java/quiz/ui/TextUI.java`\n- `src/test/java/quiz/logic/QuizTest.java`\n\nWhat the layout says:\n\n- `src/main/java` holds the program's own classes, and the package folders start inside it. `Question.java` is still in `quiz/domain/`, and its package line is still `package quiz.domain;`.\n- `src/test/java` holds the tests, in a folder tree of its own. A test class is in the same package as the class it tests: QuizTest is in `quiz.logic` too, so it can test Quiz's package-private parts as well.\n- In a real program every class is in a package, `Main` too, often in the program's top package: `quiz.Main`. A class in a named package can't use a class of the default package at all, since no import can name it. The default package suits small programs, like the ones on this site, where nothing needs to use Main.\n- A programming editor, such as NetBeans, IntelliJ IDEA or VS Code, shows the packages as a tree, and creates the folders and the package line when you make a class in a package.\n\n### Naming packages\n\n- Package names are all small letters, one short word for each part: `quiz.logic`, not `Quiz.Logic` or `quiz.gameLogic`.\n- Many start with the internet domain of the organization that writes the code, turned around, so that the packages of two organizations never share a name. JUnit's classes, which module 26 imported, are in `org.junit`, and a company with the domain `example.com` would use packages like `com.example.quiz.logic`. Names that start with `java` belong to Java's own library.\n- A package is named for what its classes do together. A small program splits into `domain`, `logic` and `ui`, as in this module. Bigger programs often have a package for each feature instead, such as `quiz.players` and `quiz.questions`. Either way, each package has one clear job.\n\n### Messages you'll meet\n\n| javac says | It usually means |\n| --- | --- |\n| `package quiz.domian does not exist` | a typo in an import or a full name |\n| `cannot find symbol`, with `symbol: class Question` | an import is missing |\n| `Question is not public in quiz.domain; cannot be accessed from outside package` | the class needs `public` |\n| `getPoints() is not public in Question; cannot be accessed from outside package` | the method or constructor needs `public`, unless it's meant only for its own package |\n| `getAnswer() has protected access in Question` | only its package and its subclasses may use it (a subclass only through its own type) |\n| `file does not contain class quiz.domain.Question` | the file's package line doesn't match its folders |\n\nThe last one comes with more messages around it, such as `duplicate class` and `bad source file`. They all go away when the package line is right.\n\n### This module in short\n\n- A package line puts a class in a package, and the file's folders match it. A class's full name is its package and its own name. A class without a package line is in the default package.\n- An import lets a file use a class of another package by its simple name, and `*` imports a whole package. `java.lang` is imported into every file.\n- From another package, only public classes, constructors and methods can be used, and `protected` also lets in subclasses. What has no word in front in a class stays inside its package.\n- A program splits into packages by responsibility, such as `domain`, `logic` and `ui`, which use each other in one direction.\n","task":"A planetarium's program has its classes in two packages, `planetarium.domain` and `planetarium.logic`, but it has four mistakes about packages: a wrong package line, a missing import and two missing `public`s. Read javac's messages, fix one mistake at a time, and run again after each fix. Don't change what the program does. It should print:\n\n```\n100 minutes of shows\nLongest: Black Holes (40 minutes)\n```\n","kind":"code","seed":"// ==== Main.java ====\nimport planetarium.logic.Schedule;\n\npublic class Main {\n    public static void main(String[] args) {\n        Schedule saturday = new Schedule();\n        saturday.add(new Show(\"Journey to Mars\", 35));\n        saturday.add(new Show(\"The Night Sky\", 25));\n        saturday.add(new Show(\"Black Holes\", 40));\n        System.out.println(saturday.totalMinutes() + \" minutes of shows\");\n        System.out.println(\"Longest: \" + saturday.longest());\n    }\n}\n\n// ==== planetarium/logic/Schedule.java ====\npackage planetarium.logic;\n\nimport java.util.ArrayList;\nimport planetarium.domain.Show;\n\npublic class Schedule {\n    private ArrayList<Show> shows;\n\n    Schedule() {\n        this.shows = new ArrayList<>();\n    }\n\n    public void add(Show show) {\n        this.shows.add(show);\n    }\n\n    public int totalMinutes() {\n        int total = 0;\n        for (Show show : this.shows) {\n            total += show.getMinutes();\n        }\n        return total;\n    }\n\n    public Show longest() {\n        Show longest = null;\n        for (Show show : this.shows) {\n            if (longest == null || show.getMinutes() > longest.getMinutes()) {\n                longest = show;\n            }\n        }\n        return longest;\n    }\n}\n\n// ==== planetarium/domain/Show.java ====\npackage planetarium;\n\npublic class Show {\n    private String title;\n    private int minutes;\n\n    public Show(String title, int minutes) {\n        this.title = title;\n        this.minutes = minutes;\n    }\n\n    int getMinutes() {\n        return this.minutes;\n    }\n\n    @Override\n    public String toString() {\n        return this.title + \" (\" + this.minutes + \" minutes)\";\n    }\n}\n","solution":"// ==== Main.java ====\nimport planetarium.domain.Show;\nimport planetarium.logic.Schedule;\n\npublic class Main {\n    public static void main(String[] args) {\n        Schedule saturday = new Schedule();\n        saturday.add(new Show(\"Journey to Mars\", 35));\n        saturday.add(new Show(\"The Night Sky\", 25));\n        saturday.add(new Show(\"Black Holes\", 40));\n        System.out.println(saturday.totalMinutes() + \" minutes of shows\");\n        System.out.println(\"Longest: \" + saturday.longest());\n    }\n}\n\n// ==== planetarium/logic/Schedule.java ====\npackage planetarium.logic;\n\nimport java.util.ArrayList;\nimport planetarium.domain.Show;\n\npublic class Schedule {\n    private ArrayList<Show> shows;\n\n    public Schedule() {\n        this.shows = new ArrayList<>();\n    }\n\n    public void add(Show show) {\n        this.shows.add(show);\n    }\n\n    public int totalMinutes() {\n        int total = 0;\n        for (Show show : this.shows) {\n            total += show.getMinutes();\n        }\n        return total;\n    }\n\n    public Show longest() {\n        Show longest = null;\n        for (Show show : this.shows) {\n            if (longest == null || show.getMinutes() > longest.getMinutes()) {\n                longest = show;\n            }\n        }\n        return longest;\n    }\n}\n\n// ==== planetarium/domain/Show.java ====\npackage planetarium.domain;\n\npublic class Show {\n    private String title;\n    private int minutes;\n\n    public Show(String title, int minutes) {\n        this.title = title;\n        this.minutes = minutes;\n    }\n\n    public int getMinutes() {\n        return this.minutes;\n    }\n\n    @Override\n    public String toString() {\n        return this.title + \" (\" + this.minutes + \" minutes)\";\n    }\n}\n","hints":["Start with `Show.java`: it's in the folder `planetarium/domain/`, so its package line is `package planetarium.domain;`. Many of javac's messages go away with that fix.","Then: Main uses `Show` without importing it (`import planetarium.domain.Show;`), Main calls Schedule's constructor, which isn't public, and Schedule, in another package, calls Show's `getMinutes()`, which isn't public either."],"tests":[{"name":"The program's output","stdin":"","expect":"100 minutes of shows\nLongest: Black Holes (40 minutes)","hidden":false},{"name":"Test 2","stdin":"","call":"Schedule sunday = new Schedule();\nSystem.out.println(sunday.totalMinutes() + \" \" + sunday.longest());\nsunday.add(new Show(\"Comets\", 30));\nsunday.add(new Show(\"Aurora\", 30));\nSystem.out.println(sunday.totalMinutes() + \" \" + sunday.longest() + \" \" + new Show(\"Moon\", 20).getMinutes());\n","expect":"0 null\n60 Comets (30 minutes) 20","hidden":true}],"require":[{"pattern":"\\bpackage\\s+planetarium\\.domain\\s*;","message":"`Show.java` is in the folder `planetarium/domain/`, so its first line is `package planetarium.domain;`."}],"forbid":[],"more":[{"task":"In a real project, a class's tests are in the same package as the class, so they can test its package-private methods too. Here the test class is simply one more file in the package's folder.\n\n`DeskFee.java`, in the package `cowork.logic`, works out what a desk in a coworking space costs. `price(int minutes)` is public, and it uses the package-private helper `startedHours(int minutes)`, which counts every hour that has started: 1 to 60 minutes is 1 hour, 61 to 120 minutes are 2 hours, and so on. The price is the started hours times the price of an hour, but never more than the daily maximum.\n\n`DeskFeeTest.java` is in the same package and has one test. Add two more, with `assertEquals`:\n\n- a test of `startedHours`: 1 minute is 1 hour, 60 minutes are 1 hour, and 61 minutes are 2 hours\n- a test of the daily maximum: with 4 dollars an hour and a maximum of 25 dollars, 600 minutes cost 25 dollars\n\nThe checks run your tests on the DeskFee as it is, where every test should pass, and on versions of DeskFee with a bug, where at least one of your tests should fail.\n","kind":"code","seed":"// ==== cowork/logic/DeskFeeTest.java ====\npackage cowork.logic;\n\nimport static org.junit.Assert.assertEquals;\n\nimport org.junit.Test;\n\npublic class DeskFeeTest {\n    @Test\n    public void priceIsPerStartedHour() {\n        DeskFee fee = new DeskFee(4, 25);\n        assertEquals(8, fee.price(90));\n    }\n\n    // Write the tests of startedHours and of the daily maximum here.\n}\n\n// ==== cowork/logic/DeskFee.java ====\npackage cowork.logic;\n\npublic class DeskFee {\n    private int perHour;\n    private int dailyMax;\n\n    public DeskFee(int perHour, int dailyMax) {\n        this.perHour = perHour;\n        this.dailyMax = dailyMax;\n    }\n\n    public int price(int minutes) {\n        int price = this.startedHours(minutes) * this.perHour;\n        if (price > this.dailyMax) {\n            return this.dailyMax;\n        }\n        return price;\n    }\n\n    int startedHours(int minutes) {\n        return (minutes + 59) / 60;\n    }\n}\n","solution":"// ==== cowork/logic/DeskFeeTest.java ====\npackage cowork.logic;\n\nimport static org.junit.Assert.assertEquals;\n\nimport org.junit.Test;\n\npublic class DeskFeeTest {\n    @Test\n    public void priceIsPerStartedHour() {\n        DeskFee fee = new DeskFee(4, 25);\n        assertEquals(8, fee.price(90));\n    }\n\n    @Test\n    public void startedHoursCountsEveryStartedHour() {\n        DeskFee fee = new DeskFee(4, 25);\n        assertEquals(1, fee.startedHours(1));\n        assertEquals(1, fee.startedHours(60));\n        assertEquals(2, fee.startedHours(61));\n    }\n\n    @Test\n    public void priceStopsAtTheDailyMaximum() {\n        DeskFee fee = new DeskFee(4, 25);\n        assertEquals(25, fee.price(600));\n    }\n}\n\n// ==== cowork/logic/DeskFee.java ====\npackage cowork.logic;\n\npublic class DeskFee {\n    private int perHour;\n    private int dailyMax;\n\n    public DeskFee(int perHour, int dailyMax) {\n        this.perHour = perHour;\n        this.dailyMax = dailyMax;\n    }\n\n    public int price(int minutes) {\n        int price = this.startedHours(minutes) * this.perHour;\n        if (price > this.dailyMax) {\n            return this.dailyMax;\n        }\n        return price;\n    }\n\n    int startedHours(int minutes) {\n        return (minutes + 59) / 60;\n    }\n}\n","hints":["Copy the shape of `priceIsPerStartedHour`: `@Test`, then `public void` and a name that says what the test checks. DeskFeeTest is in `cowork.logic` too, so it may call `fee.startedHours(61)` although the method isn't public.","One test has three lines like `assertEquals(2, fee.startedHours(61));`, with the expected value first. The other makes `new DeskFee(4, 25)` and checks `assertEquals(25, fee.price(600));`."],"tests":[{"name":"The DeskFee as it is","stdin":"","junit":"cowork.logic.DeskFeeTest","outcome":"pass","expect":"","hidden":false},{"name":"A DeskFee whose startedHours misses an hour's first minute","stdin":"","junit":"cowork.logic.DeskFeeTest","outcome":"fail","replace":{"cowork/logic/DeskFee.java":"package cowork.logic;\n\npublic class DeskFee {\n    private int perHour;\n    private int dailyMax;\n\n    public DeskFee(int perHour, int dailyMax) {\n        this.perHour = perHour;\n        this.dailyMax = dailyMax;\n    }\n\n    public int price(int minutes) {\n        int price = this.startedHours(minutes) * this.perHour;\n        if (price > this.dailyMax) {\n            return this.dailyMax;\n        }\n        return price;\n    }\n\n    int startedHours(int minutes) {\n        return (minutes + 58) / 60;\n    }\n}\n"},"expect":"","hidden":false},{"name":"A DeskFee without a daily maximum","stdin":"","junit":"cowork.logic.DeskFeeTest","outcome":"fail","replace":{"cowork/logic/DeskFee.java":"package cowork.logic;\n\npublic class DeskFee {\n    private int perHour;\n    private int dailyMax;\n\n    public DeskFee(int perHour, int dailyMax) {\n        this.perHour = perHour;\n        this.dailyMax = dailyMax;\n    }\n\n    public int price(int minutes) {\n        return this.startedHours(minutes) * this.perHour;\n    }\n\n    int startedHours(int minutes) {\n        return (minutes + 59) / 60;\n    }\n}\n"},"expect":"","hidden":false},{"name":"A DeskFee whose startedHours counts one hour too many","stdin":"","junit":"cowork.logic.DeskFeeTest","outcome":"fail","replace":{"cowork/logic/DeskFee.java":"package cowork.logic;\n\npublic class DeskFee {\n    private int perHour;\n    private int dailyMax;\n\n    public DeskFee(int perHour, int dailyMax) {\n        this.perHour = perHour;\n        this.dailyMax = dailyMax;\n    }\n\n    public int price(int minutes) {\n        int price = this.startedHours(minutes) * this.perHour;\n        if (price > this.dailyMax) {\n            return this.dailyMax;\n        }\n        return price;\n    }\n\n    int startedHours(int minutes) {\n        return minutes / 60 + 1;\n    }\n}\n"},"expect":"","hidden":true}],"require":[{"pattern":"==== cowork/logic/DeskFeeTest\\.java ====(?:(?!// ====)[\\s\\S])*\\.startedHours\\s*\\(","raw":true,"message":"Test the package-private method itself: call `fee.startedHours(...)` in DeskFeeTest, which is in the same package."}],"forbid":[]},{"task":"To close the module, a whole program goes into packages. A kayak rental's program works, but it's all in `Main.java`: after Main come three more classes without `public`. (A file may hold several classes, but only one public one, named after the file.) Split it into packages:\n\n- `Kayak` goes into `kayak/domain/Kayak.java`, in the package `kayak.domain`\n- `Dock` goes into `kayak/logic/Dock.java`, in the package `kayak.logic`\n- `TextUI` goes into `kayak/ui/TextUI.java`, in the package `kayak.ui`\n\n`Main.java` then holds only Main. Each file needs its package line and the imports of what it uses, and what other packages use must be public. The program keeps printing exactly what it prints now. For the input shown:\n\n```\n> Take kayak 1\n> Take kayak 2\n> Free kayaks: 1\n> Kayak 1 is back\n> Kayak 1 isn't out\n> Take kayak 1\n> Take kayak 3\n> All kayaks are out\n> Free kayaks: 0\n> Bye!\n```\n","kind":"code","seed":"// ==== Main.java ====\nimport java.util.ArrayList;\nimport java.util.Scanner;\n\npublic class Main {\n    public static void main(String[] args) {\n        TextUI ui = new TextUI(new Scanner(System.in), new Dock(3));\n        ui.start();\n    }\n}\n\nclass Kayak {\n    private int number;\n    private boolean out;\n\n    Kayak(int number) {\n        this.number = number;\n        this.out = false;\n    }\n\n    int getNumber() {\n        return this.number;\n    }\n\n    boolean isOut() {\n        return this.out;\n    }\n\n    void setOut(boolean out) {\n        this.out = out;\n    }\n}\n\nclass Dock {\n    private ArrayList<Kayak> kayaks;\n\n    Dock(int count) {\n        this.kayaks = new ArrayList<>();\n        for (int i = 1; i <= count; i++) {\n            this.kayaks.add(new Kayak(i));\n        }\n    }\n\n    Kayak rent() {\n        for (Kayak kayak : this.kayaks) {\n            if (!kayak.isOut()) {\n                kayak.setOut(true);\n                return kayak;\n            }\n        }\n        return null;\n    }\n\n    boolean giveBack(int number) {\n        for (Kayak kayak : this.kayaks) {\n            if (kayak.getNumber() == number && kayak.isOut()) {\n                kayak.setOut(false);\n                return true;\n            }\n        }\n        return false;\n    }\n\n    int freeCount() {\n        int free = 0;\n        for (Kayak kayak : this.kayaks) {\n            if (!kayak.isOut()) {\n                free++;\n            }\n        }\n        return free;\n    }\n}\n\nclass TextUI {\n    private Scanner scanner;\n    private Dock dock;\n\n    TextUI(Scanner scanner, Dock dock) {\n        this.scanner = scanner;\n        this.dock = dock;\n    }\n\n    void start() {\n        while (true) {\n            System.out.print(\"> \");\n            String command = this.scanner.nextLine();\n            if (command.equals(\"quit\")) {\n                System.out.println(\"Bye!\");\n                break;\n            }\n            if (command.equals(\"rent\")) {\n                Kayak kayak = this.dock.rent();\n                if (kayak == null) {\n                    System.out.println(\"All kayaks are out\");\n                } else {\n                    System.out.println(\"Take kayak \" + kayak.getNumber());\n                }\n            } else if (command.equals(\"return\")) {\n                int number = Integer.valueOf(this.scanner.nextLine());\n                if (this.dock.giveBack(number)) {\n                    System.out.println(\"Kayak \" + number + \" is back\");\n                } else {\n                    System.out.println(\"Kayak \" + number + \" isn't out\");\n                }\n            } else if (command.equals(\"free\")) {\n                System.out.println(\"Free kayaks: \" + this.dock.freeCount());\n            } else {\n                System.out.println(\"Unknown command: \" + command);\n            }\n        }\n    }\n}\n\n// ==== kayak/domain/Kayak.java ====\n// Move the class Kayak here.\n\n// ==== kayak/logic/Dock.java ====\n// Move the class Dock here.\n\n// ==== kayak/ui/TextUI.java ====\n// Move the class TextUI here.\n","solution":"// ==== Main.java ====\nimport java.util.Scanner;\nimport kayak.logic.Dock;\nimport kayak.ui.TextUI;\n\npublic class Main {\n    public static void main(String[] args) {\n        TextUI ui = new TextUI(new Scanner(System.in), new Dock(3));\n        ui.start();\n    }\n}\n\n// ==== kayak/domain/Kayak.java ====\npackage kayak.domain;\n\npublic class Kayak {\n    private int number;\n    private boolean out;\n\n    public Kayak(int number) {\n        this.number = number;\n        this.out = false;\n    }\n\n    public int getNumber() {\n        return this.number;\n    }\n\n    public boolean isOut() {\n        return this.out;\n    }\n\n    public void setOut(boolean out) {\n        this.out = out;\n    }\n}\n\n// ==== kayak/logic/Dock.java ====\npackage kayak.logic;\n\nimport java.util.ArrayList;\nimport kayak.domain.Kayak;\n\npublic class Dock {\n    private ArrayList<Kayak> kayaks;\n\n    public Dock(int count) {\n        this.kayaks = new ArrayList<>();\n        for (int i = 1; i <= count; i++) {\n            this.kayaks.add(new Kayak(i));\n        }\n    }\n\n    public Kayak rent() {\n        for (Kayak kayak : this.kayaks) {\n            if (!kayak.isOut()) {\n                kayak.setOut(true);\n                return kayak;\n            }\n        }\n        return null;\n    }\n\n    public boolean giveBack(int number) {\n        for (Kayak kayak : this.kayaks) {\n            if (kayak.getNumber() == number && kayak.isOut()) {\n                kayak.setOut(false);\n                return true;\n            }\n        }\n        return false;\n    }\n\n    public int freeCount() {\n        int free = 0;\n        for (Kayak kayak : this.kayaks) {\n            if (!kayak.isOut()) {\n                free++;\n            }\n        }\n        return free;\n    }\n}\n\n// ==== kayak/ui/TextUI.java ====\npackage kayak.ui;\n\nimport java.util.Scanner;\nimport kayak.domain.Kayak;\nimport kayak.logic.Dock;\n\npublic class TextUI {\n    private Scanner scanner;\n    private Dock dock;\n\n    public TextUI(Scanner scanner, Dock dock) {\n        this.scanner = scanner;\n        this.dock = dock;\n    }\n\n    public void start() {\n        while (true) {\n            System.out.print(\"> \");\n            String command = this.scanner.nextLine();\n            if (command.equals(\"quit\")) {\n                System.out.println(\"Bye!\");\n                break;\n            }\n            if (command.equals(\"rent\")) {\n                Kayak kayak = this.dock.rent();\n                if (kayak == null) {\n                    System.out.println(\"All kayaks are out\");\n                } else {\n                    System.out.println(\"Take kayak \" + kayak.getNumber());\n                }\n            } else if (command.equals(\"return\")) {\n                int number = Integer.valueOf(this.scanner.nextLine());\n                if (this.dock.giveBack(number)) {\n                    System.out.println(\"Kayak \" + number + \" is back\");\n                } else {\n                    System.out.println(\"Kayak \" + number + \" isn't out\");\n                }\n            } else if (command.equals(\"free\")) {\n                System.out.println(\"Free kayaks: \" + this.dock.freeCount());\n            } else {\n                System.out.println(\"Unknown command: \" + command);\n            }\n        }\n    }\n}\n","hints":["Start with Kayak: `package kayak.domain;` on the first line, then the class, with `public` in front of the class, its constructor and its methods, since Dock, in another package, uses them all. Then Dock, which imports `java.util.ArrayList` and `kayak.domain.Kayak`, and TextUI, which imports Scanner, Kayak and Dock.","When javac says `... is not public in ...; cannot be accessed from outside package`, add `public` where the message points. In the end, `Main.java` holds only Main, with the imports `java.util.Scanner`, `kayak.logic.Dock` and `kayak.ui.TextUI`."],"tests":[{"name":"Test 1","stdin":"rent\nrent\nfree\nreturn\n1\nreturn\n1\nrent\nrent\nrent\nfree\nquit\n","expect":"> Take kayak 1\n> Take kayak 2\n> Free kayaks: 1\n> Kayak 1 is back\n> Kayak 1 isn't out\n> Take kayak 1\n> Take kayak 3\n> All kayaks are out\n> Free kayaks: 0\n> Bye!","hidden":false},{"name":"Test 2","stdin":"free\nreturn\n2\nrent\npaddle\nreturn\n1\nrent\nrent\nfree\nquit\n","expect":"> Free kayaks: 3\n> Kayak 2 isn't out\n> Take kayak 1\n> Unknown command: paddle\n> Kayak 1 is back\n> Take kayak 1\n> Take kayak 2\n> Free kayaks: 1\n> Bye!","hidden":true},{"name":"Test 3","stdin":"","call":"Dock dock = new Dock(2);\nKayak first = dock.rent();\nSystem.out.println(first.getNumber() + \" \" + first.isOut() + \" \" + dock.freeCount());\nSystem.out.println(dock.giveBack(2) + \" \" + dock.giveBack(1) + \" \" + dock.freeCount());\n","expect":"1 true 1\nfalse true 2","hidden":true}],"require":[{"pattern":"\\bpackage\\s+kayak\\.domain\\s*;","message":"`Kayak.java` is in the folder `kayak/domain/`: start it with `package kayak.domain;`."},{"pattern":"\\bpackage\\s+kayak\\.logic\\s*;","message":"`Dock.java` is in the folder `kayak/logic/`: start it with `package kayak.logic;`."},{"pattern":"\\bpackage\\s+kayak\\.ui\\s*;","message":"`TextUI.java` is in the folder `kayak/ui/`: start it with `package kayak.ui;`."}],"forbid":[{"pattern":"==== Main\\.java ====(?:(?!// ====)[\\s\\S])*\\bclass\\s+(?:Kayak|Dock|TextUI)\\b","raw":true,"message":"Move Kayak, Dock and TextUI out of `Main.java`, each into the file of its package, so that `Main.java` holds only Main."}]}]}