Javaについて徹底解説!

Javaでメソッドから戻る・戻り値を戻す returnの使い方と活用方法

大石 英人

開発エンジニア/Java20年/Java GOLD/リーダー/ボールド歴2年

Javaでのreturnは、メソッドの実行を終了させて呼び出し元に処理を戻す時と、呼び出し元に戻り値を戻す時に使うものです。

と、このようにreturnは単純なものではありますが、時にプログラマを惑わせます。特に、try-catch-finallyと組み合わさった時は、人によっては直感と反する動きになります。

そして、メソッドのどこでどうやってreturnをするか。これはプログラムの分かりやすさにも大きく影響する、なかなか侮れないものなのです。

この記事では、returnの使い方を基本からお伝えします。そして、分かりやすいプログラムを作るためのreturnもお伝えしますので、ぜひこの記事で色々なコツを学んで行ってください。

※この記事のサンプルは、Java 11の環境で動作確認しています


1.returnとはどういうものか

1-1.returnはメソッドをすぐ終了させ、戻り値を戻すもの

returnを実行する(returnする」ともよく言います)と、メソッドの実行をその場ですぐに終了させて、プログラムの実行箇所を呼び出し元に戻します。さらに、メソッドの処理結果となる値を呼び出し元に戻す役割も持っています。

ちなみに、プログラムでreturnという単語が使われるのは、プログラムの制御を呼び出し元に「戻す」、あるいは処理結果を呼び出し元に「戻す」からです。

1-1-1.メソッドの実行をその場ですぐ終了させる

returnすると、returnよりも後ろに書いてある処理は実行されません。さらに、returnより後ろの処理がいかなる状況でも動かないことが明確な場合は、コンパイルエラーになってしまうことがあります。

実行結果

1-1-2.メソッドの処理結果を戻す

returnすると、メソッドから呼び出し元に値を戻せます。メソッドの呼び出し元では、戻り値をメソッドの処理結果として使うのが普通です。どういう値が戻せるかは、メソッドの宣言のされ方によります。詳細は次の章でお伝えします。

1-2.returnはメソッドの中ならいつでもできる

returnできるのはメソッドの中に限られています。細かく言えば、メソッドの中か、メソッドの一種ともみなせるコンストラクタの中か、メソッドを実装することと同じ意味であるラムダ式の中にしか書けません(※Java 11時点では)

returnはいつでもできます。ですから、「もうこのメソッドの処理は終わりでいいや」となったら、その時点ですぐさまreturnできます。ですからfor/while文のようなループ構文の中、if/switch文のような条件判定の中、それ以外の部分でもreturnできます。


2.returnと戻り値

returnは、一つのメソッドの中では戻り値がある・ないの必ずどちらか一方です。どちらになるかはメソッドの宣言のされ方、特に戻り値の有無で決まります。

2-1.戻り値がvoidでないなら戻り値が必須

メソッドの宣言で戻り値がvoidではないなら、returnには戻り値となる値、あるいは戻り値とするインスタンスを指す変数を「必ず」指定しなければなりません。つまり、以下の書き方になります。

それに、メソッドのどこかで、戻り値があるreturnを「必ず」しなければなりません。そうしないとコンパイルエラーです。メソッドの戻り値が分からなくなってしまうからですね。メソッドの戻り値は、メソッドが呼び出したなら必ずその型の値が戻るという「約束」です。それは絶対に守る必要があるのです。

なお、他のプログラミング言語では、プログラマがreturnを書かなくても、メソッドや関数の中で「最後に評価された式の結果」が戻り値になるものがあります。ですが、Javaでは必ず明示的にreturnをしなければなりません。これはJavaでは絶対に守らなければならないルールです。

2-2.returnの戻り値は互換性がある値や型であればよい

returnの戻り値は、プリミティブ型ならその値か変数、参照型(≒クラス、配列)ならその型と見なせるインスタンスを指す変数かnullです。プリミティブ型は分かりやすいのですが、参照型は少し注意が必要です。ここに、オブジェクト指向プログラミング言語であるJavaの特徴が現れます。

例えば、以下のメソッドでreturnできるのは、NumberそのものかNumberのサブクラスのインスタンスです。具体的にはInteger/Long/Doubleなどです。なぜかと言うと、IntegerDoubleNumberとの間で「IS-A関係」と言うものが成立し、IntegerDoubleNumberだとみなせるからです(Integer is a Number)

【参考】is-a(Wikipedia)

https://ja.wikipedia.org/wiki/Is-a

この約束事が守られていないクラスのインスタンスは戻り値にはできません。ですので、以下のメソッドはコンパイルエラーになるのです。

2-3.戻り値がvoidなら戻り値を返せない

メソッドの戻り値がvoidなら、returnには戻り値を指定できません。戻り値を指定すると、コンパイルエラーになります。つまり、以下の書き方だけになります。なお、コンストラクタでreturnする場合も、コンストラクタには戻り値はない(書かないですよね?)扱いがされるので、こちらの書き方になります。

メソッドの中でreturnをすることは、メソッドの戻り値がvoidなら必須ではありません。メソッドの終わりまで行きつけば、そこでメソッドの実行は自動的に終わって、呼び出し元に処理が自動的に戻ります。

ですので、戻り値がないメソッドなら、returnすべきところにだけ書くのが普通です。もちろん、returnを書いてもコンパイルエラーにはなりません。でも、他のプログラマからは「余計なreturnだなぁ、returnのことをわかってるのかなぁ」と思われてしまうかもしれませんよ。


3.【発展】returnのいろいろ

3-1.returnでは三項演算子を上手に使おう

三項演算子は、使う人は積極的に使い、使わない人は全然使わないという、プログラマの間でも好き嫌いが大変激しい演算子です。でも、三項演算子はreturnと大変相性がいいものでもあります。

三項演算子は文ではなく式なので、returnのパラメータとして記述できます。すると、それがreturnのための記述であることが明確になります。さらに構文上でelse扱いの記述が必須なので、if文でうっかりelseを書き忘れるというような凡ミスを防げもします。

しかも、この例ではif文で5行使う処理を三項演算子では1行で書けていて、やっていることは(慣れてしまえば)一目瞭然です。if全体を視線を動かして調べるよりも、ぱっと見るだけでreturnの全体・意図が分かるのが良いと感じます。

もちろん、三項演算子は複雑な条件は苦手なので、if文とは適材適所です。でも、分かりやすくて読みやすいプログラムになるかもしれませんので、食わず嫌いをするよりも、簡単なところからだけでも使ってみてはいかがでしょうか。

3-2.try-catch-finallyとreturnにはご注意を!!

さて、以下のようにtry-catch-finallyのそれぞれでreturnを書いた場合、メソッドtryCatchFinallyの戻り値はどうなるでしょうか。tryの中で例外がthrowされない場合、throwされる場合で分けて考えてみましょう。

答え合わせです。例外がthrowされない場合は3、される場合も3です。合っていましたか? なぜかこうなるかと言うと、一番最後に実行されるfinallyreturnをしているからです。

実行結果

イメージ的には、try-catch-finallyの中でreturnすると、returnする値がJava上のどこかに予約され、returnが動くと上書きされるという感じでしょうか。ですので、try-catch-finallyの中で一番最後に実行されるfinallyreturnした値が、最終的なメソッドの戻り値になるのです。

3-2-1.例外がthrowされない場合

この場合は、try→finallyの順番で動きます。例外がthrowされていませんので、catchは動きません。finallytryの結果が何であれ、tryの後に必ず実行されます。もう少し細かく言うなら、tryreturnが動いた後に実行されます。

tryが終わった時点で、returnする値としては1であるとJavaは一旦覚えます。ですが、finallyでもう一度returnが呼ばれました。ですので、returnする戻り値がfinallyで指定されている3に入れ替わります。

3-2-2.例外がthrowされる場合

この場合は、try→catch→finallyの順番で動きます。例外が途中でthrowされているのでtryreturnまでは到達しませんが、catchreturnは動きます。finallycatchの結果がどうであれ、catchの後に必ず実行されます。もう少し細かく言うなら、catchreturnが動いた後に実行されます。

catchが終わった時点で、returnする値としては2であるとJavaは一旦覚えます。ですが、finallyでもう一度returnが呼ばれました。ですので、returnする戻り値がfinallyで指定されている3に入れ替わります。

3-2-3.finallyでreturnするならtry/catchに要注意!!

ということで、finallyreturnするとtrycatchreturnしても、メソッドの戻り値としては無視されてしまうことがお分かりいただけたかと思います。ですので、finallyreturnする時は、意図に反した戻り値にならないように、trycatchreturnしていないかをよく調べましょう。

もしfinallyでだけでreturnをしたいなら、戻り値を入れる変数をtryの外で宣言して使うなどしてもいいでしょう。

3-3.returnで複数の値を戻す方法あれこれ

Javaのreturnで戻せるモノは一つだけです。他のプログラミング言語では複数の値を戻す構文、いわゆるタプル(tuple)と呼ばれるものなどがありますが、残念ながらJava 11の時点では一つだけです。

それでも、Javaでも複数の値を戻す方法がないではありません。ここではその方法をいくつか紹介します。

3-3-1.配列/List/Mapを使う

最も単純な方法は、配列やListを使うことです。この場合は、インデックスごとにどんな値が入るのかを、仕様やJavadocで明確にしておく必要があります。ただ、インデックスを間違えてしまうと必要な値を得られないので、呼び出し元では十分に気を付ける必要があります。

さらに柔軟にやりたいならMapを使います。キーとして文字列を使うと分かりやすいです。キーとする文字列はあらかじめ決めておき、クラスを使う人に教えておきます。これもキーを間違えてしまうと値が取得できないので、気を付けましょう。

この方法を使う場合、戻したい値の型が一つなら、まだ大きな問題はありません。しかし、違う型を使いたい場合は最悪Objectにせざるを得ず、呼び出し元でキャストが必要になります。キャストが必要だと、プログラムもしづらいものになりますし、深刻なバグの原因にもなりえます。

3-3-2.戻り値専用のクラスを使う

戻り値を表現するためのクラスを作って使う方法です。これなら戻り値の型を必要なものだけにできますし、値を取得する際にインデックスやキー文字列を意識しなくてもいいので、お勧めです。戻したい値はコンストラクタで設定し、クラスは値を取得するgetterのみを持つようにするといいでしょう。

ただし、複数の値を戻したいメソッドが多くなると、これだと専用のクラスをたくさん作ることになり、ちょっと大変になってきます。それに、値として扱うクラスが少しだけ違うものがプログラム全体でたくさん作られてしまうことにもなりかねません。そうなると、プログラムはカオスまっしぐらです。

3-3-3.複数の戻り値を柔軟に管理できるクラスを作る

もっと柔軟なやり方にしたいなら、型引数を活用して、複数の戻り値を汎用的に扱うことに特化したクラスを用意するといいでしょう。以下の例では、二つの値がペアになるので、Pairというクラスにしています。値を増やすなら、数に応じた名前にしましょう(三つならTripleとかです)

なお、このようなクラスと似たものは、標準APIの中に一応あります(javafx.util.Pairjava.util.AbstractMap.SimpleEntryなど)。でもクラスのパッケージがJavaFXだったりするので、なかなか気付きづらいです。それに、メソッド名がキー(Key)と値(Value)なので、ここで表現したいこととは方向性が違います。

また、こういうクラスを数多く提供する外部ライブラリもありますので、それらを使ってもいいでしょう。例えば、Apache Commons LangPairTriplejavatuples(https://www.javatuples.org/)PairTripletなどです。

3-4.thisをreturnする

returnでは自分自身をreturnできます。つまり、thisreturnできるのです。一見何に使うか分かりませんが、メソッドチェーンと呼ばれるものを使う場合にそうしたりします。

メソッドチェーンは、例えばStringBuilderappendをする時の常とう手段です。こうすることで、同じモノに対して連続して何かをしているのだということが、プログラム上でも明確になります。また、単純にプログラムの行数を少なくしたり、見た目をスッキリさせる効果もあります。

これをさらに推し進めると「流れるようなインターフェイス(fluent interface)」と呼ばれるプログラミング手法に、いずれたどり着きます。ここでは詳細は述べませんが、興味があれば調べてみてもいいでしょう。参考になる知識が得られるでしょうし、既存のフレームワークでも使っているものは多いです。

3-5.nullかもしれないならOptionalreturnする

Optionalというクラスをreturnすると「戻り値がnullかもしれないから気を付けてね」と教えてあげられます。この章で、複数の値を戻す時はPairのようなクラスを使うとよいとお伝えしましたが、同じような考え方です。

NullPointerExceptionの主な発生理由は、メソッドの呼び出し元でのnullチェックのし忘れだったりします。メソッドの戻り値が参照型ならnullreturnでき、これはごく普通のことですが、メソッドの呼び出し元でnullのチェックをしてくれるとは限りません。

でも、Javaではメソッドの呼び出し元へnullチェックを強制することが、文法上で出来ません。ですから、Optionalを使って本当の戻り値を包み込んでreturnして、本当の戻り値をOptionalから取得する時にnullチェックをしてもらうのです。

詳細は以下の記事を参照してください。

関連記事

4.【発展】returnのスタイル

returnの「スタイル」はいろいろあります。大きな分け方では、メソッドの途中で即座にreturnする(早期return)メソッドの最後や決まった場所だけでreturnするの二つです。実際にはの折衷案になるのですが、それぞれメリット・デメリットがありますので、簡単に紹介します。

4-1.【お勧め】条件次第で即returnする「早期return

4-1-1.早期returnの特徴

早期return(early returns)は、メソッドを実行する必要性や理由がなくなったなら、その時点ですぐreturnするスタイルです。見た目上の特徴は、if文でのelseが少なく、各if文の条件が比較的シンプルです。代表的な適用先は引数チェックですが、それに限りません。そして、私のお勧めスタイルはこちらです。

このスタイルの利点は、if文の構造や判定条件が簡単になること、メソッド内のインデントがかなり減ることです。つまり、メソッドの構造が簡潔になるのです。でも、よくあるプログラムの仕様書にある条件とは、処理結果は同じでも違った形になるので、ソースレビュー時に修正指示が出るかもしれませんね。

この例だと、条件2に処理が来るのは、条件1を満たさない場合だけです。ですので、条件1の後ろはif-elseelseの部分だとも言えます。同じように、条件3に処理が来るのは、条件1と条件2の両方を満たさない場合です。途中にelseはありませんが、returnにより実質的に同じことを表現できています。

ですから、早期returnを積極的に使うスタイルは、目には見えない大きなif-else if~を、メソッド全体で表現するスタイルであるとも言えるでしょう。

4-1-2.早期returnはメソッドを読むのに必要なことを少なくする

早期returnのうれしい点は、returnした後ろではreturn済みの条件を考えなくてもいいことです。プログラムを作ったり読んだりする作業は、仮定や条件が少なくなればなるほど、やりやすくなるものです。早期returnをすれば、プログラマが一度に覚えたり考えなければならないことがグンと減るのです。

ちなみに、ループ部分でのbreakcontinueにも早期returnと同じ考え方ができます。早期breakや早期continueという言葉は聞いたことがありませんが、早期returnと同じように早めにbreakなりcontinueをすれば、ループ部分がかなり読みやすくなるのです。

4-2.メソッドの最後や決まった場所だけでreturnする

もう一つのreturnのスタイルは、returnをする箇所はメソッドの最後やメソッドの途中の決まった箇所だけとするものです。

このスタイルだと、どこから入ってどこから出る、という流れが分かりやすくはなります。それに、よくあるプログラムの設計書に書いてあるような、処理全体の流れやいろいろな条件を素直に作ると、こういう書き方になるのが普通でしょう。

ですが、このスタイルは判定条件が複雑になりがちで、そうなるとメソッド全体のインデントが深くなり、見通しが悪くなることが多いです。一つのifに対応するelse-ifが、数十行~数百行も後ろにあるなどということもよく起こります。それに全ての条件を網羅した、ものすごく複雑なif文にもなりがちです。

そういうメソッドを理解するのはかなり大変です。なぜかと言うと、人間が一度に覚えていられることはせいぜい数個だからです。極端な話、メソッドの最後でだけでreturnをするなら、return直前ではメソッドの中で宣言した全ての変数が見えるのですが、それぞれどういう状態か理解するのはすごく大変です。

4-3.できる範囲で早期returnをしよう!!

実際のプログラミングの現場では、結局はこの二つのスタイルが混ざるのが普通です。早期returnをするなら仕様書などにあるロジックを解釈して、同じ動きをするように再構成しなければなりませんが、そこでミスをしないとも限りません。早期returnを意識して仕様を書いてくれる人は少ないでしょうしね。

ですが、テスト駆動開発などの「このメソッドはこう動くべき」ということを事前に明確にするプログラミングスタイルならば、失敗を恐れることなく、積極的に早期returnへチャレンジしてもらいたいです。その効果を実体験できたならチームの中にも広めて、読みやすいプログラムを作るチームを作りましょう。


5.まとめ

この記事ではJavareturnについてお伝えしてきました。returnでメソッドを終了させる、そして戻り値を戻す。returnはプログラミングをする上での基本的なことではありますが、returnの正しい理解はプログラムには絶対に欠かせないものです。

そして、プログラムの中でreturnをどう実行するかは、プログラマにすべて任せられています。早期returnなどの工夫をすると、より分かりやすいプログラムに一歩近づけるでしょう。

あなたや周囲のプログラマが作ったプログラムは、分かりやすく読みやすいものでしょうか? もし、少しでも読みづらいなぁと感じているなら、returnの仕方に注目してみると、もしかすれば解決への糸口がつかめるかもしれませんよ。

私たちは、全てのエンジニアに市場価値を高め自身の望む理想のキャリアを歩んでいただきたいと考えています。もし、今あなたが転職を検討しているのであればこちらの記事をご一読ください。理想のキャリアを実現するためのヒントが見つかるはずです。

『技術力』と『人間力』を高め市場価値の高いエンジニアを目指しませんか?

私たちは「技術力」だけでなく「人間力」の向上をもって遙かに高い水準の成果を出し、関わる全ての人々に感動を与え続ける集団でありたいと考えています。

高い水準で仕事を進めていただくためにも、弊社では次のような環境を用意しています。

  • 定年までIT業界で働くためのスキル(技術力、人間力)が身につく支援
  • 「給与が上がらない」を解消する6ヶ月に1度の明確な人事評価制度
  • 平均残業時間17時間!毎週の稼動確認を徹底しているから実現できる働きやすい環境

現在、株式会社ボールドでは「キャリア採用」のエントリーを受付中です。

まずは以下のボタンより弊社の紹介をご覧いただき、あなたの望むキャリアビジョンをエントリーフォームより詳しくお聞かせください。