List.get(0).isEmpty() の落とし穴と正しい判定方法
Spring Boot でファイルアップロードを扱っていると、次のようなコードを書いたことがある人は多いと思います。
if (list.get(0).isEmpty()) {
// 空ファイル
}
一見すると正しそうですが、
このコードは安全でしょうか?
結論から言うと、
「たまたま動いているだけ」になるケースがあります。
本記事では、
- List.get(0).isEmpty() は安全なのか
- size が 1 になる「空ファイル」の正体
- 実務で事故らない判定方法
を整理します。
よくある疑問
まず、多くの人が一度はこう書きます。
if (list == null || list.isEmpty() || list.get(0) == null || list.get(0).isEmpty()) {
// 空 or 不正
}
ここで必ず出てくる疑問がこれです。
「この条件、全部必要?」
各条件が守っているもの
それぞれ何を防いでいるのか整理します。
条件 防いでいる事故
list == null //バインドされない
list.isEmpty() //要素 0(IndexOutOfBounds)
list.get(0) == null //不正データ
list.get(0).isEmpty() //ファイル未選択・空
重要なのは、
Spring MVC ではすべてが同時に必要とは限らないという点です。
Spring MVC の暗黙仕様
Controller で次のように受け取っている場合:
@PostMapping("/upload")
public void upload(@RequestParam("files") List<MultipartFile> list) {
}
Spring MVC は以下を ほぼ保証します。
- list は null にならない
- list.get(0) も null にならない
ファイル未選択時でも
list.size() == 1 && list.get(0).isEmpty() == true
つまり、
list.get(0).isEmpty()
が「動いてしまう」理由は、
Spring がよしなにオブジェクトを作ってくれているからです。
Controller 限定なら必要な条件はこれだけ
Spring MVC 前提・Controller 直下であれば、
実務上の最小条件はこれです。
if (list.isEmpty() || list.get(0).isEmpty()) {
// ファイル未指定 or 空
}
- list == null → 起きない
- list.get(0) == null → 起きない
- IndexOutOfBounds → isEmpty() で防止
それでも事故るケース:size=1 問題
次にハマりやすいのがこれです。
file.isEmpty() == false
file.getSize() == 1
「空ファイルのはずなのに size が 1」
これはバグではありません。
正体は「改行」
- LF(\n) → 1 byte
- CRLF(\r\n) → 2 bytes
改行だけの CSV / TXT は、
見た目は空でも size > 0 になります。
size / isEmpty を信用しすぎない
以下は NG 例です。
if (file.getSize() == 0) {
// 空
}
size は 論理的な空を保証しません。
実務で安全な判定(テキスト前提)
CSV や TXT なら、
実体を見るのが正解です。
byte[] bytes = file.getBytes();
String content = new String(bytes, StandardCharsets.UTF_8);
if (content.trim().isEmpty()) {
// 実質空
}
これで、
- 改行だけ
- 空白だけ
- タブだけ
すべて防げます。
再利用コードなら防御的に書く
Service や Util に切り出す場合は、
Spring 前提を捨てた方が安全です。
if (list == null || list.isEmpty() || list.get(0) == null || list.get(0).isEmpty()) {
throw new IllegalArgumentException("ファイルが不正です");
}
どの層のコードかで正解が変わる、これが重要です。
設計的に一番きれいな解決策
そもそも List を使わない。
@PostMapping("/upload")
public void upload(@RequestParam("file") MultipartFile file) {
if (file.isEmpty()) {
// 空
}
}
- 条件が減る
- NPE を考えなくていい
- 可読性が高い
まとめ
- list.get(0).isEmpty() は Spring 前提なら動く
- ただし 暗黙仕様に依存している
- size / isEmpty は「物理的な空」しか見ていない
- テキストは 中身を見て判定
- 防御レベルは Controller / Service で切り分ける
おわりに
「動いているから正しい」と思っていたコードほど、あとから事故になります。
MultipartFile 周りは、Spring がどこまで保証してくれているかを理解した上で使うのが大事です。