Попробуйте с ресурсами и System.in

Хорошо, это, вероятно, не лучший вопрос, но я застрял на нем и не могу найти ответа на него в сети.

Этот код не будет считываться со стандартного ввода второй раз:

try (BufferedReader br = new BufferedReader(new InputStreamReader(System.in)))
{
    input = br.readLine();
}
catch (final Exception e)
{
    System.err.println("Read from STDIN failed: " + e.getMessage());
}
// do some processing
try (BufferedReader br = new BufferedReader(new InputStreamReader(System.in)))
{
    input = br.readLine();
}
catch (final Exception e)
{
    System.err.println("Read from STDIN failed: " + e.getMessage());
}

Я знаю, что java try-with-resources рекурсивно закрывает все потоки в цепочке, поэтому после первого чтения System.in закрывается. Есть ли какое-нибудь хорошее решение для этого? Или мне действительно нужно самому заниматься закрытием потока?

upd: Я сам пытался обработать закрытие потока (то есть в стиле java6). Вот код, если кому-то интересно. Но я заметил, что такое закрытие цепочки происходит не из-за попытки с ресурсами, а из-за реализации закрытых методов. Так что я ничего не выиграл от этой попытки.

Я выбираю решение fge, потому что оно наиболее подробное. Это сработало для меня напрямую.

В целом, мне кажется довольно странным, что в java нет такого решения из коробки, поскольку есть системные потоки, которые не следует закрывать.


person Danny Lo    schedule 13.04.2014    source источник
comment
Вы можете создать свой собственный InputStream класс, который не будет закрывать базовые ресурсы на .close(); хотя это нарушает .close() контракт   -  person fge    schedule 13.04.2014
comment
Или вы можете избежать использования try-with-resources в этом случае, поскольку вы не хотите, чтобы поток был закрыт, и это то, что делает try-with-resources.   -  person JB Nizet    schedule 13.04.2014


Ответы (4)


Одним из способов обходного решения было бы создать собственный InputStream класс, который будет делегировать другому классу, за исключением того, что он не .close(), когда сам закрыт. Как в:

public class ForwardingInputStream
    extends InputStream
{
    private final InputStream in;
    private final boolean closeWrapped;

    public ForwardingInputStream(final InputStream in, final boolean closeWrapped)
    {
        this.in = in;
        this.closeWrapped = closeWrapped;
    }

    public ForwardingInputStream(final InputStream in)
    {
        this(in, false);
    }

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

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

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

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

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

    @Override
    public void close()
        throws IOException
    {
        if (closeWrapped)
            in.close();
    }

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

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

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

Обратите внимание, что, вероятно, более простым решением в вашем случае было бы расширить InputStreamReader, поскольку класс не является final и просто переопределить .close().

person fge    schedule 13.04.2014
comment
Я думаю, что ОП в курсе. Он просит обходной путь. - person JB Nizet; 13.04.2014

Я подумал, что эта проблема будет более общей, чем попытка с ресурсами, поскольку другие могут использовать старую версию Java и сами закрыть BufferedReader. Это поставит вас в ту же ситуацию, что и сейчас.
Я нашел соответствующий вопрос SO для этого более общего кейс. Приведенный здесь ответ заключается в использовании Apache Commons IO, у которого есть потоковый прокси под названием _ 2_. Если это единственное, что вы могли бы использовать из Commons IO, вы также могли бы подумать о написании прокси-класса самостоятельно, вместо того, чтобы зависеть от большой библиотеки, такой как Commons IO.

person Apanatshka    schedule 13.04.2014

Это действительно небольшая проблема. Я не знаю, существуют ли какие-либо решения в общих библиотеках (Apache Commons IO, Google Guava, ...), но вы могли бы написать простой класс, решающий эту проблему самостоятельно.

Напишите класс, который является InputStream и который является оболочкой для InputStream, переопределяя все общедоступные методы, делегируя вызов обернутому потоку, за исключением метода close, который вообще ничего не делает.

public final class NonClosingInputStream extends InputStream {
    private final InputStream wrappedStream;
    public NonClosingInputStream(final InputStream wrappedStream) {
        this.wrappedStream = Objects.requireNonNull(wrappedStream);
    }
    @Override
    public void close() {
        // do nothing
    }
    // all other methods
}

Заключение System.in в экземпляр этого класса решит вашу проблему.

person Seelenvirtuose    schedule 13.04.2014

Это старый вопрос, но вот более краткое решение:

try (BufferedReader br = new BufferedReader(new InputStreamReader(System.in)
{public void close() throws IOException {}})) {
    input = br.readLine();
}

Это гарантирует, что System.in не будет закрыто путем передачи InputStreamReader с пустым переопределенным методом close() конструктору BufferedReader.

person MikaelF    schedule 28.02.2017