Appresso Engineer Blog

アプレッソのエンジニアが書く技術ブログです。

Effective Java 第三版、ゲットだぜ!

皆さんこんにちはこんばんは、開発部の陳です。

この間の大雪の積雪の影響で、首都圏の交通事情が一時期ひどいことになりましたね。 そんな大変な状況でも、物流業の方々はみんなの手元に荷物がちゃんと届くように頑張り続けていました。 おかげで、去年の夏に予約した Effective Java 第三版を無事に入手しました!

というわけで、その気になる内容をかるく紹介して行きたいと思います。

第二版と第三版、構成がどうか変わったか

2008 年に第二版が出版されてから、Java はメジャーバージョンアップを3回もリリースして、第三版では、元の Java 6 向けの内容を Java 9 までの変更を含めて一通り改定しました。

その影響でボリュームが少し増えて、第二版の 11 章 78 項目という構成に対して、第三版では 12 章 90 項目になっていて、新しい章は第 7 章の "Lambdas and Streams" です。(ちなみに、初版は 10 章 57 項目、第二版で第 5 章の "Generics" が追加された)

二刷までの正誤表はこちらになります。

手元の本は誤植全部入っています…初刷ですね!ヽ(`▽´)/

無くなった項目

第二版の項目 73「スレッドグループを避ける」が無くなりました。

どのような経緯で消えたからは分かりませんが、ThreadGroup 自体は想定された目的(セキュリティのためにアプレットを隔離する仕組み)を果たされていないし、他の目的に転用しようとしてもより適任なクラスがあるし、わざわざ取り上げる必要もないでしょうね。

新しい項目

第三版の新しい項目は全部13件です。以下では各項目の概要を、個人的なコメントを交えて紹介していきます。

内容はまだじっくり読んでいないので、鵜呑みせずに「こんな考えもあるよ」という程度で捉えていただければと思います。

ちなみに、日本語の項目名は第二版邦訳のスタイルを真似した適当な意訳です。

項目 5: 固定リソースより依存性の注入を選ぶ *1

カプセル化の力加減についてのアドバイスです。

ロジックの柔軟性、再利用性とテストしやすさを意識するように、コンポーネントの依存先リソースをコード内で直書きしないで、必要に応じて差し替えられるような手段(コンストラクタもしくはセッター)を用意しておこう、という内容です。

項目 9: try-finally より try-with-resources を選ぶ *2

Java 7 で try-with-resources が登場してだいぶ経ったので、既に実践している方が多いでしょう。

try-with-resources はクローズもれを防いでくれるだけでなく、複数リソースを扱う際の読みやすさも try-finally よりはるかに上なので、リソースが AutoCloseable を実装していない場合を除き、実践しない理由はどこにもないですね。

項目 21: 後世のために、インタフェースを設計する *3

Java 8 で登場したデフォルトメソッドのお陰で、インタフェースを拡張する際のインパクトは以前よりすっと小さい程度に抑えられるようになりました。

しかし、デフォルトメソッドによるインタフェースの拡張は、すべての既存実装と上手く共働き協働するとは限りません。

インタフェースの拡張が簡単になったとしても、インタフェースの設計自体はやはり以前と変わらず、慎重に行うべきです。

項目 25: ソースファイルの範囲を単一トップレベルクラスに限定する *4

一つのソースファイルにトップレベルのクラスを複数入れられるが、コンパイル時のエラーの原因になる可能性があるし、読みやすさに悪影響も及ぼすから、一つだけにしょう。

項目 32: ジェネリクスと可変長引数を注意して合わせて使用する *5

Java 7 で登場した @SafeVarargs を使用すれば、メソッド引数でジェネリクスの可変長引数を使用するのは以前より快適になっています。

ただし、@SafeVarargs はメソッドの型安全性を約束していることを示すアノテーションに過ぎないので、安全性を保証できないメソッドにつけるべきではありません。

ジェネリクスの可変長引数の型安全性を保証する実装例と、@SafeVarargs を使用するルールを説明する項目です。

項目 42: 匿名内部クラスよりラムダ式を選ぶ *6

数行のコードなら、ほとんどの場合、ラムダ式は匿名内部クラスより読みやすいので、ラムダ式を使うべきです。逆に、コードのロジックが複雑で行数が多い場合、ラムダ式にしないほうが賢明ですね。

項目 43: ラムダ式よりメソッド参照を選ぶ *7

(自明なメソッド名なら)メソッド参照はラムダ式よりも読みやすいし、メソッド自体もテスト可能なので、メソッド参照が使える場合は積極的に使いましょう。

項目 44: 標準関数インタフェースを使用する *8

多くの場合、標準関数インタフェースだけで事足りるので、自前でインタフェースを定義する必要性はあまりないですが、この項目はその必要性の判断基準と、定義する際の注意点を説明する項目です。

項目 45: ストリームを注意して使用する *9

無闇にストリームを使いまくると、可読性と保守性に悪影響をおよびす恐れがあります。ストリームを上手に使うためのヒントを提示する項目です。

項目 46: ストリームでは、副作用ありの関数より副作用なしの関数を選ぶ *10

ストリームのパラダイム自体は関数プログラミングに基づいているので、ストリームの表現力と速度と並列化の可能性を最大限に活かすには、使用する関数の同じパラダイムに従う必要があります。

副作用のある関数を使用する場合、一つ一つの処理はストリーム内の中間状態だけでなく、外部にも依存しているので、全体の流れが分かりづらくなるかもしれません。速度も並列化の可能性も、外部の依存先による影響が大きいので、ストリームの利点を活かすのは難しいでしょう。

項目 47: 戻り値として、ストリームよりコレクションを選ぶ *11

コレクションはイテレーションとストリームアクセスを同時に提供できるので、シーケンスを取得する public なメソッド戻り値として、可能であればコレクションを利用しましょう。コレクションを利用できない場合は StreamIterable を使いましょう。

項目 48: ストリームの並列化は注意深く使用する *12

並列化することによって、ストリームのパフォーマンスが改善されるのは、ストリームのデータソースに大きく依存しているので、手当たり次第ストリームを並列化するのはやめましょう。

項目 55: Optional を注意して返す *13

Optional を返すメソッドで null を返さない、コンテナを Optional でラップしない、プリミティブ型の場合は専用のものを使うなど、Optional を使うにあたって知るべきアドバイスを教えてくれる項目です。

おわりに

新しい項目以外に、元々あった内容も現状に合わせて一通り改定したようです。読みがいありそうですね。

近いうちアプレッソ内で第三版の勉強会は開催されると思うので、その様子と内容を追って共有していきたいと思います。

*1:Item 5: Prefer dependency injection to hardwiring resources

*2:Item 9: Prefer try-with-resources to try-finally

*3:Item 21: Design interfaces for posterity

*4:Item 25: Limit sources files to a single top-level class

*5:Item 32: Combine generics and varargs judiciously

*6:Item 42: Prefer lambda to anonymous classes

*7:Item 43: Prefer method references to lambdas

*8:Item 44: Favor the use of standard function interfaces

*9:Item 45: Use streams judiciously

*10:Item 46: Prefer side-effect-free functions in streams

*11:Item 47: Prefer Collections to Stream as a return type

*12:Item 48: Use caution when making stream parallel

*13:Item 55: Return optionals judiciously