Posted on Leave a comment

Sealed Interfaces – Object-Oriented Programming

Sealed Interfaces

Analogous to sealed classes, sealed interfaces can also be defined. However, a sealed interface can have both subinterfaces and subclasses as its permitted direct subtypes. The permitted subtypes can be subclasses that implement the sealed interface and subinterfaces that extend the sealed interface. This is in contrast to a sealed class that can have direct subclasses, but not direct subinterfaces—as classes cannot be extended or implemented by interfaces.

Figure 5.10 shows the book domain from Figure 5.9 that has been augmented with a sealed interface that specifies its permitted direct subtypes in a permits clause:

Click here to view code image

public sealed interface Subscribable permits Ebook, Audiobook, VIPSubcribable {}

The sealed superinterface Subscribable has two final permitted direct subclasses (that implement the superinterface) and one non-sealed direct subinterface (that extends the superinterface). The declarations of the permitted direct subclasses Ebook and Audiobook have been updated accordingly so that they implement the direct superinterface Subscribable.

Click here to view code image

public final class Ebook extends Book implements Subscribable {}
public final class Audiobook extends Book implements Subscribable {}
public non-sealed interface VIPSubcribable extends Subscribable {}

The rest of the type declarations from Figure 5.9 remain the same in Figure 5.10:

Click here to view code image

public abstract sealed class Book permits PrintedBook, Ebook, Audiobook {}
public non-sealed class PrintedBook extends Book {}

Note that it is perfectly possible for a class or an interface to be a permitted direct subtype of more than one direct supertype—as is the case for the Ebook and the Audiobook subclasses in Figure 5.10.

Figure 5.10 Sealed Classes and Interfaces

We see from the discussion above that the permitted direct subtypes of a sealed superinterface abide by the same contract rules as for sealed superclasses:

  • A permitted direct subclass or subinterface must extend or implement its direct superinterface, respectively.
  • Any permitted subclass of a sealed interface must be declared either sealed, non-sealed or final, but any permitted subinterface can only be declared either sealed or non-sealed. The modifier final is not allowed for interfaces.
  • The same rules for locality also apply for sealed interfaces and their permitted direct subtypes: All are declared either in the same named module or in the same package (named or unnamed) in the unnamed module.
Enum and Record Types as Permitted Direct Subtypes

By definition, an enum type (p. 287) is either implicitly final (has no enum constants that have a class body, as shown at (2)) or implicitly sealed (has at least one enum constant with a class body that constitutes an implicitly declared direct subclass, as shown at (3) for the constant DVD_R that has an empty class body). Thus an enum type can be specified as a permitted direct subtype of a sealed superinterface, as shown at (1). The modifiers final and sealed cannot be explicitly specified in the enum type declaration.

Click here to view code image

sealed interface MediaStorage permits CD, DVD {}       // (1) Sealed interface
enum CD implements MediaStorage {CD_ROM, CD_R, CD_W}   // (2) Implicitly final
enum DVD implements MediaStorage {DVD_R {}, DVD_RW}    // (3) Implicitly sealed

Analogously, a record class (p. 299) is implicitly final, and can be specified as a permitted direct subtype of a sealed superinterface. The sealed interface MediaStorage at (1a) now permits the record class HardDisk as a direct subtype. Again note that the modifier final cannot be specified in the header of the HardDisk record class declared at (4).

Click here to view code image

sealed interface MediaStorage permits CD, DVD, HardDisk {}// (1a) Sealed interface
record HardDisk(double capacity) implements MediaStorage {}// (4) Implicitly final

Posted on Leave a comment

Overriding the throws Clause – Exception Handling

Overriding the throws Clause

A subclass can override a method defined in its superclass by providing a new implementation (§5.1, p. 196). What happens when a superclass method with a list of exceptions in its throws clause is overridden in a subclass? The method declaration in the subclass need not specify a throws clause if it does not throw any checked exceptions, and if it does, it can specify only checked exception classes that are already in the throws clause of the superclass method, or that are subclasses of the checked exceptions in the throws clause of the superclass method. As a consequence, an overriding method can have more number of exceptions, but it cannot allow broader checked exceptions in its throws clause than the superclass method does. Allowing broader checked exceptions in the overriding method would create problems for clients who already deal with the exceptions specified in the superclass method. Such clients would be ill prepared if an object of the subclass threw a checked exception they were not prepared for. However, there are no restrictions on specifying unchecked exceptions in the throws clause of the overriding method. The preceding discussion also applies to overriding methods from an interface that a class implements.

In the code below, the method compute() at (1) in superclass A is overridden correctly at (2) in subclass B. The throws clause of the method at (2) in subclass B specifies only one checked exception (ThirdException) from the throws clause at (1) and adds the more specific subclass exception (SubFirstException) of the superclass exception (FirstException) that is specified in the throws clause at (1). An unchecked exception (NumberFormatException) is also specified in the throws clause at (2). The checked exceptions in the throws clause at (2) are covered by the checked exceptions specified in the throws clause at (1). Unchecked exceptions are inconsequential in this regard. The subclass C does not override the compute() method from class A correctly, as the throws clause at (3) specifies an exception (FourthException) that the overridden method at (1) in class A cannot handle.

Click here to view code image

// New exception classes:
class FirstException    extends Exception { }
class SecondException   extends Exception { }
class ThirdException    extends Exception { }
class FourthException   extends Exception { }
class SubFirstException extends FirstException { }
// Superclass
class A {
  protected void compute()
      throws FirstException, SecondException, ThirdException { /* … */ }  // (1)
}
// Subclass
class B extends A {
  @Override
  protected void compute()
      throws ThirdException, SubFirstException, NumberFormatException {     // (2)
    /* … */
  }
}
//Subclass
class C extends A {
  @Override
  protected void compute()                            // Compile-time error at (3)
      throws FirstException, ThirdException, FourthException { /* … */ }  // (3)
}

Usage of checked and unchecked exceptions in different contexts is compared in Table 7.1.

Table 7.1 Comparing Checked and Unchecked Exceptions

Posted on Leave a comment

The throws Clause – Exception Handling

7.5 The throws Clause

A throws clause can be specified in a method or a constructor header to declare any checked exceptions that can be thrown by a statement in the body of a method or a constructor. It is declared immediately preceding the body of the method or the constructor.

Click here to view code image

… throws
ExceptionType
1
,
ExceptionType
2
,…,
ExceptionType
n
 { /* Body */ }

Each ExceptionTypei is an exception type (i.e., a Throwable or one of its subclasses), although usually only checked exceptions are specified. The compiler enforces that if a checked exception can be thrown from the body of the method or the constructor, then either the type of this exception or a supertype of its exception type is specified in the throws clause of the method or the constructor. The throws clause can specify unchecked exceptions, but this is seldom done and the compiler does not enforce any restrictions on their usage.

The throws clause is part of the contract that a method or a constructor offers to its clients. The throws clause can specify any number of exception types in any order, even those that are not thrown by the method or the constructor. The compiler simply ensures that any checked exception that can actually be thrown in the method or constructor body is covered by the throws clause. Of course, any caller of the method or constructor cannot ignore the checked exceptions specified in the throws clause.

In a method or a constructor, a checked exception can be thrown directly by a throw statement, or indirectly by calling other methods or constructors that can throw a checked exception. If a checked exception is thrown, the code must obey the following rule (known by various names: catch-or-declare rule, handle-or-declare rule, catch-or-specify requirement):

  • Either use a try block and catch the checked exception in a catch block and deal with it
  • Or explicitly allow propagation of the checked exception to its caller by declaring it in the throws clause

Note that catching and dealing with a checked exception does not necessarily imply resumption of normal execution. A catch clause can catch the checked exception and choose to throw some other exception or even the same exception that is either unchecked or declared in the throws clause (p. 401). This rule ensures that a checked exception will be dealt with, regardless of the path of execution. This aids development of robust programs, as allowance can be made for many contingencies.

In Example 7.8, a new checked exception is defined, where the checked exception class IntegerDivisionByZero extends the Exception class. The method call at (2) in the try block at (1) results in the printAverage() method at (6) to be executed. The method call at (7) results in the computeAverage() method at (8) to be executed.

In the if statement at (9), the method computeAverage() throws the checked exception IntegerDivisionByZero. Neither the computeAverage() method nor the printAverage() method catches the exception, but instead throws it to the caller, as declared in the throws clauses in their method headers at (6) and (8). The exception propagates to the main() method. Since the printAverage() method was called from the context of the try block at (1) in the main() method, the exception is successfully caught by its catch clause at (3). The exception is handled and the finally clause at (4) is executed, with normal execution resuming from (5). If the method main() did not catch the exception, it would have to declare this exception in a throws clause. In that case, the exception would end up being handled by the default exception handler.

Example 7.8 The throws Clause

Click here to view code image

// File: IntegerDivisionByZero.java
public class IntegerDivisionByZero extends Exception {
  IntegerDivisionByZero() { super(“Integer Division by Zero”); }
}

Click here to view code image

// File: Average8.java
public class Average8 {
  public static void main(String[] args) {
    try {                                                          // (1)
      printAverage(100, 0);                                        // (2)
    } catch (IntegerDivisionByZero idbz) {                         // (3)
      idbz.printStackTrace();
      System.out.println(“Exception handled in main().”);
    } finally {                                                    // (4)
      System.out.println(“Finally done in main().”);
    }
    System.out.println(“Exit main().”);                            // (5)
  }
  public static void printAverage(int totalSum, int totalCount)
      throws IntegerDivisionByZero {                               // (6)
    int average = computeAverage(totalSum, totalCount);            // (7)
    System.out.println(“Average = ” +
        totalSum + ” / ” + totalCount + ” = ” + average);
    System.out.println(“Exit printAverage().”);
  }
  public static int computeAverage(int sum, int count)
      throws IntegerDivisionByZero {                               // (8)
    System.out.println(“Computing average.”);
    if (count == 0)                                                // (9)
      throw new IntegerDivisionByZero();
    return sum/count;                                              // (10)
  }
}

Output from the program:

Click here to view code image

Computing average.
IntegerDivisionByZero: Integer Division By Zero
        at Average8.computeAverage(Average8.java:27)
        at Average8.printAverage(Average8.java:17)
        at Average8.main(Average8.java:5)
Exception handled in main().
Finally done in main().
Exit main().

As mentioned earlier, the exception type specified in the throws clause can be a superclass of the actual exceptions thrown—that is, the exceptions thrown must be assignable to the type of the exceptions specified in the throws clause. If a method or a constructor can throw a checked exception, then the throws clause must declare its exception type or a supertype of its exception type; otherwise, a compile-time error will occur. In the printAverage() method, the superclass Exception of the subclass IntegerDivisionByZero could be specified in the throws clause of the method. This would also entail that the main() method either catch an Exception or declare it in a throws clause.

Click here to view code image

public static void main(String[] args) throws Exception {
  /* … */
}
public static void printAverage(int totalSum, int totalCount) throws Exception {
  /* … */
}

It is generally considered bad programming style to specify exception superclasses in the throws clause when the actual exceptions thrown are instances of their subclasses. It is also recommended to use the @throws tag in a Javadoc comment to document the checked exceptions that a method or a constructor can throw, together with any unchecked exceptions that might also be relevant to catch.

Posted on Leave a comment

The Multi-catch Clause – Exception Handling

7.6 The Multi-catch Clause

Example 7.9 uses a try block that has multiple uni-catch clauses. This example is based on Example 7.8. The sum of the values and the number of values needed to calculate the average are now read as program arguments from the command line at (2) and (3), respectively. The example shows a try statement at (1) that uses three uni-catch clauses: at (5), (6), and (7). In a uni-catch clause, a single exception type is specified for the catch parameter.

In Example 7.9, the method printAverage() is only called at (4) if there are at least two consecutive integers specified on the command line. An unchecked ArrayIndexOutOfBoundsException is thrown if there are not enough program arguments, and an unchecked NumberFormatException is thrown if an argument cannot be converted to an int value. The astute reader will notice that the code for handling these two exceptions is the same in the body of the respective catch clauses. In order to avoid such code duplication, one might be tempted to replace the two catch clauses with a single catch clause that catches a more general exception, for example:

Click here to view code image

catch (RuntimeException rte) { // NOT RECOMMENDED!
  System.out.println(rte);
  System.out.println(“Usage: java Average9 <sum of values> <no. of values>”);
}

This is certainly not recommended, as specific exceptions are to be preferred over general exceptions, not the least because a more general exception type might unintentionally catch more exceptions than intended.

Example 7.9 Using Multiple catch Clauses

Click here to view code image

// File: IntegerDivisionByZero.java
public class IntegerDivisionByZero extends Exception {
  IntegerDivisionByZero() { super(“Integer Division by Zero”); }
}

Click here to view code image

// File: Average9.java
public class Average9 {
  public static void main(String[] args) {
    try {                                                     // (1)
      int sum         = Integer.parseInt(args[0]);            // (2)
      int numOfValues = Integer.parseInt(args[1]);            // (3)
      printAverage(sum, numOfValues);                         // (4)
    } catch (ArrayIndexOutOfBoundsException aioob) {          // (5) uni-catch
      System.out.println(aioob);
      System.out.println(“Usage: java Average9 <sum of values> <no. of values>”);
    } catch (NumberFormatException nfe) {                     // (6) uni-catch
      System.out.println(nfe);
      System.out.println(“Usage: java Average9 <sum of values> <no. of values>”);
    } catch (IntegerDivisionByZero idbz) {                    // (7) uni-catch
      idbz.printStackTrace();
      System.out.println(“Exception handled in main().”);
    } finally {                                               // (8)
      System.out.println(“Finally done in main().”);
    }
    System.out.println(“Exit main().”);                       // (9)
  }
  public static void printAverage(int totalSum, int totalCount)
      throws IntegerDivisionByZero {
    int average = computeAverage(totalSum, totalCount);
    System.out.println(“Average = ” +
        totalSum + ” / ” + totalCount + ” = ” + average);
    System.out.println(“Exit printAverage().”);
  }
  public static int computeAverage(int sum, int count)
      throws IntegerDivisionByZero {
    System.out.println(“Computing average.”);
    if (count == 0)
      throw new IntegerDivisionByZero();
    return sum/count;
  }
}

Running the program:

Click here to view code image

>
java Average9 100 twenty

java.lang.NumberFormatException: For input string: “twenty”
Usage: java Average9 <sum of values> <no. of values>
Finally done in main().
Exit main().

Running the program:

Click here to view code image

>
java Average9 100

java.lang.ArrayIndexOutOfBoundsException: 1
Usage: java Average9 <sum of values> <no. of values>
Finally done in main().
Exit main().

The multi-catch clause provides the solution, allowing specific exceptions to be declared and avoiding duplicating the same code for the body of the catch clauses. The syntax of the multi-catch clause is as follows:

Click here to view code image

catch (
exception_type
1
|
exception_type
2
|…|
exception_type
k
parameter
) {
statements
}

The multi-catch clause still has a single parameter, but now a list of exception types, delimited by the vertical bar (|), can be specified as the types for this parameter. This list defines a union of alternatives that are the exception types which the multi-catch clause can handle. The statements in the body of the multi-catch clause will be executed when an object of any of the specified exception types is caught by the multi-catch clause.

The multiple catch clauses at (5) and (6) in Example 7.9 have been replaced with a multi-catch clause at (5) in Example 7.10:

Click here to view code image

catch (ArrayIndexOutOfBoundsException |                  // (5) multi-catch
       NumberFormatException ep) {
  System.out.println(ep);
  System.out.println(“Usage: java Average10 <sum of values> <no. of values>”);
}

The multi-catch clause in Example 7.10 is semantically equivalent to the two uni-catch clauses in Example 7.9, and we can expect the same program behavior in both examples.

Example 7.10 Using the Multi-catch Clause

Click here to view code image

// File: Average10.java
public class Average10 {
  public static void main(String[] args) {
    try {                                                      // (1)
      int sum = Integer.parseInt(args[0]);                     // (2)
      int numOfValues = Integer.parseInt(args[1]);             // (3)
printAverage(sum, numOfValues);                          // (4)
    } catch (ArrayIndexOutOfBoundsException |                  // (5) multi-catch
             NumberFormatException ep) {
      System.out.println(ep);
      System.out.println(“Usage: java Average10 <sum of values> <no. of values>”);
    } catch (IntegerDivisionByZero idbz) {                     // (6) uni-catch
      idbz.printStackTrace();
      System.out.println(“Exception handled in main().”);
    } finally {                                                // (7)
      System.out.println(“Finally done in main().”);
    }
    System.out.println(“Exit main().”);                        // (8)
  }
  public static void printAverage(int totalSum, int totalCount)
      throws IntegerDivisionByZero {
    // See Example 7.9.
  }
  public static int computeAverage(int sum, int count)
      throws IntegerDivisionByZero {
    // See Example 7.9.
  }
}

A few remarks are in order regarding the alternatives of a multi-catch clause. There should be no subtype–supertype relationship between any of the specified exception types in the alternatives of a multi-catch clause. The following multi-catch clause will not compile, as ArrayIndexOutOfBoundsException is a subtype of IndexOutOfBoundsException:

Click here to view code image

catch (IndexOutOfBoundsException |                  // Compile-time error!
       ArrayIndexOutOfBoundsException e) {
  // …
}

The parameter of a multi-catch clause is also considered to be implicitly final, and therefore cannot be assigned to in the body of the multi-catch clause. In a uni-catch clause, the parameter is considered to be effectively final if it does not occur on the left-hand side of an assignment in the body of the uni-catch clause.

Click here to view code image

try {
  // Assume appropriate code to throw the right exceptions.
} catch (NumberFormatException |
         IndexOutOfBoundsException e) {    // Parameter is final.
  e = new ArrayIndexOutOfBoundsException();// Compile-time error!
                                           // Cannot assign to final parameter e.
} catch (IntegerDivisionByZero idbz) {     // Parameter is effectively final.
  idbz.printStackTrace();
} catch (IOException ioe) {                // Parameter is not effectively final.
  ioe = new FileNotFoundException(“No file.”);
}

Disallowing any subtype–supertype relationship between alternatives and the parameter being final in a multi-catch clause or effectively final in a uni-catch clause allows the compiler to perform precise exception handling analysis.

The compiler also generates effective bytecode for a single exception handler corresponding to all the alternatives in a multi-catch clause, in contrast to generating bytecode for multiple exception handlers for uni-catch clauses that correspond to the multi-catch clause.

Posted on Leave a comment

Rethrowing Exceptions – Exception Handling

Rethrowing Exceptions

Rethrowing an exception refers to throwing an exception in the body of a catch clause. The catch clause catches an exception, but then throws this exception or another exception in its body. This allows an exception to be partially handled when it is caught the first time, and then again when the rethrown exception is caught later. Typically, the first exception handler is a common handler for the situation and the later exception handler is a more specific one.

Exception parameters that are explicitly, implicitly, or effectively final in catch clauses allow the compiler to perform improved analysis of exception handling in the code, especially when it comes to rethrowing exceptions.

For the examples in this section, it is important to keep in mind that the exception type IOException is the supertype of both EOFException and FileNotFoundException.

Example 7.11 illustrates how the compiler is able to identify unreachable code by precise analysis of rethrown exceptions that are either final or effectively final. The body of the try statement at (1) can only throw a FileNotFoundException that is caught by the catch clause at (3). This exception is effectively final in the catch clause at (3), as no assignment is made to it in the body of the catch clause. This exception is rethrown in the nested try statement at (4), but the catch clause at (6) of this try statement can only catch an EOFException. Since parameter ex is effectively final, it can only denote a FileNotFoundException, never an EOFException. The catch clause at (6) is unreachable, and the compiler flags an error.

Example 7.11 Precise Rethrowing of Exceptions

Click here to view code image

import java.io.EOFException;
import java.io.FileNotFoundException;
import java.io.IOException;

public class ExceptionAnalysis {
  public static void main(String[] args) throws IOException {
    try {                                // (1)
      throw new FileNotFoundException(); // (2)
    } catch (IOException ex) {           // (3)
      try {                        // (4) Nested try statement
        throw ex;                  // (5) Can only rethrow FileNotFoundException
      } catch (EOFException se) {  // (6) Compile-time error: clause unreachable
        System.out.println(“I am unreachable.”);
      }
}
  }
}

Example 7.12 illustrates how final or effectively final catch parameters allow more precise exceptions to be specified in the throws clause of a method. The thing to note is that the parameter e in the catch clause at (5) is not effectively final, as an assignment is made to the parameter at (6). All bets are off when the parameter is not final and the exception is rethrown. The throws clause must specify the same type as the type of the catch parameter, as shown at (3a). This has consequences for the caller method main(). Its try statement at (1) must include the catch clause at (2) to catch an IOException as well, or the compiler will flag an error about an uncaught checked exception. The contract of the checkIt() method allows for all exceptions that are either IOException or its subtypes.

If the assignment statement at (6) is commented out, the catch parameter e is effectively final in the catch body. The compiler can deduce that the rethrown exception can only be of type FileNotFoundException or EOFException. The throws clause of the checkIt() method can be made more specific, as at (3b). Note that the type of the catch parameter is the supertype IOException of the subtypes specified in the throws clause at (3b). Commenting out the assignment statement at (6) and uncommenting the more precise throws clause at (3b) has consequences for the caller method main() as well. The catch clause at (2) becomes unreachable, and the compiler issues a warning. Note also that the type of the exception parameter e at (5) is IOException, which is the supertype of the exception types specified in the throws clause. However, static analysis by the compiler is able to confirm that the exception parameter e can only denote objects of either FileNotFoundException or EOFException, but not of supertype IOException.

Example 7.12 Precise throws Clause

Click here to view code image

import java.io.EOFException;
import java.io.FileNotFoundException;
import java.io.IOException;
public class MorePreciseRethrow {
  public static void main(String[] args) {                        // (1)
    try {
      checkIt(1);
    } catch (FileNotFoundException fnfe) {
      System.out.println(“Check that the file exits.”);
    } catch (EOFException eofe) {
      System.out.println(“Check the contents of the file.”);
    } catch (IOException ioe) { // (2) mandatory with (3a), but compiler warning
                                //     that clause is unreachable with (3b).
      System.out.println(“This should never occur.”);
    }
  }
public static void checkIt(int value) throws IOException {      // (3a)
//public static void checkIt(int value)                           // (3b)
//    throws FileNotFoundException, EOFException {
    try {                                                         // (4)
      switch (value) {
        case 1:
          throw new FileNotFoundException(“File not found”);
        case 2:
          throw new EOFException(“End of file”);
        default:
          System.out.println(“OK”);
      }
    } catch (IOException e) {                                      // (5)
      System.out.println(e.getMessage());
      e = new EOFException(“End of file”);         // (6) not effectively final,
                                                   //     requires (3a).
                                                   //     When commented out,
                                                   //     can use (3b).
      throw e;
    }
  }
}

Program output with (3a) and (6) uncommented, and (3b) commented out:

Click here to view code image

File not found
Check the contents of the file.

Program output with (3a) and (6) commented out, and (3b) is uncommented:

Click here to view code image

File not found
Check that the file exits.

In summary, a throw statement in the body of a catch clause can throw a final or an effectively final exception parameter that has exception type E if all of the following conditions are satisfied:

  • Exception type E can be thrown in the body of the try statement with which the catch clause is associated.
  • Exception type E is assignment compatible with any of the exception types declared for the parameter in the catch clause.
  • Exception type E is not assignment compatible with any of the exception types declared for the parameters in any preceding catch clause in the same try statement.

In Example 7.13, the throw statements at (1), (2), and (3) all try to rethrow an exception that is effectively final in the body of the catch clause.

  • The throw statement at (1) in the main() method satisfies all the conditions. The try block throws an exception of the right type (EOFException). The exception thrown (EOFException) is assignment compatible with the type declared for the parameter in the catch clause (IOException). There is no preceding catch clause that handles the exception (EOFException).
  • The throw statement at (2) in the rethrowA() method cannot throw the exception, as the first condition is not satisfied: The try block does not throw an exception of the right type (EOFException). The compiler flags an error for the catch clause that is unreachable.
  • The throw statement at (3) in the rethrowB() method cannot throw the exception, as the third condition is not satisfied: A preceding catch clause can handle the exception (EOFException). The compiler flags a warning for the catch clause which is unreachable.

Example 7.13 Conditions for Rethrowing Final Exceptions

Click here to view code image

import java.io.EOFException;
import java.io.FileNotFoundException;
import java.io.IOException;
public class RethrowMe {
  public static void main(String[] args) throws EOFException {
    try {
      switch (1) {
        case 1: throw new FileNotFoundException(“File not found”);
        case 2: throw new EOFException(“End of file”);
        default: System.out.println(“OK”);
      }
    } catch (FileNotFoundException fnfe) {
      System.out.println(fnfe);
    } catch (IOException ioe) {
      throw ioe;                                                    // (1)
    }
  }
  public static void rethrowA() throws EOFException {
    try {
      // Empty try block.
    } catch (EOFException eofe) { // Compile-time error: exception not thrown
                                  //                     in try block.
      throw eofe;                                                   // (2)
    }
  }
  public static void rethrowB() throws EOFException {
    try {
      throw new EOFException(“End of file”);
    } catch (EOFException eofe) {
      System.out.println(eofe);
    } catch (IOException ioe) {   // Compile-time warning: unreachable clause
      throw ioe;                                                    // (3)
    }
  }
}

Posted on Leave a comment

Members with Package Access – Access Control

Members with Package Access

No access modifier implies package accessibility in this context. When no member access modifier is specified, the member is accessible only by other classes in the same package in which its class is declared. Even if its class is accessible in another package, the member is not accessible elsewhere. Package member access is more restrictive than protected member access.

In Example 6.8, the instance field pkgBool in an instance of the class Superclass1 has package access and is only accessible within pkg1, but not in any other packages— that is to say, it is accessible only by Client 1 and Client 2. Client 3 and Client 4 in pkg2 cannot access this field.

private Members

The private modifier is the most restrictive of all the access modifiers. Private members are not accessible by any other classes. This also applies to subclasses, whether they are in the same package or not. Since they are not accessible by their simple names in a subclass, they are also not inherited by the subclass. A standard design strategy for a class is to make all instance fields private and provide public get methods for such fields. Auxiliary methods are often declared as private, as they do not concern any client.

None of the clients in Figure 6.5 can access the private instance field privLong in an instance of the class Superclass1. This instance field is only accessible in the defining class—that is, in the class Superclass1.

Table 6.3 provides a summary of access modifiers for members in a class. References in parentheses refer to clients in Figure 6.5.

  

Table 6.3 Accessibility of Members in a Class (Non-Modular)

Member accessIn the defining class Superclass1In a subclass in the same package (Client 1)In a non-subclass in the same package (Client 2)In a subclass in a different package (Client 3)In a non-subclass in a different package (Client 4)
publicYesYesYesYesYes
protectedYesYesYesYesNo
packageYesYesYesNoNo
privateYesNoNoNoNo
Additional Remarks on Accessibility

Access modifiers that can be specified for a class member apply equally to constructors. However, when no constructor is specified, the default constructor inserted by the compiler implicitly follows the accessibility of the class.

Accessibility of members declared in an enum type is analogous to those declared in a class, except for enum constants that are always public and constructors that are always private. A protected member in an enum type is only accessible in its own package, since an enum type is implicitly final. Member declarations in an enum type are discussed in §5.13, p. 290.

In contrast, the accessibility of members declared in an interface is always implicitly public (§5.6, p. 238). Omission of the public access modifier in this context does not imply package accessibility.

Posted on Leave a comment

Scope Rules – Access Control

6.6 Scope Rules

Java provides explicit access modifiers to control the accessibility of members in a class by external clients, but in two areas access is governed by specific scope rules:

  • Class scope for members: how member declarations are accessed within the class
  • Block scope for local variables: how local variable declarations are accessed within a block

Class Scope for Members

Class scope concerns accessing members (including inherited ones) from code within a class. Table 6.4 gives an overview of how static and non-static code in a class can access members of the class, including those that are inherited. Table 6.4 assumes the following declarations:

Click here to view code image

class SuperClass {
  int instanceVarInSuper;
  static int staticVarInSuper;
void instanceMethodInSuper()      { /* … */ }
  static void staticMethodInSuper() { /* … */ }
  // …
}
class MyClass extends SuperClass {
  int instanceVar;
  static int staticVar;
  void instanceMethod()      { /* … */ }
  static void staticMethod() { /* … */ }
  // …
}

Table 6.4 Accessing Members within a Class

Member declarationsNon-static code in the class MyClass can refer to the member asStatic code in the class MyClass can refer to the member as
Instance variablesinstanceVar this.instanceVar instanceVarInSuper this.instanceVarInSuper super.instanceVarInSuperNot possible
Instance methodsinstanceMethod() this.instanceMethod() instanceMethodInSuper() this.instanceMethodInSuper() super.instanceMethodInSuper()Not possible
Static variablesstaticVar this.staticVar MyClass.staticVar staticVarInSuper this.staticVarInSuper super.staticVarInSuper MyClass.staticVarInSuper SuperClass.staticVarInSuperstaticVar MyClass.staticVar staticVarInSuper MyClass.staticVarInSuper SuperClass.staticVarInSuper
Static methodsstaticMethod() this.staticMethod() MyClass.staticMethod() staticMethodInSuper() this.staticMethodInSuper() super.staticMethodInSuper() MyClass.staticMethodInSuper() SuperClass.staticMethodInSuper()staticMethod() MyClass.staticMethod() staticMethodInSuper() MyClass.staticMethodInSuper() SuperClass.staticMethodInSuper()

The golden rule is that static code can only access other static members by their simple names. Static code is not executed in the context of an object, so the references this and super are not available. An object has knowledge of its class, so static members are always accessible in a non-static context.

Note that using the class name to access static members within the class is no different from how external clients access these static members.

The following factors can all influence the scope of a member declaration:

  • Shadowing of a field declaration, either by local variables (p. 354) or by declarations in the subclass (§5.1, p. 203)
  • Overriding an instance method from a superclass (§5.1, p. 196)
  • Hiding a static method declared in a superclass (§5.1, p. 203)

Within a class, references of the class can be declared and used to access all members in the class, regardless of their access modifiers. In Example 6.9, the method duplicateLight at (1) in the class Light has the parameter oldLight and the local variable newLight that are references of the class Light. Even though the fields of the class are private, they are accessible through the two references (oldLight and newLight) in the method duplicateLight(), as shown at (2), (3), and (4).

Example 6.9 Class Scope

Click here to view code image

class Light {
  // Instance variables:
  private int     noOfWatts;       // Wattage
  private boolean indicator;       // On or off
  private String  location;        // Placement
  // Instance methods:
  public void switchOn()  { indicator = true; }
  public void switchOff() { indicator = false; }
  public boolean isOn()   { return indicator; }
  public static Light duplicateLight(Light oldLight) {     // (1)
    Light newLight = new Light();
    newLight.noOfWatts = oldLight.noOfWatts;               // (2)
    newLight.indicator = oldLight.indicator;               // (3)
    newLight.location  = oldLight.location;                // (4)
    return newLight;
  }
}

Posted on Leave a comment

Compiling Code into Package Directories – Access Control

Compiling Code into Package Directories

Conventions for specifying pathnames vary on different platforms. In this chapter, we will use pathname conventions used on a Unix-based platform. While trying out the examples in this section, attention should be paid to platform dependencies in this regard—especially the fact that the separator characters in file paths for the Unix-based and Windows platforms are / and \, respectively.

As mentioned earlier, a package can be mapped on a hierarchical file system. We can think of a package name as a pathname in the file system. Referring to Example 6.1, the package name wizard.pandorasbox corresponds to the pathname wizard/ pandorasbox. The Java bytecode for all types declared in the source files Clown.java and LovePotion.java will be placed in the package directory with the pathname wizard/pandorasbox, as these source files have the following package declaration:

package wizard.pandorasbox;

The location in the file system where the package directory should be created is specified using the -d option (d for destination) of the javac command. The term destination directory is a synonym for this location in the file system. The compiler will create the package directory with the pathname wizard/pandorasbox (including any subdirectories required) under the specified location, and will place the Java bytecode for the types declared in the source files Clown.java and LovePotion.java inside the package directory.

Assuming that the current directory (.) is the directory /pgjc/work, and the four source code files in Figure 6.3a (see also Example 6.1) are found in this directory, the following command issued in the current directory will create a file hierarchy (Figure 6.3b) under this directory that mirrors the package structure in Figure 6.2, p. 327:

Click here to view code image

>
javac -d . Clown.java LovePotion.java Ailment.java Baldness.java

Note that two of the source code files in Figure 6.3a have multiple type declarations. Note also the subdirectories that are created for a fully qualified package name, and where the class files are located. In this command line, the space between the -d option and its argument is mandatory.

  

Figure 6.3 Compiling Code into Package Directories

The wildcard * can be used to specify all Java source files to be compiled from a directory. It expands to the names of the Java source files in that directory. The two commands below are equivalent to the command above.

>
javac -d . *.java
>
javac -d . ./*.java

We can specify any relative pathname that designates the destination directory, or its absolute pathname:

Click here to view code image

>
javac -d /pgjc/work Clown.java LovePotion.java Ailment.java Baldness.java

We can, of course, specify destinations other than the current directory where the class files with the bytecode should be stored. The following command in the current directory /pgjc/work will create the necessary packages with the class files under the destination directory /pgjc/myapp:

Click here to view code image

>
javac -d ../myapp Clown.java LovePotion.java Ailment.java Baldness.java

Without the -d option, the default behavior of the javac compiler is to place all class files directly under the current directory (where the source files are located), rather than in the appropriate subdirectories corresponding to the packages.

The compiler will report an error if there is any problem with the destination directory specified with the -d option (e.g., if it does not exist or does not have the right file permissions).

Posted on Leave a comment

Access Modifiers – Access Control

6.5 Access Modifiers

In this section, we discuss accessibility of top-level type declarations that can be encapsulated into packages and accessibility of members that can be encapsulated in a top-level type declaration. A top-level reference type is a reference type (class, interface, enum, record) that is not declared inside another reference type.

Access modifiers are sometimes also called visibility modifiers.

Access Modifiers for Top-Level Type Declarations

The access modifier public can be used to declare top-level reference types that are accessible from everywhere, both from inside their own package and from inside other packages. If the access modifier is omitted, the reference types can be accessed only in their own package and not in any other packages—that is, they have package access, also called package-private or default access.

The packages shown in Figure 6.2, p. 327, are implemented by the code in Example 6.7. Class files with Java bytecode for top-level type declarations are placed in designated packages using the package statement. A top-level type declaration from one package can be accessed in another packages either by using the fully qualified name of the type or by using an import statement to import the type so that it can be accessed by its simple name.

Example 6.7 Access Modifiers for Top-Level Reference Types

Click here to view code image

// File: Clown.java
package wizard.pandorasbox;                  // Package declaration
import wizard.pandorasbox.artifacts.Ailment; // Importing class Ailment
public class Clown implements Magic {        // (1)
  LovePotion tlc;                            // Class in same package
  Ailment problem;                           // Simple class name
  Clown() {
    tlc = new LovePotion(“passion”);
    problem = new Ailment(“flu”);            // Simple class name
  }
  @Override public void levitate()  {        // (2)
    System.out.println(“Levitating”);
  }
  public void mixPotion()   { System.out.println(“Mixing ” + tlc); }
  public void healAilment() { System.out.println(“Healing ” + problem); }
  public static void main(String[] args) {
    Clown joker = new Clown();
    joker.levitate();
    joker.mixPotion();
    joker.healAilment();
  }
}
interface Magic { void levitate(); }         // (3)

Click here to view code image

// File: LovePotion.java
package wizard.pandorasbox;                  // Package declaration

public class LovePotion {                    // (4) Accessible outside package
  String potionName;
  public LovePotion(String name) { potionName = name; }
  public String toString()       { return potionName; }
}

Click here to view code image

// File: Ailment.java
package wizard.pandorasbox.artifacts;        // Package declaration

public class Ailment {                       // Accessible outside package
  String ailmentName;
  public Ailment(String name) { ailmentName = name; }
  public String toString() { return ailmentName; }
}

Click here to view code image

// File: Baldness.java
package wizard.spells;                       // Package declaration

import wizard.pandorasbox.*;                 // Redundant
import wizard.pandorasbox.artifacts.*;       // Import of subpackage

public class Baldness extends Ailment {      // Simple name for Ailment
  wizard.pandorasbox.LovePotion tlcOne;      // Fully qualified name
  LovePotion tlcTwo;                         // Class in same package
  Baldness(String name) {
    super(name);
    tlcOne = new wizard.pandorasbox.         // Fully qualified name
                 LovePotion(“romance”);
    tlcTwo = new LovePotion();               // Class in same package
  }
}
class LovePotion /* implements Magic */ {    // (5) Magic is not accessible
  // @Override public void levitate() {}     // (6) Cannot override method
}

Compiling and running the program from the current directory gives the following results:

Click here to view code image

>
javac -d . Clown.java LovePotion.java Ailment.java Baldness.java
>
java wizard.pandorasbox.Clown

Levitating
Mixing passion
Healing flu

In Example 6.7, the class Clown at (1) and the interface Magic at (3) are placed in a package called wizard.pandorasbox. The public class Clown is accessible from everywhere. The Magic interface has package accessibility, and can only be accessed within the package wizard.pandorasbox. It is not accessible from other packages, not even from subpackages.

The class LovePotion at (4) is also placed in the package called wizard.pandorasbox. The class has public accessibility, and is therefore accessible from other packages. The two files Clown.java and LovePotion.java demonstrate how several compilation units can be used to group classes in the same package, as the type declarations in these two source files are placed in the package wizard.pandorasbox.

In the file Clown.java, the class Clown at (1) implements the interface Magic at (3) from the same package. We have used the annotation @Override in front of the declaration of the levitate() method at (2) so that the compiler can aid in checking that this method is declared correctly as required by the interface Magic.

In the file Baldness.java, the class LovePotion at (5) wishes to implement the interface Magic at (3) from the package wizard.pandorasbox, but this is not possible, although the source file imports from this package. The reason is that the interface Magic has package accessibility, and can therefore only be accessed within the package wizard.pandorasbox. The method levitate() of the Magic interface therefore cannot be overridden in class LovePotion at (6).

Table 6.2 summarizes accessibility of top-level reference types in a package. Just because a reference type is accessible does not necessarily mean that members of the type are also accessible. Accessibility of members is governed separately from type accessibility, as explained in the next subsection.

Table 6.2 Access Modifiers for Top-Level Reference Types (Non-Modular)

ModifiersTop-level types
No modifierAccessible in its own package (package accessibility)
publicAccessible anywhere
Posted on Leave a comment

Access Modifiers for Class Members – Access Control

Access Modifiers for Class Members

By specifying member access modifiers, a class can control which information is accessible to clients (i.e., other classes). These modifiers help a class define a contract so that clients know exactly which services are offered by the class.

The accessibility of a member in a class can be any one of the following:

  • public
  • protected
  • package access (also known as package-private and default access), when no access modifier is specified
  • private

In the following discussion of access modifiers for members of a class, keep in mind that the member access modifier has meaning only if the class (or one of its subclasses) is accessible to the client. Also, note that only one access modifier can be specified for a member.

The discussion in this subsection applies to both instance and static members of top-level classes.

In UML notation, when applied to member names the prefixes +, #, and – indicate public, protected, and private member access, respectively. No access modifier indicates package access for class members.

The package hierarchy shown in Figure 6.5 is implemented by the code in Example 6.8. The class Superclass1 at (1) in pkg1 has two subclasses: Subclass1 at (3) in pkg1 and Subclass2 at (5) in pkg2. The class Superclass1 in pkg1 is used by the other classes (designated as Client 1 to Client 4) in Figure 6.5.

  

Figure 6.5 Accessibility of Class Members

Accessibility of a member is illustrated in Example 6.8 by the four instance fields defined in the class Superclass1 in pkg1, where each instance field has a different accessibility. These four instance fields are accessed in a Superclass1 object created in the static method printState1() declared in five different contexts:

  • Defining class: The class in which the member is declared—that is, pkg1.Superclass1 in which the four instance fields being accessed are declared
  • Client 1: From a subclass in the same package—that is, pkg1.Subclass1
  • Client 2: From a non-subclass in the same package—that is, pkg1.NonSubclass1
  • Client 3: From a subclass in another package—that is, pkg2.Subclass2
  • Client 4: From a non-subclass in another package—that is, pkg2.NonSubclass2

Example 6.8 Accessibility of Class Members

Click here to view code image

// File: Superclass1.java
package pkg1;

import static java.lang.System.out;
public class Superclass1 {                       // (1)
  // Instance fields with different accessibility:
  public    int     pubInt   = 2017;
  protected String  proStr   = “SuperDude”;
            boolean pgkBool  = true;
  private   long    privLong = 0x7777;
  public static void printState1() {             // (2)
    Superclass1 obj1 = new Superclass1();
    out.println(obj1.pubInt);
    out.println(obj1.proStr);
    out.println(obj1.pgkBool);
    out.println(obj1.privLong);
  }
}
// Client 1
class Subclass1 extends Superclass1 {            // (3)
  public static void printState1() {

    Superclass1 obj1 = new Superclass1();
    out.println(obj1.pubInt);
    out.println(obj1.proStr);
    out.println(obj1.pgkBool);
    out.println(obj1.privLong);  // Compile-time error! Private access.
  }
}
// Client 2
class NonSubclass1 {                             // (4)
  public static void printState1() {

    Superclass1 obj1 = new Superclass1();
    out.println(obj1.pubInt);
    out.println(obj1.proStr);
    out.println(obj1.pgkBool);
    out.println(obj1.privLong);  // Compile-time error! Private access.
  }
}

Click here to view code image

// File: Subclass2.java
package pkg2;
import pkg1.Superclass1;
import static java.lang.System.out;
// Client 3
public class Subclass2 extends Superclass1 {     // (5)
  public static void printState1() {             // (6)
    Superclass1 obj1 = new Superclass1();        // Object of Superclass1
    out.println(obj1.pubInt);
    out.println(obj1.proStr);   // (7) Compile-time error! Protected access.
    out.println(obj1.pgkBool);  // Compile-time error! Package access.
    out.println(obj1.privLong); // Compile-time error! Private access.
  }
  public static void printState2() {             // (8)
    Subclass2 obj2 = new Subclass2();            // (9) Object of Subclass2
    out.println(obj2.pubInt);
    out.println(obj2.proStr);   // (10) OK! Protected access.
    out.println(obj2.pgkBool);  // Compile-time error! Package access.
    out.println(obj2.privLong); // Compile-time error! Private access.
  }
}
// Client 4
class NonSubclass2 {                             // (11)
  public static void printState1() {
    Superclass1 obj1 = new Superclass1();
    out.println(obj1.pubInt);
    out.println(obj1.proStr);   // Compile-time error! Protected access.
    out.println(obj1.pgkBool);  // Compile-time error! Package access.
    out.println(obj1.privLong); // Compile-time error! Private access.
  }
}