Close Scanner without closing System.in
Asked Answered
S

3

12

I'm trying to re-factor a large and frequently used part of my application into separate methods to make it easier to maintain.

Some of these methods asks the user for input and does input validation, so I've used a Scanner and System.in But when I close my Scanner I also close System.in

So my question is, can I only prevent System.in being closed by shielding it with CloseShieldInputStream or should I just start passing a Scanner to the methods?

Someplace answered 19/2, 2013 at 16:12 Comment(4)
Please post some code...Shavers
Do you really need to close the Scanner? I'd suggest letting the garbage collector handle it - there's no way of closing it without closing the underlying object.Mom
My method declares a Scanner, reads and returns nextLine(), closes the Scanner and gives me a headache on the next run. If I do not close it, Eclipse pesters me about a potential resource leak, would it be safe to ignore that?Someplace
Eclipse is simply wrong in this case - or rather, it's being overly cautious. Closing a Scanner has no useful effect other than closing the underlying InputStream, so if you don't want to close System.in then don't call close() on the Scanner.Manicdepressive
S
3

You can just ignore close by implementing custom decorator.

public class UnClosableDecorator extends InputStream {

    private final InputStream inputStream;

    public UnClosableDecorator(InputStream inputStream) {
        this.inputStream = inputStream;
    }

    @Override
    public int read() throws IOException {
        return inputStream.read();
    }

    @Override
    public int read(byte[] b) throws IOException {
        return inputStream.read(b);
    }

    @Override
    public int read(byte[] b, int off, int len) throws IOException {
        return inputStream.read(b, off, len);
    }

    @Override
    public long skip(long n) throws IOException {
        return inputStream.skip(n);
    }

    @Override
    public int available() throws IOException {
        return inputStream.available();
    }

    @Override
    public synchronized void mark(int readlimit) {
        inputStream.mark(readlimit);
    }

    @Override
    public synchronized void reset() throws IOException {
        inputStream.reset();
    }

    @Override
    public boolean markSupported() {
        return inputStream.markSupported();
    }

    @Override
    public void close() throws IOException {
        //do nothing
    }
}

And use it in main

public static void main(String[] args) throws Exception {
        System.setIn(new UnClosableDecorator(System.in));
}
Shavers answered 19/2, 2013 at 16:27 Comment(5)
Wouldn't this be the same as writing my own version of CloseShieldInputStream?Someplace
I'm not quite sure what do you mean, but if you need to perform some actions with input stream on close do this actions in UnClosableDecorator.close() methodShavers
CloseShieldInputStream is a proxy stream that prevents the underlying input stream from being closed. commons.apache.org/io/apidocs/org/apache/commons/io/input/…Someplace
Actually my implementation is not the same as CloseShieldInputStream. CloseShieldInputStream on close will replace input stream with dummy object that on read always returns -1 (it means that InputStream is closed) and my implementation on close just do nothing. I'm not sure what solution will be more appropriate for you.Shavers
I suspect I should just start passing Scanners around instead, it seems to be the least painful way in the end.Someplace
N
19

Simply use a custom FilterInputStream instead of System.in:

new FilterInputStream(System.in) {
    @Override
    public void close() throws IOException {
        //don't close System.in! 
    }
}
Natalyanataniel answered 11/11, 2013 at 12:26 Comment(4)
This is pretty much what commons' CloseShieldInputStream does and if you end up this position, you may want to have another look at the design of your software. A solution like this shouldn't be needed unless you're working with third-party solutions. (applies to all the answers here)Someplace
@Someplace I don't understand your reasoning. Why would this not be needed? How else would one handle a case where you need to use a scanner multiple times in the lifetime of a program other than making the scanner a variable that is effectively global?Haughty
@Haughty you're asking a question regarding an 8 year old question, but the question is not how to re-use the scanner, the question is how to prevent System.in from being closed when the Scanner closesSomeplace
@Someplace I saw how old the question was, I simply held out hope you might be notified of my question. Alas, you were. Your comment was not exactly an answer to the question. You stated a viewpoint that I do not understand i.e. "... if you end up this position, you may want to have another look at the design of your software". I fail to see how ending up in said position (wanting to close the scanner but not System.in) implied a design flaw in the software. I was hoping you would clarify that view point. I'm just trying to learn.Haughty
S
3

You can just ignore close by implementing custom decorator.

public class UnClosableDecorator extends InputStream {

    private final InputStream inputStream;

    public UnClosableDecorator(InputStream inputStream) {
        this.inputStream = inputStream;
    }

    @Override
    public int read() throws IOException {
        return inputStream.read();
    }

    @Override
    public int read(byte[] b) throws IOException {
        return inputStream.read(b);
    }

    @Override
    public int read(byte[] b, int off, int len) throws IOException {
        return inputStream.read(b, off, len);
    }

    @Override
    public long skip(long n) throws IOException {
        return inputStream.skip(n);
    }

    @Override
    public int available() throws IOException {
        return inputStream.available();
    }

    @Override
    public synchronized void mark(int readlimit) {
        inputStream.mark(readlimit);
    }

    @Override
    public synchronized void reset() throws IOException {
        inputStream.reset();
    }

    @Override
    public boolean markSupported() {
        return inputStream.markSupported();
    }

    @Override
    public void close() throws IOException {
        //do nothing
    }
}

And use it in main

public static void main(String[] args) throws Exception {
        System.setIn(new UnClosableDecorator(System.in));
}
Shavers answered 19/2, 2013 at 16:27 Comment(5)
Wouldn't this be the same as writing my own version of CloseShieldInputStream?Someplace
I'm not quite sure what do you mean, but if you need to perform some actions with input stream on close do this actions in UnClosableDecorator.close() methodShavers
CloseShieldInputStream is a proxy stream that prevents the underlying input stream from being closed. commons.apache.org/io/apidocs/org/apache/commons/io/input/…Someplace
Actually my implementation is not the same as CloseShieldInputStream. CloseShieldInputStream on close will replace input stream with dummy object that on read always returns -1 (it means that InputStream is closed) and my implementation on close just do nothing. I'm not sure what solution will be more appropriate for you.Shavers
I suspect I should just start passing Scanners around instead, it seems to be the least painful way in the end.Someplace
B
1

you could just let it be without closing it, just set the holding variable to null

Brinton answered 19/2, 2013 at 16:20 Comment(2)
With that Eclipse gives a warning about scanner never being closed, is this safe to ignore?Someplace
I think it is an improvable programming style. The programmer might end up leaving open resources that must indeed be closed.Longterm

© 2022 - 2024 — McMap. All rights reserved.