Omitting throw declarations in derived classes

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 ""; } } 

Please note that I missed the throws IOException portion of the throws IOException signature specified in the Generator interface. However, there is no compiler error, no compiler warning, even the @Override complaint complains.

I know that this works as intended. However, I would like to know about the intentions behind this. If my method does not actually throw an IOException , it would just not throw it, I do not need to remove it from the signature. But if I remove it from my method signature in EmptyStringGenerator , I force all current and future subclasses of this class to refuse the possibility of throwing an exception, which is really indicated in the interface.

This sounds like a function to me that really doesn't do you any good (besides saving a few keystrokes, which is not really an advantage), but it can be a terrible mistake when actually used.

So my question is really this: what is the throws exception point in derived classes? What is the problem solved by this opportunity? Why is this allowed?

UPDATE

For people asking “where is the harm in this?”, Here is my example from one of my comments. By the way, this is not far-fetched, because that is exactly what I have right now:

Programmer A indicates interface I. Programmer B writes an implementation class X, but forgets to add throws. He also never notices, because a warning does not even appear here. Programmer C writes an implementation class Y inheriting from class X, he even specifically wants to put throws there, because he is going to throw. But even if the interface provides this, they are now no longer allowed to do this due to oversight B. He is no longer allowed to use this exception here. This is a very big harm. Especially if class X is not under your control.

+6
source share
3 answers

If you omit the throws exception in derived classes, you can call the method of the derived class without catching the exception.

Ensure that subclasses of EmptyStringGenerator do not throw exceptions either. Otherwise, there would be no confidence in the compiler if a method call could raise a checked exception that the code should handle. In this case, throws doesn't make sense at all.

+1
source

Depending on your class, follow these two conditions:

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

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

but if you intend to use an interface instead of a class, follow these steps:

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

than the compiler will 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 way, the same compilation error will occur. Thus, dropping the throw does not actually free you from handling the exception. It’s natural not to throw an exception into the class and its subclasses when not to throw them, but when you call the method through the interface, the exception should still be handled.

0
source

In Java, it’s normal and normal that you can make your classes less restrictive when implementing or extending, which is the design decision of the Java developer. I can’t say why Sun solved it this way, and of course I can understand your problem with this, but the current method also has some advantages.

I see the interface as an opportunity to have several implementations for one job. For example, List classes that have different implementations for different needs. But all of them can be used through the List interface. Assume that the remove method throws a CheckedException if the item is not part of the list. Now, if I cannot be less restrictive in my implementations, all List classes should throw this CheckedException, even they are not needed or do not use it.

So, if I use the remove method inside my class, I am forced to handle a CheckedException. If I use my List class directly without an interface, I have to catch an exception. But for both cases, I am sure that I do not need this, and this will never happen. Thus, with the current approach, I can save a lot of "try catch ignore" blocks.

Another advantage of the current solution is that the class can easily match similar interfaces.

So, for example, in one lib, someone added a:

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

And in another lib:

 public interface GeneratorInterface { String generate(); } 

If I write a class with the generate method without any CheckedException, I can easily use it to satisfy both interfaces and possibly 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 Checked and Unchecked exception handling, and therefore the design decisions that pull Java are weird compared to other languages ​​that don't have checked exceptions.

0
source

Source: https://habr.com/ru/post/953623/


All Articles