JavaでSystem.inを閉じると再び開くことができないのはなぜですか。
以前気になってたのですが、デコレータを書けば回避できるので忘れていました。
質問
1 System.in、System.out、System.errを閉じると、再び開くことができない理由は。
- 再び開けないとは思えません。調べると C で File Descriptor を dup する方法がありました。
- プラットフォームごとに動作が異るから、一律に開けなくしているのか。
2 閉じないための回避策はありますか。
- InputStreamReaderのデコレータを用意して close() をオーバーライドする方法(コード参照)。
- それ以外に回避策はあるか。System.setIn() はこの目的で本当に使えるのか。
いまさら感のある質問で申しわけありません。
try-with-resources を使用する System.in の閉じ
Java
1 public static void main(String[] args) { 2 for (int i=0; i<2; ++i) { 3 // 2度目にSystem.inを開くことができない。 4 try (BufferedReader br = new BufferedReader( 5 new InputStreamReader(System.in))) { 6 while(true) { 7 String str = br.readLine(); 8 if ("quit".equals(str)) { 9 break; 10 } 11 } 12 // System.inはtry-with-resourceブロックの最後に閉じられる。 13 } catch(IOException e) { 14 e.printStackTrace(); 15 } 16 } 17 }
Decoraterパーターンを適用する閉じ無効策
Java
1package autocloseable; 2 3import java.io.IOException; 4import java.io.InputStream; 5import java.io.InputStreamReader; 6import java.io.UnsupportedEncodingException; 7import java.nio.charset.Charset; 8import java.nio.charset.CharsetDecoder; 9 10/** 11 * InputStreamReaderをラップする。 12 * System.inなら閉じない。それ以外なら閉じる。 13 */ 14public class SmartInputStreamReader extends InputStreamReader { 15 16 private final boolean isSystemIn; 17 18 public SmartInputStreamReader(InputStream in) { 19 super(in); 20 this.isSystemIn = (System.in == in); 21 } 22 public SmartInputStreamReader(InputStream in, String charsetName) 23 throws UnsupportedEncodingException { 24 super(in, charsetName); 25 this.isSystemIn = (System.in == in); 26 } 27 public SmartInputStreamReader(InputStream in, Charset cs) { 28 super(in, cs); 29 this.isSystemIn = (System.in == in); 30 } 31 public SmartInputStreamReader(InputStream in, CharsetDecoder dec) { 32 super(in, dec); 33 this.isSystemIn = (System.in == in); 34 } 35 36 /** 37 * InputStreamReaderの閉じを判断する。 38 * System.inなら閉じない。それ以外なら閉じる。 39 */ 40 @Override 41 public void close() throws IOException { 42 if (isSystemIn) { 43 return; 44 } else { 45 super.close(); 46 } 47 } 48}
追記(2019-6-29)
いまさらですが、java.io.Console を使うと try-with-resources でストリームが閉じられないことがわかりました。ただし eclipse で実行すると Console は null になります。コマンドラインで実行してください。
Java
1import java.io.BufferedReader; 2import java.io.Console; 3import java.io.IOException; 4 5public class ConsoleIo { 6 public static void main(String[] args) { 7 Console consl = System.console(); 8 for (int i = 0; i < 2; ++i) { 9 try (BufferedReader rdr = new BufferedReader(consl.reader())) { 10 String line = rdr.readLine(); 11 System.out.println(line); 12 } catch (IOException e) { 13 e.printStackTrace(); 14 } 15 } 16 } 17}
追記(2020-7-31)
いまさらですが、無名クラスを使用するやり方を追記。
Java
1import java.util.*; 2import java.io.InputStreamReader; 3 4public class Main_ { 5 public static void main(String[] args) throws Exception { 6 try (Scanner sc = new Scanner( 7 new InputStreamReader(System.in) {@Override public void close() {}})){ 8 } 9 } 10}
回答3件
あなたの回答
tips
プレビュー