7

Consider the following interface:

public interface Generator {
    String generate() throws IOException;
}

and the following implementation:

public class EmptyStringGenerator implements Generator {
    @Override
    public String generate() {
        return "";
    }
}

Note that I omitted the throws IOException part of the signature specified in the Generator interface. Yet there is no compiler error, no compiler warning, not even the @Override annotation complains.

I am aware that this is working as intended. I would, however, like to know the intent behind this. If my method does not actually throw an IOException, it would be fine to just not throw it, I do not have to remove it from the signature. But if I do remove it from my method signature in EmptyStringGenerator, I am forcing all current and future subclasses of this class to forego the possibility of throwing an exception that is actually specified in the interface.

This, to me, sounds like a feature that does not really bring you any benefit (apart from saving a couple of keystrokes, which is not really a benefit at all), but has the potential to be a terrible mistake, when actually used.

So my question, effectively, is this: What is the point of omitting throws exceptions in derived classes? What is the problem that this possibility solves? Why is this allowed?

UPDATE

For people asking "but where is the harm in that?", here is my example from one of my comments. It is not far-fetched, by the way, because that is exactly what I am dealing with right now:

Programmer A specifies interface I. Programmer B writes implementation class X, but forgets to add the throws. He also never notices, because there is not even a warning being thrown here. Programmer C writes implementation class Y, inheriting from class X, he even specifically also wants to put the throws there, because he is gonna throw. But even though the interface stipulates it, he is now not allowed to do so anymore, because of B's oversight. He is effectively not allowed to use that exception here anymore. That's a pretty big harm. Especially if class X is not under your control.

Jan Dörrenhaus
  • 6,581
  • 2
  • 34
  • 45

3 Answers3

1

If you omit the throws exception in derived classes you can call the method of the derived class without having to catch the exception.

You make sure that subclasses of EmptyStringGenerator also don't throw an exception. Otherwise it would not be sure for the compiler if the method call can cause a checked exception which the code must handle. In that case the throws wouldn't make sense at all.

Jimmy T.
  • 4,033
  • 2
  • 22
  • 38
  • If I have an interface, chances are, I will call my method through that interface. If I call the derived class directly, what would be the point of the interface? – Jan Dörrenhaus Sep 11 '13 at 11:59
  • @JanDoerrenhaus An interface only gives you access to a restriced set of methods. And also interfaces can have super- and sub-interfaces. – Jimmy T. Sep 11 '13 at 12:01
  • An interface gives me the possibility to switch implementations behind the scenes. That is its main functionality, not restricting access to some methods. I'm sorry, but this is not helping me at all. – Jan Dörrenhaus Sep 11 '13 at 12:03
  • @JanDoerrenhaus An interface hide implemention-specific methods so you can switch the implementation. It is restricting the set of methods. But I know that the purpose of an interface is not to restict access to some methods. – Jimmy T. Sep 11 '13 at 12:04
0

Based on your class decleration, campare these two conditions:

    EmptyStringGenerator generator = new EmptyStringGenerator();
    generator.generate();

if you create an instance of EmptyStringGenerator like above, Since you omit the throws in EmptyStringGenerator, the above code indicates you are using the class itself, which of course has nothing to do with the interface, so the code works just fine.

but if you intend to use the interface instead of the class, like this:

    Generator generator = new EmptyStringGenerator();
    generator.generate();

than the compiler would actually remind you that there is an unhandled exception, you must handle it with a try-catch or throw it, otherwise the code will not compile.

if you use any subclass of EmptyStringGenerator in the latter manner, the same compile error will occur. So omitting the throw does not actually rlease you from handling the exception. It's only natural not to throw exception in a class and its subclasses when there is none to throw, but When you are calling the method through the interface, the exception still has to be handle.

  • I would not call omitting something specified in the interface "natural". The interface says `throws`, so I would expect all implementations to say `throws`, too. The interface is a contract. Omitting parts of it breaches the contract. Since this is apparently working as intended, I would like to know what the benefit of that is. – Jan Dörrenhaus Sep 11 '13 at 13:36
  • admittedly i don't see any benefit to allow the omitting but i don't see any harm either. Throws says to throw a **possible** but not **necessary** exception. Omitting throws does not "force to forego the possibility of throwingthrowing an exception", throws or not you always have to handle it when calling from the interface. the point of adding throws to the abstract method is to remind you to always remember to handle the possible execptions, not to force the subclasses to throw one. – judaschrist Sep 11 '13 at 14:15
  • 1
    Programmer A specifies interface I. Programmer B writes implementation class X, but forgets to add the `throws`. He also never notices, because there is not even a warning being thrown here. Programmer C writes implementation class Y, inheriting from class X, he even specifically also wants to put the `throws` there, because he is gonna throw. But even though the interface stipulates it, he is now not allowed to do so anymore, because of B's oversight. He is effectively not allowed to use that exception here anymore. That's a pretty big harm. Especially if class X is not under your control. – Jan Dörrenhaus Sep 11 '13 at 14:19
0

In Java its OK and normal that you can make your classes less restrictive when implementing or extending that is a design decision of the Java developer. I can not say why Sun decided it this way and of course I can understand your problem with it, but the current way also has some benefits.

I see an Interface as a possibility to have multiple implementations for one job. For example the List classes which have different implementations for different needs. But they can all be used via the List interface. Lets say the remove method throws a CheckedException if the element is not part of the list. Now if am not able to be less restrictive in my implementations all List classes must throw this CheckedException, even they do not need or use it.

So if I use the remove method internally in my Class I am forced to handle the CheckedException. If I use my List class directly without the Interface I am forced to catch the exception. But for both cases I am pretty sure I do not need it and it will never happen. So with the current approach I can save a lot of "try catch ignore" blocks.

An other benefit of the current solution is that a class can easily match similar Interfaces.

So for example in one lib someone added a:

public interface Generator {
    String generate() throws IOException;
}

And in an other lib:

public interface GeneratorInterface {
    String generate();
}

If I write a Class with a generate method without any CheckedException I can easily use this to satisfy both Interfaces and maybe work with both libs:

public class EmptyStringGenerator implements Generator,GeneratorInterface {

    @Override
    public String generate() {
        return "";
    }
}

Also as far as I know Java is the only language with such handling of Checked and Unchecked exceptions and so the design decisions which pull through Java are strange compared with other languages not having Checked exceptions.

mszalbach
  • 10,612
  • 1
  • 41
  • 53