Exception Handling and Parameterized Polymorphism
Theory
Exception handling:
Runtime Error: Runtime errors occur while a program is running if the JVM detects an operation that is impossible to carry out. Example:
- accessing an unavailable array index
- entering a double when program wants int
When a program runs into a runtime error, the program terminates abnormally.
Exception: An object representing
- an error or
- an abnormal condition
that prevents execution from proceeding normally. JVM or Java Programs – can throw an exception.
Exception handling: enables a program to
deal with the exceptional situations and
continue its normal execution.
Exceptions are thrown from a method definition. The caller of the method can catch and handle the exception.
Syntax for throwing and handling of exception is as follows:
try{
Code to run:
A statement or a method definition that may throw an exception.
More code to run
}
catch(type ex){
Code to process the exception
}
Types:
System Errors: generated by JVM and
- represented by Error class
Exceptions: caused by the java program and external circumstances
- represented by Exception class
Runtime Exceptions: caused by programming errors, such as bad casting, accessing an out-of-bounds array, and numeric errors
- represented by RuntimeException class
RuntimeException, Error and their subclasses are known as unchecked exceptions.
Unchecked exceptions can occur anywhere in a program and are unrecoverable
Java does not mandate that you write code to catch or declare unchecked exceptions.
All other exceptions are known as checked exceptions - the compiler forces the programmer to check and deal with the exceptions.
Checked exceptions that can be thrown in a method must be caught and handled
OR declared in the throws clause
Every method must state the types of checked exceptions it might throw. This is known as declaring exceptions. If a method does not declare exceptions in the superclass, you cannot override it to declare exceptions in the subclass
Syntax of throws:
- If a method is capable of causing an exception that it does not handle, it must specify this behavior so that callers of the method can guard themselves against that exception.
type method-name (parameter-list) throws exception-list
{
// body of method
}
- It is not applicable for Error or RuntimeException, or any of their subclasses.
Throwing Exception: When the program detects an error, the program can create an instance of an appropriate exception type and throw it. This is known as throwing an exception. Here is an example,
throw new TheException();
or
TheException ex = new TheException(); throw ex;
Syntax of throw:
- It is possible for your program to throw an exception explicitly throw ThrowableInstance
- Here, ThrowableInstance must be an object of type Throwable or a subclass Throwable.
Catching exception:
On executing try block:
- If no exceptions, the catch blocks are skipped.
- If one of the statements inside the try block throws an exception,
- Skip remaining statements in the try block
- Start finding the code to handle the exception by propagating the exception backward through a chain of method calls, starting from the current method.
- Visit previous caller: Examine each catch block in order to match exception type.
- If handler found, object is assigned to the variable declared, and the code in the catch block is executed.
- If no handler is found, Java exits this method, passes the exception to the method that invoked the method, and continues the same process to find a handler in subsequent called method. The process of finding a handler is called catching an exception
try{
statements;
//statements that may throw exceptions
}
catch (Exception1 exvar1){
handler for exception1;
}
catch (Exception2 exvar2){
handler for exception2;
}
catch (Exception1 exvar3){
handler for exception3;
}
Generics/Parameterized Polymorphism:
Generics deals with type-safe objects. It makes the type errors identifiable at compile-time. Consider an example to create a Box object:
public class OldBox
{
Object data;
public OldBox(Object data)
{
this.data = data;
}
public Object getData()
{
return data;
}
}
As the all the classes have the parent as Object we can create multiple types of Boxes as follows:
OldBox intBox = new OldBox(42);
int x = (Integer) intBox.getData();
OldBox strBox = new OldBox(“Hi”);
String s = (String) strBox.getData();
int y = (Integer) strBox.getData();
intBox = strBox;
You see that an String type Box object is assigned to an Integer type object which produes the error: ClassCastException! Compiles but fails at runtime.
Parametrized Classes:
A Naïve solution is to create independent class for each specific Box type which demands the development of infinite number of classes. Hence, using the analogy as methods which generalize the inputs through its parameters, we can design parametrized classes. So, the example gets modified as follows:
public class Box <E>
{
E data;
public Box(E data)
{
this.data = data;
}
public E getData()
{
return data;
}
}
This parametrized class can be invoked as follows:
Box intBox = new Box(42);
int x = intBox.getData();//no cast needed
Box strBox = new Box(“Hi”);
String s = strBox.getData();//no cast needed
The Runtime errors get converted to compile-time errors. The usage of generics in Java are:
Particularly useful for “container” classes. Containers hold but do not process data
All collections framework classes in Java 5.0 defined using generics. See the Java 5.0 API documentation.
The parametrized class has the following features:
- It can take multiple parameters. Syntax: public class classname<A,B,C,…>{ … }
- Subclassing parameterized classes allowed. Example: ``` /* Extending a particular type */
class IntBox extends Box { … } Or
/* Extending a parameterized type */
class SpecialBox extends Box { …}
- A parameterized class is a type just like any other class. It can be used in method input types and return types, Example:
Box<String> aMethod(int i, Box<Integer> b) { … }
Bounded Parameterized Types:
Sometimes we want restricted parameterization of classes. We want a box, called MathBox that holds only Number objects. We can’t use Box<E> because E could be anything. We want E to be a subclass of Number.
public class MathBox extends Box
{
public MathBox(E data)
{
super(data);
}
public double sqrt()
{
return Math.sqrt(getData().doubleValue());
}
}
The <E extends Number> syntax means that the type parameter of MathBox must be a subclass of the Number class. We say that the type parameter is bounded.
Inside a parameterized class, the type parameter serves as a valid type. So the following is valid.
public class OuterClass<T>
{
private class InnerClass<E extends T>
{ … }
…
}
Syntax note: The <A extends B> syntax is valid even if B is an interface.
Java allows multiple inheritance in the form of implementing multiple interfaces. So multiple bounds may be necessary to specify a type parameter. The following syntax is used then:
Class clasname<T extends A & B &C &….>{ … }
Parametrized Methods:
We can fix the type used inn methods by parametrizing the class. Here we have locked the type used in methods.
public class Bar
{
//Bar is parameterized
public T aMethod(T x)
{
return x;
}
public static void main(String[] args)
{
Bar bar = new Bar();
int k = bar.aMethod(5); String s = bar.aMethod("abc"); //Compilation error here
}
}
Now, if we do not fix the type in class then can vary the type in methods.
public class Bar
{
//Bar is not parameterized
public
{
return x;
}
public static void main(String[] args)
{
Bar bar = new Bar();
int k = bar.aMethod(5); String s = bar.aMethod("abc"); //No Compilation error
}
}
Upper Bounded Wildcards in Parameterized Types:
We start to run into some new issues when we do some things that seem “normal”. For instance, the following seems reasonable:
Box<Number> numBox = new Box<Integer> (31);
Compiler comes back with an “Incompatible Type” error message. This is because numBox can hold only a Number object and nothing else, not even an object of type Integer which is a subclass of Number. The type of numBox we desire is “a Box of any type which extends Number”.
Box<? extends Number> numBox = new Box<Integer> (31);
Similarly, we can also add upper bound in method parameters also.
public class Box
{
public void copyFrom(Box b)
{
this.data = b.getData();//b.getData() is a subclass of this.data
}
}
is called “upper bounded wildcard” because it defines a type that is bounded by the superclass E. Lower Bounded Wildcards in Parameterized Types:
Suppose we want to write copyTo() that copies data in the opposite direction of copyFrom(). copyTo() copies data from the host object to the given object. This can be done as:
public void copyTo(Box
{
b.data = this.getData();
}
Above code is fine as long as b and the host are boxes of exactly same type.
But b could be a box of an object that is a superclass of E.
This can be expressed as:
public void copyTo(Box<? Super E> b)
{
b.data = this.getData(); //b.data() is a superclass of this.data()
}
<? Super E> is called a “lower bounded wildcard” because it defines a type that is bounded by the subclass E.
Unbounded Wildcards:
Use unbounded wildcards when any type parameter works. is used to specify unbounded wildcards. The following are legal statements.
Box<?> b1 = new Box<Integer> (31);
Box<?> b2 = new Box<String> (“Hi”);
b1 = b2;
Box<?> is a superclass of Box<T> for any T. Unbounded wildcards are useful when writing code that is completely independent of the parameterized type.
Wildcard capture: The compiler can figure out exactly what type b1 is above from the right hand side of the assignments. This “capturing” of type information means:
1. The type on the left hand doesn’t need to be specified.
2. The compiler can do additional type checks because it knows the type of b1.