#前置き
新しい言語を作るにあたって、ファイルを読み書きするものが必要になると思ってこの記事を書きました。汎用性がとても高いので、ぜひ何かに応用してみてください。
##EAMとは
EAMとはExecute Around Methodの略でメソッドの前後でメモリを確保または開放するというものです。特に、外部リソースを使うとき(IOなど)ガベージコレクタ(GC)だけでは十分でなく、リソースを閉じる必要があります(FileWriterのclose()など)。言葉で言ってもわかりづらいのでさっそく実装を見てみましょう。
##try-with-resourcesやARMを使わずEAMを実装している理由
Javaによる関数型プログラミング -Java8ラムダ式とStream-
からの引用ですが、
Java8で追加されたStreamクラスがAutoCloseableを実装しました。(中略)これに伴いAutoCloseableは以前の厳格な「リソースは閉じられなければならない」ではなく、より緩い「リソースは閉じられる可能性がある」という取り決めに変更されました。(中略)ARMを使用するこのコードは非常に簡潔で魅力的ですが、開発者が忘れず使わなければなりません。このエレガントな構文を無視してもコードに注意されることはありません(中略)しかし、タイムリーなリソース解放を行い、開発者によるエラーを確執に回避する方法を探しているのであれば、ARMのさらに先を行かなければいけません。
##実装
import java.util.*;
import java.io.*;
public class EAM{
private final FileReader fr;
private EAM(final String s) throws IOException{
fr = new FileReader(s);
}
public static void use(final String s,final Use<EAM,IOException> u) throws IOException{
final EAM eam = new EAM(s);
try{
u.accept(eam);
}finally{
eam.close();
}
}
private void close() throws IOException{
System.out.println("close()");
fr.close();
}
public void read(char[] c) throws IOException{
fr.read(c);
}
}
Readerのみの実装ですが、Writerの実装も簡単です。
@FunctionalInterface
public interface Use<T,X extends Throwable>{
void accept(T t) throws X;
}
##実装の説明
せっかく外部リソースを管理しようとしているのにuse()の外でリソースが開かれてしまうといけないので、コンストラクタをprivateにしています。これによって外部から呼び出せるのはuse()だけになります。
--EAM.javaの説明
final EAM eam = new EAM(s); では、インスタンス化とともにコンストラクタ内で外部リソースを開いています。
u.accept(eam); これがメソッドの本体です。
eam.close(); finallyの中に記述されているので、確実に外部リソースを閉じます。
--Use.javaの説明
void accept(T t) throws X; IOExceptionを投げます。Consumerを使うこともできましたが、例外を投げることを示す必要があります。
##実行
import java.io.*;
import java.util.*;
public class Main{
public static void main(String[] args) throws IOException{
char[] c = new char[100];
EAM.use("eam.txt",eam -> eam.read(c));
for(char aChar:c) System.out.print(aChar);
}
}
eam.txtを作成して文字列を打ち込むか、ほかのファイルを指定してください。
ファイル内の文字列は読み込めたでしょうか。分からないことがあれば質問してください。