2

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.

Lii
  • 11,553
  • 8
  • 64
  • 88
T-Bag
  • 10,916
  • 3
  • 54
  • 118
  • 1
    I'm not an expert in Java, but `throws Exception` just declares that a method _can_ throw a certain exception, not that it necessarily _will_. Your implementation of `f()` doesn't throw an exception. – Tim Biegeleisen Mar 02 '17 at 08:42
  • Since `C.f` is `A.f`, it cannot throw `InterruptedException`. Since `C.f` is `B.f`, it cannot throw `FileNotFoundException`. Therefore `C.f` can't throw any checked exception, so you don't have to catch any. – khelwood Mar 02 '17 at 08:42
  • No , If you put B instead of C in statement C obj = new Test(); you will get an error – T-Bag Mar 02 '17 at 08:49
  • Interesting observation! – Lii Mar 02 '17 at 08:53
  • @TimBiegeleisen It's not about the implementation (`Test.f`). It's about the interface method `C.f`. – khelwood Mar 02 '17 at 09:19
  • @khelwood I thought that perhaps the OP thought that `throws Exception` means that the method always throws an exception. On a side note, good to learn that implementation class can only throw the _intersection_ (and not the union) of its parent interfaces' exceptions. – Tim Biegeleisen Mar 02 '17 at 09:23

3 Answers3

2

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
    }
}
iavanish
  • 509
  • 3
  • 8
  • Contrary to what you seem to suggest, the method `B.f` is not allowed to throw neither `FileNotFoundException` nor `InterruptedException`. – Lii Mar 02 '17 at 09:00
  • `C.f()` cannot throw any exceptions, as demonstrated by the code in the question. – khelwood Mar 02 '17 at 09:09
  • Still not really answering the question, because you're talking about the _implementation_ not throwing exceptions; but the issue is that `C.f()` cannot throw any checked exceptions (without even looking at the implementation). – khelwood Mar 02 '17 at 09:12
  • In the example given in the question, C.f() cannot throw any checked exception. But in case A.f() and B.f() threw the same exception, then C.f() also needed to throw that exception. However, the implementation doesn't need to throw any exception even in that case. I was trying to give a generalized answer. I have edited my answer to clarify this. – iavanish Mar 02 '17 at 09:34
2

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.

khelwood
  • 55,782
  • 14
  • 81
  • 108
0

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.