あなたの “Java力”を試してみない? 「Javaパズラー」が帰って来た!──Java Day Tokyo 2013レポート

builder by ZDNet Japan Ad Special
2013-07-26 11:15:00
  • このエントリーをはてなブックマークに追加

JavaOneの人気セッションがJava Day Tokyo 2013に登場

 「帰ってきたJavaパズラー」は、日本Javaユーザーグループ(JJUG)の協力によって実現したセッションである。グリーの岡崎隆之氏、JJUGの橋本吉治氏、日本オラクルの大渕雅子氏らが次々にクイズを出題し、それに対して参加者が挙手で回答するかたちでセッションは進められた。


出題者の大渕氏、岡崎氏、橋本氏(写真左より)

 クイズの内容は、提示されたコードを実行した結果がどうなるのかを4択から選択するというものであり、すべての設問に"Java SE 8"などのバージョン指定がある。参加者は短い時間でコードの内容を読み取り、実行結果を予想しなければならない。

 それでは、具体的にどのような問題が出題されたのだろうか。以下に当日の出題から2つを抜粋して紹介しよう。

クラスBigDecimalのメソッドequalsの理解度を試す問題

 1つ目は、クラスBigDecimalのメソッドequalsの理解度を試す問題だ。出題されたのは貸借対照表に関するコードで、総資産(equity)と負債(liabilities)を足した額が純資産(asset)と等しければ1カウントされるという内容である。

 このコードでは、Java SE 8で新たに導入されるラムダ式が使われている。内容は、純資産と総資産、負債の値が納められた2つのコレクションをストリームに変換して、並列実行を許可しつつ、フィルタを使って条件に合致するストリームを取り出してカウントし、最後にカウント数を出力するというものだ。このコードを実行して得られる結果は、果たして「0」、「1」、「2」、「不定」のどれだろうか。

 ここでのポイントは、クラスBigDecimalの値の扱いである。最初のコレクションでは純資産として「"100"」が代入されており、総資産と負債を足した値は「100.00」になっている。次のコレクションは純資産が「"10000"」、総資産と負債を加算した結果は「10000」となる。

 実は値の比較でクラスBigDecimalのメソッドequalsを使用した場合、小数点以下の桁数も比較対象になる。つまり、2つ目のコレクションの純資産「"10000"」と総資産「10000」は等しいと判断されるが、1つ目のコレクションの「"100"」と「100.00」は等しくないと判断されてしまうのだ。

 例題では、フィルタの中でメソッドequalsを使っているため、2つ目のコレクションだけが等しいと判定されてカウントアップが行われ、結果として「1」が出力されることになる。ちなみに、メソッドcompareToを使用した場合、桁数を無視して値を比較することができる。

switch文の盲点を突く問題

 次の出題は、HTMLタグのHTML5での対応状況を出力するコードだ。Mapの中には「<Progress>」、「<tt>」、「<applet>」という3つのタグと、それぞれの対応状況(new=新規追加、deprecated=非推奨、removed=廃止)が入っている。その後、switch文で入力された文字列と一致するキーのデータを出力するという流れである。

 このコードでポイントとなるのは、入力された文字列(出題ではapplet)が括弧でくくられていない場合、それを付加するためにメソッドdecorateを呼び出している部分だ。このメソッドでは、クラスStringBuilderを使い、入力された文字列の前後に括弧を付加している。

 回答の選択肢として提示されたのは、「"<applet>はHTML5から削除されました"と出力される」、「何も出力されない」、「例外がスローされる」、「コンパイルエラー」の4つである。

 それでは、コードの内容を検討していこう。

 まず、このコードには明らかなミスがある。それは、switch文の各caseにbreak文がないことだ。これにより、想定した出力は得られないことから、まず「"<applet>はHTML5から削除されました"と出力される」という選択肢を除外できる。

 次にコンパイルエラーの可能性を考えてみよう。可能性としてありうるのは、switch文で文字列を条件として指定している部分である。確かにJava SE 6まではswitch文の条件として文字列を使うことはできなかったが、Java SE 7からはそれが可能になっている。したがって、このコードはコンパイルエラーにはならない。

暗黙変換が思わぬトラブルを引き起こす

  残る可能性は「何も出力されない」と「例外がスローされる」のいずれかである。ここでメソッドdecorateを見てみると、クラスStringBuilderをインスタンス化している個所で、コンストラクタ呼び出し時にchar型となる文字列(<)を指定している。だが、クラスStringBuilderのコンストラクタにchar型はないので、結果として、文字を持たず、引数で指定された初期容量の文字列ビルダを構築するint型の「99」に変換されることになる。

 その後にある、引数をシーケンスに追加するメソッドappendは正常に動作するので、nameの内容(applet)と「<」がシーケンスに追加される。これにより、最終的に「applet<」が戻り値となる。

 ここで元に戻ってみると、Mapのstatusに「<applet>」はあるが、「applet>」はない。したがって、switch文の条件を「status.get(key)」としているが、対応するstatusがないためnullが返されることになる。

 そして、ここが"味噌"なのだが、Javaの仕様ではswitch文の評価結果がnullである場合、例外NullPointerExceptionがスローされてしまう。

 つまり、正解は「何も出力されない」ではなく、「例外がスローされる」である。

 こうしたchar型からの暗黙変換によるミスは、静的解析ツールや統合開発環境(IDE)の警告をチェックすることなどで発見できるので、積極的に活用してほしい。また、switch文で文字列を利用する際にはnullチェックを行うようにすることも重要だろう。

 以上、ここでは2つの問題に絞って紹介したが、帰ってきたJavaパズラー・セッションでは、参加者を悩ませつつも大いに参考になる問題が多数出題された。次回も開催されることがあれば、腕に覚えのある方はぜひご参加いただきたい。

このサイトでは、利用状況の把握や広告配信などのために、Cookieなどを使用してアクセスデータを取得・利用しています。 これ以降ページを遷移した場合、Cookieなどの設定や使用に同意したことになります。
Cookieなどの設定や使用の詳細、オプトアウトについては詳細をご覧ください。
[ 閉じる ]