Reputation: 11140
I am working on checked Exceptions and in between found a case where compiler is not showing any error on checked exception and is working fine.
import java.io.FileNotFoundException;
interface A {
void f() throws FileNotFoundException;
}
interface B {
void f() throws InterruptedException;
}
interface C extends A, B {
}
public class Test implements C {
public void f() {
System.out.println("Hello world");
}
public static void main(String[] args) {
C obj = new Test();
obj.f(); // Working fine and printing "Hello World"
}
}
Kindly let me know the reason, I googled it a lot but did not find anything.
Upvotes: 2
Views: 398
Reputation: 1
The set of checked exceptions that a method can throw is the intersection of the sets of checked exceptions that it is declared to throw in all applicable types, not the union [JLS 15.12.2.5]. The compiler only check the exceptions in the method declaration.
interface InterfaceA{
void f() throws AException;
}
interface InterfaceB{
void f() throws BException;
}
interface InterfaceC extends InterfaceA, InterfaceB {
void f(); //correct one
void f() throws AException; //cannot pass compile
void f() throws BException; //cannot pass compile
void f() throws AException, BException; //cannot pass compile
}
The method f() in InterfaceC can only throws the exception in the intersection of {AException} and {BException}, so it is none. That rule limits the exceptions a method can thorws.
interface InterfaceA{
void f() throws AException, CException;
void g() throws AException, CException;
}
interface InterfaceB{
void f() throws BException, CException;
void g() throws BException, CException;
}
interface InterfaceC extends InterfaceA, InterfaceB {
void g();
}
public class C implement InterfaceC {
public void f() {}
public void g() {}
public static void main(String[] args) {
InterfaceC interfaceC = new C();
C c = new C();
interfaceC.f(); //cannot pass compile, must handle Exception CException
c.f(); //compile passed
interfaceC.g(); //compile passed
}
}
The method f() in InterfaceC is not declared, so this method is auto-generated with the exception list of {CException}, the intersection of {AException, CException} from InterfaceA and {BException, CException} from InterfaceB. the method g() in IntrefaceC and method f() in class C are both declared without any Exception, so no exception handling is need to pass the compile.
Upvotes: 0
Reputation: 59185
This question has been correctly answered, but I wanted to add some clarification. Various comments and answers have suggested that in the code
C obj = new Test();
obj.f();
that the reason you don't need to handle any checked exceptions is "because the implementation doesn't throw any checked exceptions". This is not the issue. The implementation is Test.f()
. But obj.f()
is calling C.f()
. The compiler is not looking at the implementation at compile-time. It's looking at the specification in C
.
You can demonstrate the same compiler behaviour by writing
void doThing(C obj) {
obj.f();
}
This can be compiled without even writing a class that implements C
.
Now doThing
doesn't have any knowledge of what implementation of f
is being called, but it still knows that it won't throw any checked exception, because C.f()
cannot throw a checked exception not declared by A.f()
and B.f()
. The possible set of checked exceptions thrown by C.f()
is the intersection of the sets of checked exceptions thrown by its two superinterfaces.
Upvotes: 2
Reputation: 509
In object oriented terms, when you program to an interface (i.e. implement an interface or extend an interface), you can not be more restrictive than the interface. However, you can be less restrictive. So, in your example, the interface's method may or may not throw a FileNotFoundException and/or InterruptedException, but your implementation' method will not throw an exception. Note that if your implementation's method throws a FileNotFoundException and/or InterruptedException, it is perfectly alright, but if it throws some other exception it won't be allowed. This means your implementation can be equal to or less restrictive than the interface, but it can not be more restrictive.
Also note, when someone uses your method f() through a variable of the extending interface type (C) or through a variable of class' type (Test), they don't need to handle the exception. However, if they use your method f() through a variable of interface types A or B, they need to handle the exception.
A obj = new Test();
obj.f(); // need to handle FileNotFoundException
B obj = new Test();
obj.f(); // need to handle InterruptedException
C obj = new Test();
obj.f(); // don't need to handle any of the exceptions
Test obj = new Test();
obj.f(); // don't need to handle any of the exceptions
More clarifications: The reason why C.f() doesn't throw an exception is that it's parent interfaces throw different exceptions. So, according to the argument "interface's implementation or extension can not be more restrictive but can be less restrictive", the only way for C.f() to be less restrictive is by not throwing an exception. Otherwise, it will be more restrictive than at least one of it's parent interfaces. On the other hand, if both of C's parents threw the same exceptions, then C.f() also needed to throw those exceptions. However, the implementation can still choose to not throw an exception.
import java.io.FileNotFoundException;
interface A{
void f() throws FileNotFoundException;
}
interface B {
void f() throws FileNotFoundException;
}
interface C extends A, B {
}
public class Test implements C {
public void f() {
System.out.println("Hello world");
}
public static void main(String[] args) {
C obj = new Test();
obj.f(); // Compilation error, unhandled exception
Test obj = new Test();
obj.f(); // No error
}
}
Upvotes: 2