13

I have a class where on the one hand, it feels right to return an InputStream from a public method, e.g.

public class MyClass {

    private File _file;

    ...

    public InputStream getInputStream() {
        return new FileInputStream( _file );
    }
}

However, I'm also very cautious about doing this, as it puts the onus on the caller to close this stream. What ways can I potentially avoid this problem?

Matt Dunn
  • 5,106
  • 6
  • 31
  • 55
  • I don't think that this is a problem. It's already done in Java APIs (for example, `ServletResponse.getOutputStream()`). – Harry Cutts Nov 16 '12 at 16:39
  • 1
    While I'm a fan of trying to make an API that reduces the likelihood of errors, there's a certain minimum you can expect from the people using it. I think closing `InputStream`s is one of those things - if they don't remember to do that they've only got themselves to blame. – Vala Nov 16 '12 at 16:50

4 Answers4

10

Depends on why this is a problem in your eyes. If you absolutely must return an InputStream and the file in question is not too large, you could buffer the entire file into a byte array, close the original stream and return new ByteArrayInputStream(buf). Closing a ByteArrayInputStream is not necessary (and in fact has no effect).

However, if it "feels right" to return an InputStream, does it not make sense that the caller should be expecting an InputStream, and all that goes with it, including the necessity of closing the stream when finished?

CupawnTae
  • 14,192
  • 3
  • 29
  • 60
4

Returning InputStream is not intrinsically a bad thing. Now if you want your caller to access the data without being responsible for closing the resource, you can do that:

interface InputReader {
    void readInput(InputStream is);
}
public class MyClass {
    void feed(InputReader ir){
       try(InputStream is=new FileInputStream( _file )){
          ir.readInput(is);
       }
    }
}

The caller specify an instance of InputReader that will receive the closable resource as argument and is no longer responsible for closing it.

MyClass myClass = ... ; //Get the instance
myClass.feed( new InputReader() {
    @Override
    void readInput(InputStream is){
       ... ; // Use at will without closing
    }
});

One should consider to decorate the InputStream before passing it to the InputReader so that .close() throws and exception.

Pierre
  • 545
  • 5
  • 14
2

Realistically there's not much you can do without knowing more details about the class. You could provide file processing through methods in MyClass (which requires knowing what the file contents mean) and close the stream when it's empty. Aside from that though, the user of the class is responsible for this object, and you can't really avoid that. Without the capability of destructors, as in C++, you cannot be 100% responsible for any object that you let leave the scope of your class.

What you can do is put a close() method on your class which cleans up any open file handlers, connections, etc., and require the user of the class to be responsible for calling close(). See this question for some discussion of how to use the finalize method to keep track of whether callers are properly closing your class.

Community
  • 1
  • 1
Chris Hayes
  • 11,471
  • 4
  • 32
  • 47
1

...as it puts the onus on the caller to close this stream.

Yes, the caller is responsible for handling the closing operation of the stream(s) returned. As there is no way for your method to track its returned thing, this task completely belongs to the caller. There is an analogy between this case and using exceptions. Developers use exceptions because the API authors cannot always control what they present to us. We have to be careful, e.g. when there is a possibility of dividing a number by zero.

Juvanis
  • 25,802
  • 5
  • 69
  • 87