ECS510 Algorithms and Data Structures in an Object-Oriented Framework (“ADSOOF”)

Exceptions in Java: drinks machines implemented with exceptions

Checked and run time exceptions

The code you have been given for the class DrinksMachine simulates pressing the Coke or Fanta button when there are no drinks of that type available by returning null from the call pressCoke() or pressFanta() on the DrinksMachine object, rather than a reference to a Can object. As noted this is actually considered bad programming practice, and a better simulation would work by throwing an exception.

To show the use of exceptions, there is a modified version of the code for drinks machines objects in the file DrinksMachineA.class. In order to use this, you will also need the file for the class of exceptions it can throw, which is in the file EmptyMachineException.class. Modified versions of the code in the previous notes which demonstrate use of objects is given in the Java files UseDrinksMachines1A.java, UseDrinksMachines2A.java, UseDrinksMachines3A.java and UseDrinksMachines4A.java.

The only difference between the code for DrinksMachineA and the code for DrinksMachine is that the two methods pressCoke and pressFanta are declared as possibly throwing the exception EmptyMachineException. This is indicated by the headers for these methods in the file DrinksMachineA.java, which you haven't seen and don't need to see, being:

  Can pressCoke() throws EmptyMachineException
  Can pressFanta() throws EmptyMachineException
Although you don't need to know what the code inside these methods is, you do need to know these are their headers in order to use them properly. The reason for this is that the throws EmptyMachineException indicates these methods may throw a checked exception of the type EmptyMachineException. A checked exception is an exception which has to be dealt with in the code which calls the method which may throw it. The other sort of exception is a run time exception where you don't have to write special code to deal with it. In general, the only exceptions which are run time exceptions are those which can occur in almost any piece of code. An example of a run time exception is NullPointerException, which as we saw previously, could be thrown by any call of a method, since it is thrown by any call of a method on a variable when that variable is set to null rather than to an object reference. An exception which represents an exceptional occurence in the world being handled by the program, rather than an internal programming error, should always be a checked exception not a run-time exception.

Note, below, it is explained why DrinksMachineA needs further modifications, but for most of these notes we shall ignore the additional issue raised there.

Catching exceptions

If a method has a header which shows that it throws a checked exception, then any code in which that method is called must be written to deal with the possibility of that exception. Java's try...catch statement is one way of doing that. The other way is that the method in which the call which may throw the exception occurs is itself declared as throwing the exception, we shall discuss that later. A try...catch statement consists of the keyword try, followed by {, followed by some statements, followed by catch, followed by ( an exception type, a variable name and ) followed by { followed by some statements (“some” here could be 0 statements), followed by }. When it is executed, the statements in the try part are executed one by one as normal Java statements, but if when executing one of them an exception is thrown, then execution of the try parts stops. If the exception is of the type of the exception type given in the catch part (or a subtype of it - we will discuss subtypes when we discuss inheritance), then the statements in the catch part are executed. If either all the statement in the try part are executed and no exception occurred, or an exception occurred which was of the type given in the catch part, causing the try part to be abandoned and the catch part to be executed, then execution continues to the statement following the try...catch statement, or the method it is in ends normally if it is the last statement. This is described as the exception being “caught”, the whole program then just carries on as normal. It is also possible for a try...catch statement to have more than one catch parts, and (although it is rare) to have an extra part indicated by the key word finally. See here for a full explanation.

This exception catching mechanism is perhaps better understood by looking at some code which uses try...catch statements. Here is the version of the previous UseDrinksMachines1.java which uses the modified DrinksMachineA:

class UseDrinksMachines1A
{
 public static void main(String[] args)
 {
  DrinksMachineA machine = new DrinksMachineA(50,0,10);
  System.out.println("I insert 200p into the drinks machine");
  machine.insert(200);
  System.out.println("I press the button labelled \"Coke\"");
  try {
       Can myCan;
       myCan = machine.pressCoke();
       System.out.println("I press the button labelled \"change\"");
       int myChange = machine.pressChange();
       System.out.println("I have "+myCan+" and "+myChange+"p");
      }
  catch(EmptyMachineException e)
      {
       System.out.println("The machine is empty");
       System.out.println("I press the button labelled \"change\"");
       int myChange = machine.pressChange();
       System.out.println("I have "+myChange+"p");
      }
  System.out.println("I have finished");
 }
}
Since a call to pressCoke may cause an EmptyMachineException to be thrown, it must occur within a setting where that exception is handled. So here it is in a try part which has a catch part which handles it. What happens here is that when
       myCan = machine.pressCoke();
is executed, if it causes an EmptyMachineException to be thrown, the remaining statements within the try part are not executed, instead the statements within the catch part are executed. If the pressCoke method executes normally, then the statements following it in the try part are executed, and the statements in the catch part are not executed. In either case, the final statement which causes I have finished to be printed is executed and the whole program finishes with no special error message. You can see that the drinks machine set up in this program is set up with no Cokes in the machine, so the exception will be thrown. Change the line
   DrinksMachineA machine = new DrinksMachineA(50,0,10);
by having a positive number instead of 0 to see the try...catch statement executed without an exception being thrown.

A more complex use of exceptions is shown in a version of the previous UseDrinksMachines2.java which uses the modified DrinksMachineA:

import java.util.Scanner;

class UseDrinksMachines2A
{
 public static void main(String[] args)
 {
  Scanner input = new Scanner(System.in);
  DrinksMachineA machine = new DrinksMachineA(50,10,10);
  System.out.print("Enter the sum of money you wish to spend on cokes: ");
  int amount = input.nextInt();
  int cans = spendOnCokes(amount,machine);
  int change = machine.pressChange();
  System.out.println("You have bought "+cans+" cans of coke");
  System.out.println("You have "+change+"p left");
  if(machine.cokesEmpty())
     System.out.println("You have emptied the machine of cokes");
  else
     System.out.println("The machine still has some cokes left");
 }

 public static int spendOnCokes(int sum,DrinksMachineA mach)
 {
  int count=0;
  mach.insert(sum);
  try {
       while(mach.getBalance()>=mach.getPrice())
         {
          mach.pressCoke();
          count++;
         }
      }
  catch(EmptyMachineException e)
      {
      }
  return count;
 }
}
Here the try...catch statement is inside the method spendOnCokes. The loop inside the try part could end when the test mach.getBalance()>=mach.getPrice() returns false, meaning the money left as balance in the machine is less than the price of a can so no more Cokes can be obtained. The catch part is then never reached, the next thing is to return the value of count and the method call terminates. However, if mach.pressCoke() is called when the machine referred to by mach has run out of Cokes, an EmptyMachineException is thrown, this causes the try part to be terminated, even though the exception is thrown inside a loop the loop is exited, and the code in the catch part is executed. But here there is no code in the catch part, it is empty, which is permitted in Java. So the method is terminated with the value of count returned. Here the exception is designed to be an alternative way of exiting the loop, in the case when we are finishing because the machine has run out of Coke cans rather than we have run out of money.

As another example, the following shows a case of a try...catch statement inside a try...catch statement:

import java.util.Scanner;

class UseDrinksMachines3A
{
 public static void main(String[] args)
 {
  int p;
  Scanner input = new Scanner(System.in);
  System.out.print("Enter the price for drinks on machine 1: ");
  p = input.nextInt();
  DrinksMachineA mach1 = new DrinksMachineA(p,0,10);
  System.out.print("Enter the price for drinks on machine 2: ");
  p = input.nextInt();
  DrinksMachineA mach2 = new DrinksMachineA(p,10,10);
  mach1.insert(100);
  mach2.insert(100);
  DrinksMachineA mach3 = cheaper(mach1,mach2);
  try {
        Can myCan = mach3.pressCoke();
        System.out.println("I bought a can of Coke from the cheaper machine");
      }
  catch(EmptyMachineException e1)
     {
      System.out.println("The cheaper machine has no cans of Coke left");
      if(mach3==mach2)
         mach3=mach1;
      else
         mach3=mach2;
      try {
           Can myCan = mach3.pressCoke();
           System.out.println("So I bought one from the other machine");
          }
      catch(EmptyMachineException e2)
          {
           System.out.println("Neither has the other machine");
          }
     }
  int myChange = mach1.pressChange()+mach2.pressChange();
  System.out.println("I am left with "+myChange+"p from two pounds");
 }

 public static DrinksMachineA cheaper(DrinksMachineA m1,DrinksMachineA m2)
 {
  if(m1.getPrice()<m2.getPrice())
     return m1;
  else
     return m2;
 }

}
The idea is that we try to buy a Coke from the cheaper machine, but if it is empty of Cokes, shown by an EmptyMachineException being thrown, we try to buy from the other machine - which may be empty of Cokes as well. As an interesting side point here, we don't know whether the cheaper machine, referred to by the variable mach3, refers to the machine referred to by mach1 or the machine referred to by mach2. So to switch it to refer to the other one, we use:
      if(mach3==mach2)
         mach3=mach1;
      else
         mach3=mach2;
The test mach3==mach2 returns true if mach3 and mach2 are aliases, that is, they refer to the same object. Otherwise it returns false, even if mach3 and mach2 refer to separate objects which are otherwise identical (same price, same number of drinks, same balance, same amount of accumulated cash). You are sometimes warned not to use == when testing objects for equality because it may not have the effect you expect, but it is correct to use it when you really do want to test that two object references actually refer to the same object.

Note, it is not essential to program this scenario in this way. We still have the method cokesEmpty which tests whether a machine is empty of Cokes, we could use that rather than by calling pressCoke() and seeing if an EmptyMachineException is thrown. In fact, the following code is probably clearer:


import java.util.Scanner;

class UseDrinksMachines5A
{
 public static void main(String[] args)
 {
  int p;
  Scanner input = new Scanner(System.in);
  System.out.print("Enter the price for drinks on machine 1: ");
  p = input.nextInt();
  DrinksMachineA mach1 = new DrinksMachineA(p,0,10);
  System.out.print("Enter the price for drinks on machine 2: ");
  p = input.nextInt();
  DrinksMachineA mach2 = new DrinksMachineA(p,10,10);
  mach1.insert(100);
  mach2.insert(100);
  DrinksMachineA mach3 = cheaper(mach1,mach2);
  try {
       if(!mach3.cokesEmpty())
           {
            Can myCan = mach3.pressCoke();
            System.out.println("I bought a can of Coke from the cheaper machine");
           }
       else
          {
           System.out.println("The cheaper machine has no cans of Coke left");
           if(mach3==mach2)
              mach3=mach1;
           else
              mach3=mach2;
           if(!mach3.cokesEmpty())
               {
                Can myCan = mach3.pressCoke();
                System.out.println("So I bought one from the other machine");
               }
           else
               System.out.println("Neither has the other machine");
          }
     }
  catch(EmptyMachineException e)
     {
     }
  int myChange = mach1.pressChange()+mach2.pressChange();
  System.out.println("I am left with "+myChange+"p from two pounds");
 }

 public static DrinksMachineA cheaper(DrinksMachineA m1,DrinksMachineA m2)
 {
  if(m1.getPrice()<m2.getPrice())
     return m1;
  else
     return m2;
 }

}
Here, the method pressCoke is only called when we have already tested that the machine has Cokes available, so we know an EmptyMachineException can never be thrown. However, we still have to write code which would catch it if it was thrown, since every time we call the method pressCoke it must be with code to catch the EmptyMachineException that method could throw, regardless of whether we know it is called in circumstances where that could never happen.

Exception messages

You may wonder why the Java try...catch statement not only names the type of exception caught, but has an additional variable name after that. In most cases that variable name is e, but when we had a try...catch inside a try...catch statement in UseDrinksMachines3A.java we used the names e1 and e2 for the two separate exceptions. In fact any variable name could be used, but the convention is that e is used.

What is actually happening here is that an exception is itself an object. The catch part of a try...catch statement sets its variable to refer to the exception object caught. In most cases the exception object is not otherwise referred to, but one way it can be referred to is to call the zero-argument method getMessage on it. When exception objects are created, they may be given a message which says more about the circumstances that caused them. This message is in the form of a String and calling getMessage() on the exception object returns it.

The code for DrinksMachineA has been written so that if an EmptyMachineException is thrown from a call to the pressCokes method, the message of that object is "coke", and if it is thrown from a call to the pressFantas method, the message is "fanta". The following program makes use of that:

class UseDrinksMachines6A
{
 public static void main(String[] args)
 {
  DrinksMachineA machine = new DrinksMachineA(50,10,0);
  System.out.println("I insert 200p into the drinks machine");
  machine.insert(200);
  Can myCan1=null,myCan2=null;
  try {
       System.out.println("I press the button labelled \"Coke\"");
       myCan1 = machine.pressCoke();
       System.out.println("I press the button labelled \"Fanta\"");
       myCan2 = machine.pressFanta();
      }
  catch(EmptyMachineException e)
      {
       System.out.println("The machine is empty of "+e.getMessage()+"s");
      }
  System.out.println("I press the button labelled \"change\"");
  int myChange = machine.pressChange();
  System.out.println("I have "+myCan1+" and "+myCan2+" and "+myChange+"p");
 }
}
The variable e in the catch part gets set to refer to the EmptyMachineException object that us thrown in the try part if execution of that part does throw an exception. Note that the EmptyMachineException which e could get set to could have been thrown by either the call myCan1 = machine.pressCoke() or the call myCan2 = machine.pressFanta() in the try part. The String returned by the call of e.getMessage() in the catch part says which it is.

The program in UseDrinksMachines6A.java has some faults, however. If the machine is empty of Cokes (as the program is written, it will be), the exception caused by machine.pressCoke() means the call machine.pressFanta() will never be made, because execution goes to the catch part before it is reached. Another point is that we have to declare

Can myCan1=null,myCan2=null;
that is, initialising the two variables myCan1 and myCan2 to null. The reason for this is that the Java compiler issues an error if a local variable to a method may never get assigned a value. If myCan1 = machine.pressCoke() causes an EmptyMachineException to be thrown, neither variable gets given a value, the exception means the assignment to myCan1 is not completed, and the later statement myCan2 = machine.pressFanta() is never reached. If myCan2 = machine.pressFanta() throws the exception, then while myCan1 is given a value, myCan2 is not. Initialising these variable to null means they will always have a value, even if that value is null. Note also that declaring the variables myCan1 and myCan2 before the try...catch statement means we can access them in the code after it. The statements within the { and } of the try and catch parts are normal blocks of code, so variables declared within them (as in the previous examples) have local scope only within those blocks, it is not even possible for a variable declared in the try part to be accessed in the catch part.

Passing on exceptions

If a method that may throw a checked exception is not called inside the try part of a try...catch statement with a catch parts which catches it, then the method in which the method call occurs must be annotated as throwing the exception. So what happens when an exception is thrown is that it may be inside several layers of method calls, and it gets thrown up each layer until it is caught. As a simple example, consider the following program:

import java.util.Scanner;

class UseDrinksMachines7A
{
 public static void main(String[] args)
 {
  Scanner input = new Scanner(System.in);
  DrinksMachineA machine = new DrinksMachineA(50,10,10);
  System.out.print("Enter the sum of money you wish to spend on cokes: ");
  int amount = input.nextInt();
  System.out.print("Enter the number of cokes you wish to buy: ");
  int number = input.nextInt();
  try {
       buyCokes(number,amount,machine);
       System.out.println("You successfully bought "+number+" cokes");
      }
  catch(EmptyMachineException e)
      {
       System.out.println("The machine ran out of cokes");
      }
  int change = machine.pressChange();
  System.out.println("You have "+change+"p left");
 }

 public static void buyCokes(int num,int sum,DrinksMachineA mach) throws EmptyMachineException
 {
  mach.insert(sum);
  for(int i=0; i<num; i++)
      mach.pressCoke();
 }
}
The method buyCokes here is intended to model putting some money into a machine and buying a fixed number of Cokes from that machine. It calls the method pressCoke() on the machine, but not within a try...catch statement which catches the EmptyMachineException this method may throw. Because of this, the method buyCokes has to be annotated as itself having the possibility of throwing an EmptyMachineException, which is done by adding the words throws EmptyMachineException to its signature, after the declaration of its parameters. As a result of this, the method buyCokes now has to be called either within a try...catch statement which catches the EmptyMachineException it may throw, or within another method which is also annotated as throwing that exception. In the program UseDrinksMachines7A the method is called from the main method within a try...catch which catches it.

The method buyCokes in UseDrinksMachines7A does not have a way of indicating how many Cokes were bought before the machine ran out if out could not buy the full number specified. This could not be given as the return value of the method because when a method terminates by throwing an exception it does not give a return value. However, the message inside an exception can be used to communicate a value. The program below is a rather messy way of dealing with this issue:

import java.util.Scanner;

class UseDrinksMachines8A
{
 public static void main(String[] args)
 {
  Scanner input = new Scanner(System.in);
  DrinksMachineA machine = new DrinksMachineA(50,10,10);
  System.out.print("Enter the sum of money you wish to spend on cokes: ");
  int amount = input.nextInt();
  System.out.print("Enter the number of cokes you wish to buy: ");
  int number = input.nextInt();
  try {
       buyCokes(number,amount,machine);
       System.out.println("You successfully bought "+number+" cokes");
      }
  catch(EmptyMachineException e)
      {
       number = Integer.parseInt(e.getMessage());
       System.out.println("You bought "+number+" cokes ");
       System.out.println("Then the machine ran out of cokes");
      }
  int change = machine.pressChange();
  System.out.println("You have "+change+"p left");
 }

 public static void buyCokes(int num,int sum,DrinksMachineA mach) throws EmptyMachineException
 {
  int count=0;
  mach.insert(sum);
  try {
       for(count=0; count<num; count++)
          mach.pressCoke();
      }
  catch(EmptyMachineException e)
     {
      throw new EmptyMachineException(""+count);
     }
 }
}
Here, the method buyCokes catches the EmptyMachineException and then throws a new one whose message gives the number of Cokes bought. Since an exception is an object, you can create an exception by the word new followed by a call to its constructor, which as we have seen is the class name followed by the arguments to the constructor. Exceptions have a zero-argument constructor, and a constructor which takes a String argument. To turn an int value into a String, we join it to the empty string using +, so that means new EmptyMachineException(""+count) creates a new EmptyMachineException object whose string is the string equivalent of the value in the int variable count. The Java statement throw followed by an exception value (typically the creation of a new exception, but it could be a variable already referring to an existing exception), when executed causes the method it is in to be terminated and the exception given thrown. Note the distinction between the keyword throws added to a method signature to list checked exceptions it may throw, and the keyword throw to give a statement which throws an exception.

So in UseDrinksMachine8A in the method buyCokes, the statement

   throw new EmptyMachineException(""+count);
causes a new exception to be thrown with its message the string of characters which when read as an integer is the number of cokes bought. Note that as it is within a catch part which catches an EmptyMachineException, the effect is to catch one EmptyMachineException and throw another. This second one is then caught in the main method, and its message is converted to an integer using Integer.parseInt. Note, this program is not intended as an example of good Java style. Exceptions should not generally be used for standard communication, it would be much better to rewrite the method buyCokes so it just returns an integer giving the number of Cokes actually bought. The point of this program is just to demonstrate that it is possible in Java to create your own exception object and throw it.

Run time exceptions

If a method call can throw a run time exception, you do not have to write code to catch it or indicate that it will be thrown on. But you can still write code to catch it if you want. The same try...catch mechanism is used as with checked exceptions. If a run time exception is not caught in the method which calls the method which caused it, it is thrown upwards to the method which called that method, and so on upwards until it is either caught or the main method is reached, and then the program terminates. As a simple example, below we go back to the use of the DrinksMachine class where a call of pressCoke() on a DrinksMachine object representing a machine which has run out of Cokes returns the value null. If a Can variable was set to the result of a call of pressCoke(), and the code had no check to see if it is null, then when a Can method is called on it, a NullPointerException will be thrown. In the following example, this is detected by catching the NullPointerException:

class UseDrinksMachines5
{
 public static void main(String[] args)
 {
  DrinksMachine machine = new DrinksMachine(50,0,10);
  System.out.println("I insert 200p into the drinks machine");
  machine.insert(200);
  System.out.println("I press the button labelled \"Coke\"");
  Can myCan = machine.pressCoke();
  System.out.println("I press the button labelled \"change\"");
  int myChange = machine.pressChange();
  System.out.println("I have "+myCan+" and "+myChange+"p");
  try {
       myCan.drink();
       System.out.println("I drink from the can");
      }
  catch(NullPointerException e)
      {
       System.out.println("The machine was empty of Cokes");
      }
 }
}
In fact the code for Can was written so that attempting to drink from a can which is already empty (meaning the method drink() has already been called on it) will result in an EmptyCanException being thrown. As this was written to be a run time exception, there was no necessity to write code which dealt with the possibility of this exception being thrown from a call to the drink() method on a Can object. This is not really good Java practice, since exceptions representing problems in the world being modelled by the system should be checked exceptions, with run time exceptions used only for programming errors. However, it was done this way in order to enable simple use of the DrinksMachine example without having to consider exceptions.

Below is some code which shows an EmptyCanException being caught:

class UseDrinksMachines6
{
 public static void main(String[] args)
 {
  try {
       DrinksMachine machine = new DrinksMachine(50,0,10);
       Can aCan = new Can("coke");
       aCan.drink();
       machine.loadCoke(aCan);
       System.out.println("I insert 200p into the drinks machine");
       machine.insert(200);
       System.out.println("I press the button labelled \"Coke\"");
       Can myCan = machine.pressCoke();
       System.out.println("I press the button labelled \"change\"");
       int myChange = machine.pressChange();
       System.out.println("I have "+myCan+" and "+myChange+"p");
       System.out.println("I drink from the can");
       myCan.drink();
       System.out.println("Now I have "+myCan+" and "+myChange+"p");
       System.out.println("I drink from the can again");
       myCan.drink();
       System.out.println("And now I have "+myCan+" and "+myChange+"p");
      }
  catch(NullPointerException e)
      {
       System.out.println("Error: method called on null");
      }
  catch(EmptyCanException e)
     {
      System.out.println("Error: attempt to drink from empty can");
     }
 }
}
This shows that a try...catch statement can have more than one catch part. It can have separate catch parts to catch the different sorts of exceptions that may be thrown in the try part. The code is actually set up so that the first call of myCan.drink() throws an EmptyCanException exception, because it represents a situation where an empty can has been put into the machine. It also shows why it is bad to make shortcuts by using null to cover special situations. A NullPointerException might be thrown if the variable myCan is set to null to represent no can being delivered by pressing the Coke button. But it could also be caused by any programming error which resulted in a method being called on an object variable set to null. The error message printed if a NullPointerException is caught reflects the fact that it is really a programming error, a method called on null. However, a message like this is meaningless to the user of a system who isn't interested in the code underneath, and doesn't even know about it. You may experience strange error messages even in commercial software which represents something intended for the programmer accidentally making it through to the end-user and it has probably just confused and/or annoyed you. Such things should not happen. But it would also be bad if a meaningful but wrong error message was given because the programmer assumed a particular run time exception could only be caused by a particular error in the world it was modelling, but it was actually caused by programming error.

Throwing multiple exceptions

If you experiment with the DrinksMachine class, you will find that if pressCoke() or pressFanta() is called on a DrinksMachine object when the balance (as given by calling getbalance() on the object is less than the price of a drink, then null is returned, just as it is if the machine is empty of drinks. If you experiment with the DrinksMachineA class, you will find that an EmptyMachineException is thrown if pressCoke() or pressFanta() is called on a DrinksMachineA object when the balance on the object is less than the price of a drink. To Java there is no mistake here, the Java compiler has no understanding of the human meaning we may give to variable names or classes. However, we can see this is a mistake, clearly it is misleading because the name of the exception thrown by pressCoke() and pressFanta() in DrinksMachineA leads us to believe it represents one possibility in the world we are modelling, whereas really it represents two: either the machine has run out of the required sort of drink, or we have not put enough money into it. This has led us to write programs which behave very misleadingly, they print error messages which do not represent what has really happened, telling us the machine is empty when the real problem may be that not enough money was put in.

To resolve this problem, another modified version of DrinksMachine is available, from the class file DrinksMachineB.class. In this case, calling the methods pressCoke() and pressFanta() can cause exceptions of two different types to be thrown, either EmptyMachineException or NotEnoughMoneyException, with the latter having the class file NotEnoughMoneyException.class. This means the headers for these methods in DrinksMachineB are:

  Can pressCoke() throws EmptyMachineException, NotEnoughMoneyException
  Can pressFanta() throws EmptyMachineException, NotEnoughMoneyException
It means a method in which either of these methods are called must be written to deal with the possibility of either exception. Code could be written to catch both, to throw both, or to catch one and throw the other. Below is a version of the simple UseDrinksMachines1.java program which catches both:
class UseDrinksMachines1B
{
 public static void main(String[] args)
 {
  DrinksMachineB machine = new DrinksMachineB(80,10,10);
  System.out.println("I insert 50p into the drinks machine");
  machine.insert(50);
  System.out.println("I press the button labelled \"Coke\"");
  try {
       Can myCan;
       myCan = machine.pressCoke();
       System.out.println("I press the button labelled \"change\"");
       int myChange = machine.pressChange();
       System.out.println("I have "+myCan+" and "+myChange+"p");
      }
  catch(EmptyMachineException e)
      {
       System.out.println("The machine is empty");
       System.out.println("I press the button labelled \"change\"");
       int myChange = machine.pressChange();
       System.out.println("I have "+myChange+"p");
      }
  catch(NotEnoughMoneyException e)
      {
       System.out.println("I did not put enough money in");
       System.out.println("I needed another "+e.getMessage()+"p");
       System.out.println("I press the button labelled \"change\"");
       int myChange = machine.pressChange();
       System.out.println("I have "+myChange+"p");
      }
  System.out.println("I have finished");
 }
}
As you can see, it requires a try...catch statement with two catch parts, one to catch each of the exceptions which may be thrown. We also need to add code to catch a NotEnoughMoneyException into our modified version of UseDrinksMachines2A.java even though this exception could never be thrown as the code in the loop ensures the pressCoke method is not called unless the balance is sufficient:
import java.util.Scanner;

class UseDrinksMachines2B
{
 public static void main(String[] args)
 {
  Scanner input = new Scanner(System.in);
  DrinksMachineB machine = new DrinksMachineB(50,10,10);
  System.out.print("Enter the sum of money you wish to spend on cokes: ");
  int amount = input.nextInt();
  int cans = spendOnCokes(amount,machine);
  int change = machine.pressChange();
  System.out.println("You have bought "+cans+" cans of coke");
  System.out.println("You have "+change+"p left");
  if(machine.cokesEmpty())
     System.out.println("You have emptied the machine of cokes");
  else
     System.out.println("The machine still has some cokes left");
 }

 public static int spendOnCokes(int sum,DrinksMachineB mach)
 {
  int count=0;
  mach.insert(sum);
  try {
       while(mach.getBalance()>=mach.getPrice())
         {
          mach.pressCoke();
          count++;
         }
      }
  catch(EmptyMachineException e)
      {
      }
  catch(NotEnoughMoneyException e)
      {
      }
  return count;
 }
}
The Java compiler is not intelligent enough to know a NotEnoughMoneyException will never be thrown, it requires code to deal with it so long as a method which declares that it throws it is called.

This code makes repeated calls of the method getPrice on the DrinksMachine object. However, if we assume that method always return the same value, that is unnecessary. We could just make one call and save the result in a variable. That would give the following code for the method:

public static int spendOnCokes(int sum,DrinksMachineB mach)
 {
  int count=0;
  int price=mach.getPrice();
  mach.insert(sum);
  try {
       while(mach.getBalance()>=price)
         {
          mach.pressCoke();
          count++;
         }
      }
  catch(EmptyMachineException e)
      {
      }
  catch(NotEnoughMoneyException e)
      {
      }
  return count;
 }
You should always avoid making repeated method calls in situations where you know each call will return the same value as the previous one. Saving the result in a variable and using the variable instead of repeating the call saves unnecessary time in calling the method again. In the code here, the time saved would be trivial, but if a method call involves a long calculation it could make a big improvement. Obviously it should be done only when we are sure that there is nothing which could cause repeated calls of the method to return different values.

A minor thing to note here is that a variation of the syntax for catching exceptions was introduced in the version of Java called Java 7. It enables a single catch part to list several exception types, and is used when the same action should take place for all the exception types. Using this variation, the method would be written:

public static int spendOnCokes(int sum,DrinksMachineB mach)
 {
  int count=0;
  int price=mach.getPrice();
  mach.insert(sum);
  try {
       while(mach.getBalance()>=price)
         {
          mach.pressCoke();
          count++;
         }
      }
  catch(EmptyMachineException | NotEnoughMoneyException e)
      {
      }
  return count;
 }


Matthew Huntbach

Last modified: 22 February 2019