{"id":"exceptions-throwing","slug":"throwing-exceptions","title":"Throwing an exception","text":"So far every exception came from one of Java's own methods. Your own methods and constructors can throw one too, with `throw`. That's how a class refuses values it can't work with.\n\n### Refusing a bad value\n\nA bank transfer moves money from one person to another. A transfer of -40 dollars would move money the wrong way, so this class refuses to create one:\n\n```java\npublic class Main {\n    public static void main(String[] args) {\n        Transfer rent = new Transfer(\"Maya\", \"Leo\", 400);\n        System.out.println(rent);\n        Transfer odd = new Transfer(\"Maya\", \"Leo\", -40);\n        System.out.println(odd);\n    }\n}\n// ==== Transfer.java ====\npublic class Transfer {\n    private String from;\n    private String to;\n    private int amount;\n\n    public Transfer(String from, String to, int amount) {\n        if (amount <= 0) {\n            throw new IllegalArgumentException(\"The amount must be positive, not \" + amount);\n        }\n        this.from = from;\n        this.to = to;\n        this.amount = amount;\n    }\n\n    @Override\n    public String toString() {\n        return this.from + \" -> \" + this.to + \": \" + this.amount + \" dollars\";\n    }\n}\n```\n\n```output\nMaya -> Leo: 400 dollars\n```\n\n```crash\nException in thread \"main\" java.lang.IllegalArgumentException: The amount must be positive, not -40\n\tat Transfer.<init>(Transfer.java:8)\n\tat Main.main(Main.java:5)\n```\n\n- `new IllegalArgumentException(\"...\")` creates an exception object, like any other `new`. The text becomes its message.\n- `throw` throws it. The constructor stops on that line, a bit like `return`, and the exception goes to the code that called it. Here that's `main`, which doesn't catch it, so the program stops.\n- In the stack trace, `Transfer.<init>` is the constructor: `<init>` is how a stack trace names one.\n- The check comes first, before any instance variable is set, and the exception stops the constructor, so no transfer of -40 is ever made: `odd` never gets a value. An object that refuses bad values can't end up in an impossible state.\n\n**IllegalArgumentException** is Java's exception for exactly this: a method or constructor was given a parameter value it can't accept. It's a RuntimeException, so it's unchecked: neither the constructor nor the code that calls it needs a `throws`. A good message says what was wrong, and includes the value that was refused.\n\n### Catching it\n\nThe code that creates the transfers decides what a refusal means. Here the amounts come from an array, and a refused one is reported and skipped:\n\n```java\npublic class Main {\n    public static void main(String[] args) {\n        int[] amounts = {120, 0, 35, -8};\n        int sent = 0;\n        for (int amount : amounts) {\n            try {\n                Transfer transfer = new Transfer(\"Maya\", \"Leo\", amount);\n                System.out.println(\"Sent: \" + transfer);\n                sent += amount;\n            } catch (IllegalArgumentException e) {\n                System.out.println(\"Refused: \" + e.getMessage());\n            }\n        }\n        System.out.println(\"Sent in all: \" + sent + \" dollars\");\n    }\n}\n// ==== Transfer.java ====\npublic class Transfer {\n    private String from;\n    private String to;\n    private int amount;\n\n    public Transfer(String from, String to, int amount) {\n        if (amount <= 0) {\n            throw new IllegalArgumentException(\"The amount must be positive, not \" + amount);\n        }\n        this.from = from;\n        this.to = to;\n        this.amount = amount;\n    }\n\n    @Override\n    public String toString() {\n        return this.from + \" -> \" + this.to + \": \" + this.amount + \" dollars\";\n    }\n}\n```\n\n```output\nSent: Maya -> Leo: 120 dollars\nRefused: The amount must be positive, not 0\nSent: Maya -> Leo: 35 dollars\nRefused: The amount must be positive, not -8\nSent in all: 155 dollars\n```\n\n`e.getMessage()` is the text the exception was given when it was created.\n\n### In a method\n\nMethods check their parameters the same way. A small concert hall has its seats numbered from 1 up, and a seat can be reserved once. Reserving a seat that doesn't exist is a bad parameter. Reserving a seat that's already taken is a different problem: the number is fine, just not now. Java has an exception for that too, **IllegalStateException**: the object isn't in a state where it can do what was asked. It's unchecked as well.\n\n```java\npublic class Main {\n    public static void main(String[] args) {\n        SeatMap hall = new SeatMap(20);\n        int[] requests = {5, 25, 5, 6};\n        for (int seat : requests) {\n            try {\n                hall.reserve(seat);\n                System.out.println(\"Seat \" + seat + \" is yours\");\n            } catch (IllegalArgumentException e) {\n                System.out.println(\"Check the number: \" + e.getMessage());\n            } catch (IllegalStateException e) {\n                System.out.println(\"Sorry: \" + e.getMessage());\n            }\n        }\n        System.out.println(hall.free() + \" seats left\");\n    }\n}\n// ==== SeatMap.java ====\npublic class SeatMap {\n    private boolean[] taken;\n\n    public SeatMap(int seats) {\n        this.taken = new boolean[seats];\n    }\n\n    public void reserve(int seat) {\n        if (seat < 1 || seat > this.taken.length) {\n            throw new IllegalArgumentException(\"there's no seat \" + seat);\n        }\n        if (this.taken[seat - 1]) {\n            throw new IllegalStateException(\"seat \" + seat + \" is taken\");\n        }\n        this.taken[seat - 1] = true;\n    }\n\n    public int free() {\n        int count = 0;\n        for (boolean seatTaken : this.taken) {\n            if (!seatTaken) {\n                count++;\n            }\n        }\n        return count;\n    }\n}\n```\n\n```output\nSeat 5 is yours\nCheck the number: there's no seat 25\nSorry: seat 5 is taken\nSeat 6 is yours\n18 seats left\n```\n\n- Both checks come before the seat is marked as taken, so a refused call changes nothing. Check first, then change.\n- `reserve` returns nothing. When it returns normally, the reservation worked; when it can't do its job, it throws. A method could return `false` instead, but a return value is easy to ignore by accident. An exception isn't: if nothing catches it, the program stops.\n","task":"An oven can be set from 50 to 250 degrees. In `Oven.java`, write the class `Oven` with:\n\n- a constructor `Oven(int degrees)`\n- `setDegrees(int degrees)`, which changes the setting\n- `getDegrees()`, which returns it\n\nThe constructor and `setDegrees` both refuse a temperature below 50 or above 250 with an IllegalArgumentException whose message is `No oven setting for 300 degrees`, with the refused value in it. A refused `setDegrees` keeps the old setting. The check runs:\n\n```java\nOven oven = new Oven(180);\ntry {\n    oven.setDegrees(300);\n} catch (IllegalArgumentException e) {\n    System.out.println(e.getMessage());\n}\nSystem.out.println(oven.getDegrees());\n```\n\nand expects:\n\n```\nNo oven setting for 300 degrees\n180\n```\n","kind":"code","seed":"// ==== Oven.java ====\npublic class Oven {\n\n}\n\n// ==== Main.java ====\npublic class Main {\n    public static void main(String[] args) {\n        Oven oven = new Oven(180);\n        try {\n            oven.setDegrees(300);\n        } catch (IllegalArgumentException e) {\n            System.out.println(e.getMessage());\n        }\n        System.out.println(oven.getDegrees());\n    }\n}\n","solution":"// ==== Oven.java ====\npublic class Oven {\n    private int degrees;\n\n    public Oven(int degrees) {\n        if (degrees < 50 || degrees > 250) {\n            throw new IllegalArgumentException(\"No oven setting for \" + degrees + \" degrees\");\n        }\n        this.degrees = degrees;\n    }\n\n    public void setDegrees(int degrees) {\n        if (degrees < 50 || degrees > 250) {\n            throw new IllegalArgumentException(\"No oven setting for \" + degrees + \" degrees\");\n        }\n        this.degrees = degrees;\n    }\n\n    public int getDegrees() {\n        return this.degrees;\n    }\n}\n\n// ==== Main.java ====\npublic class Main {\n    public static void main(String[] args) {\n        Oven oven = new Oven(180);\n        try {\n            oven.setDegrees(300);\n        } catch (IllegalArgumentException e) {\n            System.out.println(e.getMessage());\n        }\n        System.out.println(oven.getDegrees());\n    }\n}\n","hints":["The constructor and `setDegrees` both start with a check, as Transfer's constructor does: when the value is below 50 or above 250, throw an IllegalArgumentException before anything is set.","The check is `if (degrees < 50 || degrees > 250) { throw new IllegalArgumentException(\"No oven setting for \" + degrees + \" degrees\"); }`. Set `this.degrees` only after it: when it throws, the method stops and the old setting stays."],"tests":[{"name":"Test 1","stdin":"","call":"Oven oven = new Oven(180);\ntry {\n    oven.setDegrees(300);\n} catch (IllegalArgumentException e) {\n    System.out.println(e.getMessage());\n}\nSystem.out.println(oven.getDegrees());\n","expect":"No oven setting for 300 degrees\n180","hidden":false},{"name":"Test 2","stdin":"","call":"try {\n    new Oven(20);\n    System.out.println(\"accepted\");\n} catch (IllegalArgumentException e) {\n    System.out.println(\"refused: \" + e.getMessage());\n}\ntry {\n    new Oven(-10);\n    System.out.println(\"accepted\");\n} catch (IllegalArgumentException e) {\n    System.out.println(\"refused: \" + e.getMessage());\n}\n","expect":"refused: No oven setting for 20 degrees\nrefused: No oven setting for -10 degrees","hidden":true},{"name":"Test 3","stdin":"","call":"Oven oven = new Oven(50);\noven.setDegrees(250);\nSystem.out.println(oven.getDegrees());\ntry {\n    oven.setDegrees(251);\n} catch (IllegalArgumentException e) {\n    System.out.println(e.getMessage());\n}\ntry {\n    oven.setDegrees(49);\n} catch (IllegalArgumentException e) {\n    System.out.println(e.getMessage());\n}\nSystem.out.println(oven.getDegrees());\n","expect":"250\nNo oven setting for 251 degrees\nNo oven setting for 49 degrees\n250","hidden":true}],"require":[{"pattern":"\\bthrow\\s+new\\s+IllegalArgumentException\\s*\\(","message":"Refuse a bad temperature with `throw new IllegalArgumentException(...)`."}],"forbid":[],"more":[{"task":"A gift card refuses to spend what it doesn't have. What does this program print?\n","kind":"predict","seed":"public class Main {\n    public static void main(String[] args) {\n        GiftCard card = new GiftCard(50);\n        int[] purchases = {20, 40, -10, 30};\n        for (int amount : purchases) {\n            try {\n                card.spend(amount);\n                System.out.println(\"spent \" + amount);\n            } catch (IllegalArgumentException e) {\n                System.out.println(e.getMessage());\n            }\n        }\n        System.out.println(\"left: \" + card.getBalance());\n    }\n}\n// ==== GiftCard.java ====\npublic class GiftCard {\n    private int balance;\n\n    public GiftCard(int balance) {\n        this.balance = balance;\n    }\n\n    public void spend(int amount) {\n        if (amount <= 0) {\n            throw new IllegalArgumentException(\"not an amount: \" + amount);\n        }\n        if (amount > this.balance) {\n            throw new IllegalArgumentException(\"only \" + this.balance + \" left\");\n        }\n        this.balance -= amount;\n    }\n\n    public int getBalance() {\n        return this.balance;\n    }\n}\n","solution":"public class Main {\n    public static void main(String[] args) {\n        GiftCard card = new GiftCard(50);\n        int[] purchases = {20, 40, -10, 30};\n        for (int amount : purchases) {\n            try {\n                card.spend(amount);\n                System.out.println(\"spent \" + amount);\n            } catch (IllegalArgumentException e) {\n                System.out.println(e.getMessage());\n            }\n        }\n        System.out.println(\"left: \" + card.getBalance());\n    }\n}\n// ==== GiftCard.java ====\npublic class GiftCard {\n    private int balance;\n\n    public GiftCard(int balance) {\n        this.balance = balance;\n    }\n\n    public void spend(int amount) {\n        if (amount <= 0) {\n            throw new IllegalArgumentException(\"not an amount: \" + amount);\n        }\n        if (amount > this.balance) {\n            throw new IllegalArgumentException(\"only \" + this.balance + \" left\");\n        }\n        this.balance -= amount;\n    }\n\n    public int getBalance() {\n        return this.balance;\n    }\n}\n","hints":["After spending 20, the card has 30 left. Spending 40 is refused before the balance changes, and so is -10.","When `spend` throws, the `println` after it in the `try` block is skipped, and the `catch` block prints the message instead."],"tests":[{"name":"Output","stdin":"","expect":"spent 20\nonly 30 left\nnot an amount: -10\nspent 30\nleft: 0","hidden":false}],"require":[],"forbid":[],"lines":["spent 20","only 30 left","not an amount: -10","spent 30","left: 0"]},{"task":"`Transfer.java` is the class from the lesson, with one more check and a getter. The sender and the receiver must be different people, or the constructor throws an IllegalArgumentException with the message `Can't send money to yourself`, and `getAmount()` returns the amount.\n\nWrite `main`. It reads lines until an empty line. Each line has a sender, a receiver and an amount, separated by commas, such as `Maya,Leo,40`. For each line, create a Transfer and print `Sent: ` and the transfer. When the constructor refuses, print `Refused: ` and its message. When the amount isn't a whole number, print `Refused: ` and the amount in quotes, followed by ` is not an amount`. At the end, print `Sent in all: `, the sum of the amounts sent, and ` dollars`. Every line has three parts.\n\nA NumberFormatException is an IllegalArgumentException too, so mind the order of the `catch` blocks. For the input shown, the program prints:\n\n```\nSent: Maya -> Leo: 40 dollars\nRefused: The amount must be positive, not -5\nRefused: \"ten\" is not an amount\nRefused: Can't send money to yourself\nSent in all: 40 dollars\n```\n","kind":"code","seed":"// ==== Main.java ====\nimport java.util.Scanner;\n\npublic class Main {\n    public static void main(String[] args) {\n        Scanner scanner = new Scanner(System.in);\n\n    }\n}\n\n// ==== Transfer.java ====\npublic class Transfer {\n    private String from;\n    private String to;\n    private int amount;\n\n    public Transfer(String from, String to, int amount) {\n        if (amount <= 0) {\n            throw new IllegalArgumentException(\"The amount must be positive, not \" + amount);\n        }\n        if (from.equals(to)) {\n            throw new IllegalArgumentException(\"Can't send money to yourself\");\n        }\n        this.from = from;\n        this.to = to;\n        this.amount = amount;\n    }\n\n    public int getAmount() {\n        return this.amount;\n    }\n\n    @Override\n    public String toString() {\n        return this.from + \" -> \" + this.to + \": \" + this.amount + \" dollars\";\n    }\n}\n","solution":"// ==== Main.java ====\nimport java.util.Scanner;\n\npublic class Main {\n    public static void main(String[] args) {\n        Scanner scanner = new Scanner(System.in);\n        int sent = 0;\n        while (true) {\n            String line = scanner.nextLine();\n            if (line.isEmpty()) {\n                break;\n            }\n            String[] parts = line.split(\",\");\n            try {\n                Transfer transfer = new Transfer(parts[0], parts[1], Integer.parseInt(parts[2]));\n                System.out.println(\"Sent: \" + transfer);\n                sent += transfer.getAmount();\n            } catch (NumberFormatException e) {\n                System.out.println(\"Refused: \\\"\" + parts[2] + \"\\\" is not an amount\");\n            } catch (IllegalArgumentException e) {\n                System.out.println(\"Refused: \" + e.getMessage());\n            }\n        }\n        System.out.println(\"Sent in all: \" + sent + \" dollars\");\n    }\n}\n\n// ==== Transfer.java ====\npublic class Transfer {\n    private String from;\n    private String to;\n    private int amount;\n\n    public Transfer(String from, String to, int amount) {\n        if (amount <= 0) {\n            throw new IllegalArgumentException(\"The amount must be positive, not \" + amount);\n        }\n        if (from.equals(to)) {\n            throw new IllegalArgumentException(\"Can't send money to yourself\");\n        }\n        this.from = from;\n        this.to = to;\n        this.amount = amount;\n    }\n\n    public int getAmount() {\n        return this.amount;\n    }\n\n    @Override\n    public String toString() {\n        return this.from + \" -> \" + this.to + \": \" + this.amount + \" dollars\";\n    }\n}\n","hints":["Split each line at the commas, and create the Transfer inside a `try` block, with `Integer.parseInt(parts[2])` as the amount. Add the amount to the sum only after the transfer was created.","Two `catch` blocks: `NumberFormatException` first, then `IllegalArgumentException`. The other way around, javac refuses: the first block would already catch every NumberFormatException."],"tests":[{"name":"Test 1","stdin":"Maya,Leo,40\nLeo,Maya,-5\nLeo,Maya,ten\nMaya,Maya,15\n\n","expect":"Sent: Maya -> Leo: 40 dollars\nRefused: The amount must be positive, not -5\nRefused: \"ten\" is not an amount\nRefused: Can't send money to yourself\nSent in all: 40 dollars","hidden":false},{"name":"Test 2","stdin":"Ana,Ben,10\nBen,Ana,25\n\n","expect":"Sent: Ana -> Ben: 10 dollars\nSent: Ben -> Ana: 25 dollars\nSent in all: 35 dollars","hidden":true},{"name":"Test 3","stdin":"Ana,Ben,1.5\nAna,Ben,0\n\n","expect":"Refused: \"1.5\" is not an amount\nRefused: The amount must be positive, not 0\nSent in all: 0 dollars","hidden":true},{"name":"Test 4","stdin":"\n","expect":"Sent in all: 0 dollars","hidden":true}],"require":[{"pattern":"\\bcatch\\s*\\(\\s*NumberFormatException\\b","message":"Catch the `NumberFormatException` of an amount that isn't a number in a block of its own."},{"pattern":"\\bcatch\\s*\\(\\s*IllegalArgumentException\\b","message":"Catch the constructor's `IllegalArgumentException` and print its message."}],"forbid":[]}]}