Exception restrictions | CodeGuru

Exception restrictions

Bruce Eckel’s Thinking in Java Contents | Prev | Next When you override a method, you can throw only the exceptions that have been specified in the base-class version of the method. This is a useful restriction, since it means that code that works with the base class will automatically work with any object derived […]

Written By
CodeGuru Staff
CodeGuru Staff
Mar 1, 2001
4 minute read
CodeGuru content and product recommendations are editorially independent. We may make money when you click on links to our partners. Learn More

When


you override a method, you can throw only the exceptions that have been


specified in the base-class version of the method. This is a useful


restriction, since it means that code that works with the base class will


automatically work with any object derived from the base class (a fundamental


OOP concept, of course), including exceptions.

This


example demonstrates the kinds of restrictions imposed (at compile time) for


exceptions:

//: StormyInning.java
// Overridden methods may throw only the 
// exceptions specified in their base-class 
// versions, or exceptions derived from the 
// base-class exceptions.
 
class BaseballException extends Exception {}
class Foul extends BaseballException {}
class Strike extends BaseballException {}
 
abstract class Inning {
  Inning() throws BaseballException {}
  void event () throws BaseballException {
   // Doesn't actually have to throw anything
  }
  abstract void atBat() throws Strike, Foul;
  void walk() {} // Throws nothing
}
 
class StormException extends Exception {}
class RainedOut extends StormException {}
class PopFoul extends Foul {}
 
interface Storm {
  void event() throws RainedOut;
  void rainHard() throws RainedOut;
}
 
public class StormyInning extends Inning
    implements Storm {
  // OK to add new exceptions for constructors,
  // but you must deal with the base constructor
  // exceptions:
  StormyInning() throws RainedOut,
    BaseballException {}
  StormyInning(String s) throws Foul,
    BaseballException {}
  // Regular methods must conform to base class:
//! void walk() throws PopFoul {} //Compile error
  // Interface CANNOT add exceptions to existing
  // methods from the base class:
//! public void event() throws RainedOut {}
  // If the method doesn't already exist in the
  // base class, the exception is OK:
  public void rainHard() throws RainedOut {}
  // You can choose to not throw any exceptions,
  // even if base version does:
  public void event() {}
  // Overridden methods can throw 
  // inherited exceptions:
  void atBat() throws PopFoul {}
  public static void main(String[] args) {
    try {
      StormyInning si = new StormyInning();
      si.atBat();
    } catch(PopFoul e) {
    } catch(RainedOut e) {
    } catch(BaseballException e) {}
    // Strike not thrown in derived version.
    try {
      // What happens if you upcast?
      Inning i = new StormyInning();
      i.atBat();
      // You must catch the exceptions from the
      // base-class version of the method:
    } catch(Strike e) {
    } catch(Foul e) {
    } catch(RainedOut e) {
    } catch(BaseballException e) {}
  }
} ///:~ 

In


Inning

,


you can see that both the constructor and the


event( )

method say they will throw an exception, but they never do. This is legal


because it allows you to force the user to catch any exceptions that you might


add in overridden versions of


event( )

.


The same idea holds for


abstract

methods, as seen in


atBat( )

.

The


interface
Storm

is interesting because it contains one method (


event( )

)that


is defined in


Inning

,


and one method that isn’t. Both methods throw a new type of exception,


RainedOut

.


When


StormyInning
extends
Inning

and


implements
Storm

,


you’ll see that the


event( )

method in


Storm
cannot

change the exception interface of


event( )

in


Inning

.


Again, this makes sense because otherwise you’d never know if you were


catching the correct thing when working with the base class. Of course, if a


method described in an


interface

is not in the base class, such as


rainHard( )

,


then


there’s no problem if it throws exceptions.

The


restriction on exceptions does not apply to

constructors.
You can see in
StormyInning
that
a constructor can throw anything it wants, regardless of what the base-class
constructor throws. However, since a base-class constructor must always be
called one way or another (here, the default constructor is called
automatically), the derived-class constructor must declare any base-class
constructor exceptions in its exception specification.

The


reason


StormyInning.walk( )

will not compile is that it throws an exception, while


Inning.walk( )

does not. If this was allowed, then you could write code that called


Inning.walk( )

and that didn’t have to handle any exceptions, but then when you


substituted an object of a class derived from


Inning

,


exceptions would be thrown so your code would break. By forcing the


derived-class methods to conform to the exception specifications of the


base-class methods, substitutability of objects is maintained.

The


overridden


event( )

method shows that a derived-class version of a method may choose to not throw


any exceptions, even if the base-class version does. Again, this is fine since


it doesn’t break any code that is written assuming the base-class version


throws exceptions. Similar logic applies to


atBat( )

,


which throws


PopFoul

,


an exception that is derived from


Foul

thrown by the base-class version of


atBat( )

.


This way, if someone writes code that works with


Inning

and calls


atBat( )

,


they must catch the


Foul

exception. Since


PopFoul

is derived from


Foul

,


the exception handler will also catch


PopFoul

.

The


last point of interest is in


main( )

.


Here you can see that if you’re dealing with exactly a


StormyInning

object, the compiler forces you to catch only the exceptions that are specific


to that class, but if you upcast to the base type then the compiler (correctly)


forces you to catch the exceptions for the base type. All these constraints


produce much more robust exception-handling code.


[43]

It’s


useful to realize that although exception specifications are enforced by the


compiler during inheritance, the exception specifications are not part of the


type of a method, which is comprised of only the method name and argument


types. Therefore, you cannot overload methods based on exception


specifications. In addition, because an exception specification exists in a


base-class version of a method doesn’t mean that it must exist in the


derived-class version of the method, and this is quite different from


inheriting the methods (that is, a method in the base class must also exist in


the derived class). Put another way, the “exception specification


interface” for a particular method may narrow during inheritance and


overriding, but it may not widen – this is precisely the opposite of the


rule for the class interface during inheritance.



[43]

ANSI/ISO C++ added similar constraints that require derived-method exceptions


to be the same as, or derived from, the exceptions thrown by the base-class


method. This is one case in which C++ is actually able to check exception


specifications at compile time.

Contents

|

Prev

|

Next
CodeGuru Logo

CodeGuru covers topics related to Microsoft-related software development, mobile development, database management, and web application programming. In addition to tutorials and how-tos that teach programmers how to code in Microsoft-related languages and frameworks like C# and .Net, we also publish articles on software development tools, the latest in developer news, and advice for project managers. Cloud services such as Microsoft Azure and database options including SQL Server and MSSQL are also frequently covered.

Property of TechnologyAdvice. © 2026 TechnologyAdvice. All Rights Reserved

Advertiser Disclosure: Some of the products that appear on this site are from companies from which TechnologyAdvice receives compensation. This compensation may impact how and where products appear on this site including, for example, the order in which they appear. TechnologyAdvice does not include all companies or all types of products available in the marketplace.