I have a question about delegation and future proo...
# announcements
s
I have a question about delegation and future proofing. I need to create an InputStream wrapper that monitors the amount of data read by the stream, this has been done numerous times. In Kotlin, I was hoping to use the Property Delegation feature of Kotlin but that’s not possible as it only supports delegating to an interface and there is no interface for InputStream. So I have to do this manually:
Copy code
class ObservableInputStream(
    private val wrapped: InputStream
) : InputStream() {
    // override all methods of InputStream by calling the corresponding method in wrapped
}
Now, since Java is moving much faster than it was before 8 was released, we’re seeing methods being added to this fundamental class (
readAllBytes()
,
readNBytes(byte[], int, int)
and
transferTo(OutputStream)
added in 9,
readNBytes(int)
added in 11) my question is whether there is any best-practice to future proof such a delegator class. We’ll be running on JDK 11 for now but we’ll try to keep our runtime up to date… so if a future version of the JDK adds some methods to InputStream, this could possibly lead to mysterious bugs as those won’t get delegated unless we add those manually (which, of course, would render the new class backwards incompatible). So, any pointers on how to handle this? Even something that would simply fail compilation if we update the JDK without overriding any new methods that would have been added. I don’t know of anything like that myself but maybe something like this exists.
d
Personally - I don’t see any nice way to get out of this situation. One obvious way is to introduce your own interface. But it obviously doesn’t work, because then you can’t pass such input stream externally. However, I am curious about this particular use case. Let’s say you overriden all methods and delegated their execution to original input stream. I call
read(byte[] b, int off, int count)
method on observable input stream. It delegates call to original input stream. Which then uses it’s own
read()
, not one from observable input stream. Is that how you want it to behave? If yes - what is the point of wrapping input stream?
s
well, in this particular case I override all read methods and collect the “bytes read” in each one, to monitor how much data has passed through this input stream. So if I override the
read(byte[], int, int)
I’m intercepting the return value of that and adding that to my accumulation of bytes read. Even though that internal implementation then calls its own
read()
it’s okay because I’ve already collected the bytes read. Are you implying that using pure inheritance would be a better fit here?
d
Well, I was thinking on how to approach this problem. But for this use case, I don’t see any other way that what you are doing already. Only thing that my brain comes up with for future-proofing is doing something silly like: write unit test that gets method count for InputStream and asserts it to be this specific number. If someone would add more methods there in future, this test would fail.
s
There’s an idea 🙂 But of course, that would only catch it if the test runtime is the same as the production runtime… which should of course be the case, but you never know.
I wonder if it might be a good idea to create an annotation that would specify which Java version a class is guaranteed to work with… `@WorksWithJava("11,12,13,14")`which would throw a runtime exception if run on anything other than one of the versions specified.