web-dev-qa-db-ja.com

ReaderまたはInputStreamが渡されたときにAPIメソッド本体の内部で.close()を使用しない方がよいですか?

答えられている同様の質問がたくさんあります。例 ここ 。ただし、これらはすべて、同じスコープまたはメソッド本体内にReaderとInputStreamの両方を持っているため、最初の(Reader)ではなくチェーンの最後(InputStream)を閉じることをお勧めします。

私の特定のケースでは、私はAPIを構築しており、ユーザーに使用する入力ソースの選択肢を提供してほしいと思っています。彼らは柔軟性を必要としています。このため、私は通常、次のタイプのテンプレートに従います。

_public class Handler {
  public void handle(BufferedReader in) {
    in.lines().forEach(System.out::println);
  }

  public void handle(InputStream in) {
    handle(new BufferedReader(new InputStreamReader(in)));
  }

  public void handle(Class anchor,String resource) throws IOException {
    try (InputStream in = anchor.getResourceAsStream(resource)) {
            handle(in);
    }
  }

  public void handle(String resource) throws IOException {
    try (InputStream in = ClassLoader.getSystemResourceAsStream(resource)) {
            handle(in);
    }
  }

  public void handle(Path path) throws IOException {
    try (InputStream in = Files.newInputStream(path)) {
            handle(in);
    }
  }

  public void handle(File file) throws IOException {
    try (InputStream in = Files.newInputStream(file.toPath())) {
            handle(in);
    }
  }

  public static void main(String args[]) throws NoSuchMethodException, IOException { 
    Handler h = new Handler();
    h.handle(Handler.class,"/readme.txt");
  }
}
_

次のような利点があると思います。

  • リソースの所有者のみがそれを閉じることができます。リーダー/入力の場合、利点はそれほど明確ではありません。ただし、それがライター/出力である場合、これにより、同じリソース上で複数のそのような機能を連続して連続して動作させることができます。それはAPIとして理にかなっていると思います。
  • 理解しやすい:リソースを渡す/所有する場合は、リソースを閉じる責任があります。
  • リソース名(String)を渡すと、APIがリソースのオープンとクローズの両方を処理する必要があることは明らかです。

したがって、状況とその回答方法の両方で、他の投稿との違いがあります。

  • ここで:ReaderまたはInputStreamがパラメーターとしてAPIのメソッドに渡される場合は、APIメソッド本体の内部で.close()を実行しないでください。
  • その他:同じスコープ内にある場合、InputStreamReaderとチェーンされ、常にチェーンの最後(Reader)を閉じます。

これは理にかなっていると思いますが、これはこれを処理する正しい方法ですか?

4
YoYo

私はどちらも最善の解決策ではないと主張します。

何らかの種類のストリームまたはリーダーを処理している場合、これは、外部から利用可能なデータまたはリソースへの入力の読み取りまたは出力の書き込みを意味します。このリソースは、オペレーティングシステムファイルに関連付けられている場合とそうでない場合があります。したがって、そのリソースで.close()メソッドを呼び出すと、OSレベルで何かに影響を与える可能性があります。

一般的に言えば、メソッドが何らかのストリームまたはリーダーを取得する必要がある場合、最も安全な方法は、リソースからのストリームまたはリーダーのインスタンス化、発生する可能性のある例外の処理、およびリソースの使用の確定を適切に処理することです。 (s)メソッド呼び出しのスコープを離れる前。

ファイルまたはリソースが大きいか、潜在的に無限のデータストリームである場合、StreamまたはReaderのスコープがメソッドのスコープよりも長く存続することはおそらく意味があります。ストリームまたはリーダーのライフサイクルがクラスインスタンスのライフサイクルに関連付けられている場合、適切なオプションは、ストリームまたはリーダーをクラスのプライベートスコーププロパティとして設定し、そのクラスインスタンスのメソッドがそれを利用および依存できるようにします。ただし、リソースのクリーンアップとクローズについては注意が必要ですが、そのクラスインスタンスがガベージコレクションされると、StreamまたはReaderもガベージコレクションされます。すべてのJVM実装がfinalize()メソッドがガベージコレクションで呼び出されることを保証しているとは思いませんが、ここでは間違っているかもしれません。

結論としては、Javaの場合、ストリームまたはリーダーを開いてメソッドに引数として渡すのは、悪い習慣と見なされます。これが適切であると言えるのは、非常に非常に大きなメソッドであり、コードを小さなヘルパーメソッドに分割したいが、それでもこれらはプライベートスコープメソッドであり、ドキュメント化されている必要があります。これをパラメーターの試行として受け入れるメソッドを確認したくないのは確かです。スタックの下にあるメソッドに代わってそれを閉じます。コードレビューの観点からも、リソースハンドラーが初期化されている場所を確認し、最終的に、それらがファイナライズされて閉じられることが保証されている場所を明確に確認します。

1
maple_shaft