Java 逆引き集 | 戻り値(参照/プリミティブ) - API レイヤ設計

Java Java
スポンサーリンク

戻り値って何者か(メソッドの「出口」)

戻り値は「メソッドが仕事を終えたあと、呼び出し元に渡す結果」です。 APIレイヤを設計するとき、「このメソッドは何を返すべきか」「プリミティブにするか参照型にするか」は、使いやすさと安全性に直結します。

例えば、単純な足し算ならこうです。

public static int add(int a, int b) {
    return a + b;
}
Java

int が戻り値の型で、「このメソッドは int を返します」と宣言しています。 呼び出し側は、その結果を受け取って次の処理に使います。

int result = add(3, 5);
System.out.println(result); // 8
Java

ここから「プリミティブを返す場合」と「参照型を返す場合」で、設計の考え方が変わってきます。

プリミティブ型を戻り値にするときの考え方

単純な値・計算結果・フラグを返す

プリミティブ型の戻り値は、「単純な数値」「真偽値」「1文字」などを返したいときに向いています。

例えば、税額を計算するメソッドはこう書けます。

public static int calcTax(int price, double rate) {
    return (int)(price * rate);
}
Java

呼び出し側は「税額」という1つの値だけを受け取ります。

int tax = calcTax(1000, 0.1);
System.out.println("税額 = " + tax); // 100
Java

また、「条件を満たすかどうか」を返すメソッドは boolean が自然です。

public static boolean isAdult(int age) {
    return age >= 18;
}
Java

呼び出し側は「大人かどうか」をそのまま if に使えます。

if (isAdult(20)) {
    System.out.println("大人料金です");
}
Java

プリミティブの戻り値は、「意味がはっきりした1つの値」を返すときにとても扱いやすいです。

プリミティブ戻り値のメリットと限界

プリミティブを戻り値にすると、次のようなメリットがあります。

1つの値なのでシンプルで分かりやすい null にならないので、ヌルチェックの心配がない 値のコピーなので、呼び出し側で変更しても元のメソッドには影響しない

一方で、「返したい情報が1つではない」「値がないことを表現したい」といった場面では、プリミティブだけでは足りなくなります。 例えば「検索結果が見つかったときはIDを返し、見つからなかったときは『なし』を表現したい」とき、int だけだと「-1を特別な値として扱う」などの苦しい設計になりがちです。

そこで登場するのが「参照型の戻り値」です。

参照型を戻り値にするときの考え方

オブジェクトや複数情報を返す

参照型の戻り値は、「複数の情報をまとめたもの」「意味を持ったオブジェクト」を返したいときに使います。

例えば、ユーザー情報を表すクラスを作っておきます。

public class User {
    private final int id;
    private final String name;

    public User(int id, String name) {
        this.id = id;
        this.name = name;
    }

    public int getId() { return id; }
    public String getName() { return name; }
}
Java

ユーザーを検索するメソッドは、User を戻り値にできます。

public static User findUserById(int id) {
    // 本当はDB検索などをする想定
    return new User(id, "Taro");
}
Java

呼び出し側は「ユーザーという意味のある塊」を受け取って、その中から必要な情報を取り出します。

User user = findUserById(100);
System.out.println(user.getId());
System.out.println(user.getName());
Java

「IDだけ」「名前だけ」ではなく、「ユーザーという概念」を返せるのが参照型の強みです。 APIレイヤでは、「ドメインを表すクラスを戻り値にする」ことで、ロジックの意図がぐっと伝わりやすくなります。

「結果がない」ことを表現する(null と Optional)

参照型は null を取れるので、「結果がない」ことを表現できます。

public static User findUserOrNull(int id) {
    if (id == 1) {
        return new User(1, "Admin");
    } else {
        return null; // 見つからなかった
    }
}
Java

呼び出し側は、null かどうかをチェックしてから使います。

User user = findUserOrNull(2);
if (user != null) {
    System.out.println(user.getName());
} else {
    System.out.println("ユーザーが見つかりませんでした");
}
Java

APIレイヤ設計では、「結果がない可能性がある」メソッドをどう設計するかが重要です。 null を返す設計はシンプルですが、ヌルチェックを忘れると NullPointerException につながります。

Java 8 以降なら、Optional<T> を戻り値にする設計もよく使われます。

import java.util.Optional;

public static Optional<User> findUser(int id) {
    if (id == 1) {
        return Optional.of(new User(1, "Admin"));
    } else {
        return Optional.empty();
    }
}
Java

呼び出し側は「結果があるかどうか」を明示的に扱えます。

Optional<User> userOpt = findUser(2);
userOpt.ifPresent(user -> System.out.println(user.getName()));
Java

「結果がないこと自体が仕様の一部」である場合、参照型の戻り値はその意味をきれいに表現できます。

APIレイヤ設計での「プリミティブ vs 参照型」判断軸

単純な値か、意味のあるオブジェクトか

APIの戻り値を設計するとき、まず考えるのは「このメソッドは何を返したいのか」です。

例えば、

合計金額だけが欲しい → intlong で十分 「合計金額+税額+割引額」をまとめて返したい → TotalResult のようなクラスを作って参照型で返す

というように、「返したい情報のまとまり」が1つの値で表現できるならプリミティブ、 複数の値や意味のある構造なら参照型、という判断が自然です。

プリミティブで返すと呼び出し側は楽ですが、「何の値なのか」がコード上で曖昧になりがちです。 参照型で返すと、「型名そのものが意味を持つ」ので、APIの意図が伝わりやすくなります。

呼び出し側の使い方をイメージする

戻り値の型は、「呼び出し側がどう使うか」をイメージして決めるのが大事です。

例えば「ユーザー一覧を返す」メソッドなら、

public static List<User> findAllUsers() { ... }
Java

とすることで、「複数ユーザーを順番に処理する」呼び出し側の姿が自然に見えてきます。

List<User> users = findAllUsers();
for (User user : users) {
    System.out.println(user.getName());
}
Java

もしこれを「ユーザー名の配列」だけにしてしまうと、後から「IDも欲しい」「ステータスも欲しい」となったときにAPIを変えざるを得ません。 APIレイヤでは、「今後拡張される可能性」も含めて、戻り値を参照型にしておく方が柔軟なことが多いです。

戻り値と「状態変更」の関係(副作用を意識する)

戻り値で結果を返すか、引数やフィールドを変えるか

メソッドは「戻り値で結果を返す」だけでなく、「引数のオブジェクトを変更する」「フィールドを更新する」といった形でも結果を表現できます。

例えば、リストに要素を追加するメソッドは戻り値を持たなくても意味があります。

public static void addDefaultUser(List<User> users) {
    users.add(new User(0, "Default"));
}
Java

呼び出し側は、戻り値ではなく「渡したリストの中身が変わる」ことを期待します。

List<User> users = new ArrayList<>();
addDefaultUser(users);
System.out.println(users.size()); // 1
Java

APIレイヤ設計では、「このメソッドは戻り値で結果を返すのか」「渡されたオブジェクトを変更するのか」をはっきりさせることが重要です。 どちらも混ぜると、「何がどこで変わるのか」が分かりづらくなります。

副作用を減らして、戻り値に集中させる

再利用しやすく、テストしやすいAPIにしたいなら、「できるだけ戻り値で結果を返し、副作用(外部状態の変更)を減らす」方向が安定します。

例えば、

public static int calcDiscountPrice(int price, double rate) { ... }
Java

のように、「渡された値から新しい値を計算して返す」メソッドは、呼び出し側から見て予測しやすく、安全です。

一方で、「内部でフィールドを書き換える」「引数のオブジェクトを変更する」メソッドは、使い方を間違えるとバグの温床になります。 APIレイヤでは、「戻り値で何を返すか」を中心に設計し、「副作用が必要なメソッドは意図が伝わる名前とドキュメントにする」ことが大事です。

まとめと小さな練習

戻り値はメソッドの「出口」であり、APIレイヤの「約束そのもの」です。 プリミティブを返すときは「単純な値」「フラグ」「計算結果」をシンプルに返す場面に向いていて、 参照型を返すときは「意味のあるオブジェクト」「複数情報」「結果がない可能性」を表現したい場面に向いています。

練習として、次のようなメソッドを自分で設計してみると感覚がつかめます。

ユーザーIDを渡すと「ユーザー情報+権限一覧」を返すメソッド(参照型の戻り値) 注文金額を渡すと「割引後金額だけ」を返すメソッド(プリミティブの戻り値)

そのとき、「呼び出し側がどう使うか」「結果がないときどう表現するか」を意識して戻り値の型を選ぶと、API設計のセンスがどんどん磨かれていきます。

タイトルとURLをコピーしました