Java : FileクラスよりもPathとFilesを使おう
Fileクラスには API仕様としていくつかの問題点があります。
本記事では、Fileクラスの問題点と、それに代わる API である Pathインタフェース、Filesクラスの利点についてご紹介します。
はじめに
Fileクラスは Java 1 から存在する伝統的なクラスです。
古くから Java を使っていたかたはお世話になったのではないでしょうか。
そして、Java 7 で Pathインタフェース、Filesクラス という新しいAPIが java.nio.fileパッケージに追加されました。
これらは File に代わる新しいAPIとなります。
特に理由がなければ、File ではなく Path と Files を使うことをおすすめします。
Fileクラスの問題点
File の API仕様では、
- Fileクラスの多くの制限を克服するために java.nio.fileパッケージ (つまり Path と Files など) を使うことができます。
と紹介されています。
さらに、Java チュートリアルでは Fileクラスの問題点がより具体的に記述されています。
問題点の一部を引用しています。詳細はリンク先をご確認ください。
それでは実際にコード例でも確認してみましょう。
最初に File の例です。
// --- PowerShell ---
//PS R:\java-work> tree /F
//
//R:.
//├─empty-dir
//└─non-empty-dir
// aaa.txt
final var emptyDir = new File("R:\\java-work\\empty-dir");
System.out.println(emptyDir.delete()); // true
final var nonEmptyDir = new File("R:\\java-work\\non-empty-dir");
System.out.println(nonEmptyDir.delete()); // false
final var nonExistentDir = new File("R:\\java-work\\XXX");
System.out.println(nonExistentDir.delete()); // false
// --- PowerShell ---
//PS R:\java-work> tree /F
//
//R:.
//└─non-empty-dir
// aaa.txt
File.delete は処理に失敗すると、戻り値で false を返します。
確かに2つめと3つめがなぜ失敗したのか分かりにくいですね。
次に Path と Files の例です。
// --- PowerShell ---
//PS R:\java-work> tree /F
//
//R:.
//├─empty-dir
//└─non-empty-dir
// aaa.txt
//----------
// OK
final var emptyDir = Path.of("R:", "java-work", "empty-dir");
Files.delete(emptyDir);
//----------
// NG
try {
final var nonEmptyDir = Path.of("R:", "java-work", "non-empty-dir");
Files.delete(nonEmptyDir);
} catch (IOException e) {
System.out.println(e);
}
// 結果
// ↓
// java.nio.file.DirectoryNotEmptyException: R:\java-work\non-empty-dir
//----------
// NG
try {
final var nonExistentDir = Path.of("R:", "java-work", "XXX");
Files.delete(nonExistentDir);
} catch (IOException e) {
System.out.println(e);
}
// 結果
// ↓
// java.nio.file.NoSuchFileException: R:\java-work\XXX
// --- PowerShell ---
//PS R:\java-work> tree /F
//
//R:.
//└─non-empty-dir
// aaa.txt
Files では処理に失敗すると、IOException(もしくはそのサブクラス)を発生させます。
例では DirectoryNotEmptyException と NoSuchFileException です。
例外の名前を見ただけで原因が推測できるので、問題解析は Fileクラスの true/false よりやりやすいですね。
あとは本サイトの記事「例外 vs. 戻り値でエラーコード」でも紹介させていただきましたが、基本的には戻り値より例外のほうが堅牢なエラー処理ができるのでおすすめです。
Pathインタフェース、Filesクラスの利点
Fileクラスの問題点で紹介した内容が、Path、Files では解消されます。
それ以外にも利点があります。
それぞれのAPIの役割を見てましょう。
API | 役割 |
---|---|
File | ・パス名を保持する。 ・パス名の操作。(例えば getParent で親のパス名を取得) ・実際のファイルやディレクトリにアクセス。(mkdir, exists, deleteなど) |
Path | ・パス名を保持する。 ・パス名の操作。(例えば getParent で親のパス名を取得) |
Files | ・実際のファイルやディレクトリにアクセス。(createDirectory, exists, deleteなど) |
Fileクラスの持っていた役割を、Pathインタフェースと Filesクラスの2つに分割したイメージですね。
実際のファイルへのアクセスは、プログラムのなかでも慎重に扱わないといけないものの1つです。
間違ってユーザの大切なファイルを消してしまったら大変です…
Fileクラスを使うと、パス名の操作しか必要としないコードでも、本当にファイルにアクセスしていないか?と不安になるかもしれません。
コードレビューなどではより慎重に確認する必要があるでしょう。
しかし、本当にパス名だけの操作であれば Path だけを使えば安心できます。
なぜなら Files が出てこない限りファイルへのアクセスはしていないからです。
補足
- Path には toRealPath, register など一部 IOExceptionが発生する(ファイルにアクセスする)メソッドがあります。
相互運用も可能
とはいえ、古いAPIには Fileクラスをパラメータに要求するものがあるかもしれません。
そんなときも大丈夫です。
File ←→ Path
の相互変換用のAPIも用意されています。
final Path path = Path.of("sample.txt");
final File file = path.toFile();
System.out.println(file); // sample.txt
final File file = new File("sample.txt");
final Path path = file.toPath();
System.out.println(path); // sample.txt
まとめ
Fileクラスは古いAPIです。
特に理由がなければ、新しいAPIである Path と Files を積極的に使うことをおすすめします。