Why ByteArrayOutputStream.close() throws IOException?
Asked Answered
E

1

5

Why ByteArrayOutputStream.close is declared with throws IOException? First, de facto it can't throw anything, because its body is empty. Second, de jure it can't throw anything, because its documentation says "closing a ByteArrayOutputStream has no effect".

Isn't this (non-important, but still) a little mistake?

Yes, I understand that its superclass OutputStream implements Closable, the close method of which is allowed to throw IOException. But nobody forbids to override it (in ByteArrayOutputStream) with close method with no throw specification. (Even if overriding a more-throwing method with a less-throwing method was forbidden in some ancient versions of Java, changing ByteArrayOutputStream.close definition now won't be incompatible change.)

Exegesis answered 22/9, 2016 at 20:10 Comment(3)
if the definition was changed, existing codes which catch IOException will get compile error Unreachable catch block for IOException. This exception is never thrown from the try statement body.Syphilis
@saka1029: I think, you are on the right track.Mournful
@saka1029, you're right (I thought it would cause warning, not error).Exegesis
M
4

The most plausible explanation (besides oversight) is compatibility. Back in Java 1.1, ByteArrayOutputStream did not override close(), so it inherited the method from OutputStream which declares IOException. Back then, it might have been an oversight. Perhaps, the developers thought that this is unnecessary as nobody is going to call close() on a ByteArrayOutputStream anyway. But the documentation lacks an explicit statement about calling close() being unnecessary.

Since Java 1.2 aka Java 2, ByteArrayOutputStream does override close(). But removing the throws clause would cause code calling close() on a ByteArrayOutputStream and catching the checked IOException to produce a compile-time error when the exception is not thrown at any other place within the try block. Since this doesn’t affect the binary compatibility, it might look strange considering how much changes with more impact were made on the source code level since then.

But this decision was made a long time age. It’s also unclear, why the override was added at all, as the inherited no-op was sufficient and the override doesn’t change the signature and also doesn’t contain a useful documentation improvement in that version, i.e. no clarification about close() being unnecessary. The most plausible explanation is that it was added with the intent of removing the throws clause, but then it was detected that the incompatibility was an issue with certain existing code.

In the end, it’s not really important to remove it. If your compile-time type is ByteArrayOutputStream, you know that you don’t need to call close(). In all other cases, i.e. if the compile-time type is the more general OutputStream, you have to close() and handle the declared IOException

Mournful answered 23/9, 2016 at 13:57 Comment(3)
I understand, that it’s not really important to remove it.Exegesis
Actually, the main part of the answer was introduced by @saka1029: I didn't know that removing a throws clause really can introduce incompatibilities. I think that fact (that removing a throws clause can introduce source incompatibilities; that "Unreachable catch block for …" is an error, not a warning) is a mistake per se.Exegesis
@saka1029's statement is a comment (not an answer), so I can't accept it (despite it appeared first and is laconic, so I'd like to do it very much).Exegesis

© 2022 - 2024 — McMap. All rights reserved.