虎の穴開発室ブログ

虎の穴ラボ株式会社所属のエンジニアが書く技術ブログです

MENU

JavaでPuzzler!!

みなさん、こんにちは。虎の穴ラボのH.Kです。
今回はJavaでPuzzlerというものを作ってみたいと思います。

Puzzlerとは?

Puzzlerという言葉自体は「難問」という意味を持っています。
もともとJavaのカンファレンスで言語仕様や見落としがちな点を解説する際にクイズ形式で紹介したことが始まりのようです。
問題を私も作ってみたので、ぜひ考えてみてください。

例題

とりあえず例題として定番のStringの比較を見ていきます。

public class Main {
    public static void main(String[] args) {
        String a = "a";
        String b = new String(a);
        System.out.println(a == b);// (例題)何が出力される?
    }
}

(例題)何が出力される?での出力値を問うパズル(というかクイズ)です。

答えと解説(クリックで開きます)

答え:false
Javaの比較演算子の==は参照値を比較します。
Javaのデータ型は大きくわけてプリミティブ型(byte, int, charなど)と参照型(Object, Integer, List<Object>など)があり、Stringは参照型になります。
new Stringで文字列を生成する場合、新しい参照先が確保されるため、aとbを==で比較するとfalseになります。
文字列を比較するときはequalsメソッドを使いましょうという問題でした。

このようなパズルを考えて、Javaの仕様への理解を深めることが目的です。

問1:Exceptionのハンドリング

public class Main {
    public static void main(String[] args) throws Exception {
        try {
            try {
                throw new Exception("throw");
            } catch (Exception e1) {
                throw new Exception("catch", e1);
            } finally {
                throw new Exception("finally");
            }
        } catch (Exception e2) {
            System.out.println(e2.getMessage());// (問1)何が出力される?
        }
    }
}

このプログラムを実行すると何が出力されるかという問題です。
選択肢としては、throwcatchfinally何も出力されないの4択くらいかなと思っています。

答えと解説(クリックで開きます) 答え:finally
順を追って見ていきます。
まずmessageにthrowが入ったExceptionがthrowされます。
その後、catch (Exception e1)のcatch句に入り、messageを設定したExceptionを生成し、再度throwします。 Exceptionが発生しようがしまいが、関係なくfinally句が実行されるため、messageにfinallyが入ったExceptionがthrowされます。
外側のtry句ではtry内でthrowされた例外をcatchするのでfinallyでthrowされたものが入る、という流れになります。
こういう実装をしてしまうとtry-catchで生成したExceptionは実質的には握り潰されてしまうので注意が必要です。
メソッドにthrows Exceptionを書いたのはただの雰囲気です。

問2:Listのメソッドの戻り値

public class Main {
    public static void main(String[] args) {
        List<Object> list = new ArrayList<>();
        list.add("No1");
        System.out.println(list.set(0, list.add("No2")) + ":" + list.get(0));// (問2)何が出力される?
    }
}

この問題も実行した結果、何が出力されるかというものです。
よく使うListクラスのメソッドについて戻り値がどのようになるかというものですね。
List#setList#addの戻り値ってあまり使いませんがご存知ですか?

答えと解説(クリックで開きます) 答え:No1:true
ポイントは2点で各メソッドの戻り値と処理順(実行時にリストの0番地に何が入っているか)です。 まずJavadocからそれぞれの戻り値を確認してみましょう。
今回の処理ではJavaのVersionに依存しないですが、最新のJava SE 14のリファレンスをもとに見ていきます。
docs.oracle.com

java.util.ArrayList#add

docs.oracle.com ArrayListのaddメソッドを見ると必ずtrueが返却される仕様になっています。
ちなみにindexを指定するaddメソッドは戻り値なしです。

Returns: true (as specified by Collection.add(E))

ここで注目はtrue (as specified by Collection.add(E))=「(Collection.add(E)の指定により)true」という書き方です。
そもそもArrayListはListクラスの実装クラスであり、ListクラスはCollectionクラスをSuperInterfaceとしています。
そこでCollection.add(E)のほうの内容を確認するとtrue if this collection changed as a result of the callと記載があります。
つまり、Collectionクラスの実装クラスのaddメソッドは「呼び出しによってコレクションの中身が変更されたらtrueを返却する」ということが明記されていることになります。
Listの実装クラスではaddメソッドで中身を変更しないという結果にはならないので常にtrueを返却することになります。
例えば、同じCollection(をSuperInterfaceとするSet)の実装クラスに当たるjava.util.HashSet#addでは重複があった際はSet内に値が追加されない、すなわちコレクションの中身が変更されないのでfalseが返却されます。

java.util.ArrayList#set

docs.oracle.com ArrayListのsetメソッドは指定した場所の元の要素を返却するとあります。

Returns: the element previously at the specified position

例えば以下だとsetの戻り値はNo1になります。

List<Object> list = new ArrayList<>();
list.add("No1");
list.set(0,"No2");

java.util.ArrayList#get

docs.oracle.com これはみなさんご存知かと思いますが、一応リファレンスを確認しておきます。
getの戻り値は「リストの指定された場所の要素」となります。

Returns: the element at the specified position in this list

次に処理順です。
ワンライナーで書かれるとわかりにくいですが、以下の順番で実行されます。

  1. add
  2. set
  3. get

メソッドの戻り値と処理順を踏まえて、各メソッドの実行結果を順に書いていくと以下の通りです。

  1. list.add("No2") -> true
  2. list.set(0, list.add("No2")) -> list.set(0, true) -> No1(ここでlistの0番地にはtrueが入る)
  3. list.get(0) -> true

addメソッドの戻り値などは追っていくと、どうしてそうなるのか、ということがわかり面白いかと思います。

まとめ

2問ほど考えてみましたが、出題するのにもけっこうな知識が必要で、あらためてリファレンスを確認するなど、作る側も非常に勉強になるなと感じました。
匿名クラスでの変数の扱いなどはわかりにくいところもあるので次はそのあたりで問題を作ってみたいです。
今回はJavaで問題を作ってみましたが、当然他の言語でも可能です。
「へーこんな仕様なんだ!知らなかった!」みたいなのがあれば皆さんも問題を作ってみると、楽しみながら知識をつけることができるかもしれませんね。

P.S

虎の穴ラボ主催のオンラインライトニングトークイベントを 7/29(水)19:30〜 開催します!
今回もフリーテーマとなっており、ITに関連する内容であれば、何でも大歓迎ですので、初心者の方も練習の場としてお気軽にご参加ください! connpassにて参加受付中です!
yumenosora.connpass.com

虎の穴ラボでの開発に少しでも興味を持っていただけた方は、採用説明会やカジュアル面談という場でもっと深くお話しすることもできます。ぜひお気軽に申し込みいただければ幸いです。
カジュアル面談では虎の穴ラボのエンジニアが、開発プロセスの内容であったり、「今期何見ました?」といったオタクトークから業務の話まで何でもお応えします。

カジュアル面談や採用情報はこちらをご確認ください。
yumenosora.co.jp

また、毎週木曜にはTora-Lab Meetup!と称して虎の穴ラボのエンジニア・採用担当とお話できる機会を設けさせていただくことになりました。
虎の穴ラボに興味がある、エンジニアや採用担当に質問したいことがある、などどなたでもご参加下さい。
news.toranoana.jp

さらに、弊社では新型コロナウイルス感染症終息後もフルリモートを継続導入することになりました!
地方在住のまま働きたい人など、上記Meetupやカジュアル面談、面接すべてリモート対応していますので、ご興味のある方はぜひいずれか応募してみてください! prtimes.jp