この記事について
Effective Java 3rd Edition が発行されました。Java7以降のJava9までの新言語要素が加味されていて、そういうのはとりあえずLambdaなのだろうと思うのですがそれ以外に変わった・追加されたところが気になりました。そこで、第2版と第3版を横に並べて見比べてそういう所をざっくり確認してみました。
この記事では新規の Item についてではなく既存の Item 内で変化した所について「特徴的かも!」と思ったところを並べています。1 多分あなたがこの記事を読まれてもわかった気分になることは決してなくて、どちらかと言うと第2版を読んだことがある人が第3版を学び直す時の参考資料のような使いみちになると思います。誤解や抜け漏れてんこ盛りかと思いますので、間違い探しを楽しむぐらいの感覚でお願いします。
変化したところを全体的に言えば、シリアライゼーションのセキュリティリスクが強く言われるようになったことを除くと、それぞれの Item の趣旨はそれほど変わっていないです。また、Java7以降の文法や標準APIでより良く書けるところが追加されていたり、文書体裁がそれなりに修正されています。
紹介されているツールでは、Java Microbenchmark Harness が、なにげに便利かも思いました。(よく調べてませんけど。。。) http://openjdk.java.net/projects/code-tools/jmh/ Item 一覧は http://www.informit.com/store/effective-java-9780134685991 の、Sample Content から取得しました。
Chapter 2: Creating and Destroying Objects
Item 1: Consider static factory methods instead of constructors
- Java7リリース後となってはもはや長所と言えなくなったところが削除。
- Java8のインターフェースのデフォルトメソッドについての言及が入る。
- 2版で挙げられている長所を列挙しているところについて、構成が入替えられている。
Item 2: Consider a builder when faced with many constructor parameters
- recursive type parametor(Item30)を使ったクラス階層ありのビルダーのサンプルが追加されている。
- AbstractFactoryパターンについての言及が削除されている。
Item 3: Enforce the singleton property with a private constructor or an enum type
-
getInstance()
なるメソッドをもたせることで、メソッド参照が使えるという言及が追加。
Item 4: Enforce noninstantiability with a private constructor
- 特に特徴的な変化はない。
Item 5: Prefer dependency injection to hardwiring resources
新項目
Item 6: Avoid creating unnecessary objects
- サンプルコードが
RegEx
を使ったものに変わっている。(Calanderや、Dateが出る例を出したくなかったのかも。)
Item 7: Eliminate obsolete object references
- 特に特徴的な変化はない。
Item 8: Avoid finalizers and cleaners
- ファイナライザが非推奨になってクリーナーが追加されたが、これも使用を避けるべきという内容が追加。
Item 9: Prefer try-with-resources to try-finally
新項目。
Chapter 3: Methods Common to All Objects
Item 10: Obey the general contract when overriding equals
- equals() hashcode() の実装には、google の Autovalueが紹介されている。「IDEの自動生成は、クラスの変更に追随出来ない。きれいでもない。手で自作するよりはましな程度である」とのこと。 https://github.com/google/auto/tree/master/value
Item 11: Always override hashCode when you override equals
- サンプルのPhoneNumberクラスがItem10に記載される構成に変わった。
- ハッシュ値の生成に、Type.hashcode(f) を使う事を勧める内容になった。
- Guava のHashing クラスを使うやり方も紹介されている。 https://github.com/google/guava/wiki/HashingExplained
- hashcodeの詳細仕様を提供しないことが強調されるようになった。
Item 12: Always override toString
- staticユーティリティークラスや列挙体にtoString()は不要という段落が追加された。
- toString()の内容を生成するのにAutoValueが推奨という段落が追加された。
Item 13: Override clone judiciously
- Object#clone() の解説が全面的に書き直されている。
- 「不変クラスはclonableであってはならない。オブジェクトを無駄に生成するから。」という内容が加わった。
- 配列をduplicate するのにclone()を使うのは良いイディオム。という内容が加わった。(有益かも!)
Item 14: Consider implementing Comparable
- compareTo()の実装においては比較演算子を使うよりも、staticな
compare()
メソッドが各プリミティブ型のラッパークラスに追加されたのでそちらの利用が推奨されるようになった。Double.compare()
Float.compare()
など。 - Java8から導入された、
Comparator.comparingInt
Comparator.thenComparing
と使った例の紹介が追加された。
Chapter 4: Classes and Interfaces
Item 15: Minimize the accessibility of classes and members
- P76から4段落分、Java9 のモジュールシステムについての段落が追加。
Item 16: In public classes, use accessor methods, not public fields
特に特徴的な変化はない。
Item 17: Minimize mutability
- functional アプローチ(操作によって変化した後のオブジェクトが返される。)と procedual ・ imperative アプローチ(操作によって自分自身が変化する)の違いについての説明に追記が入った。(P82)
- staticファクトリの長所についての段落が削除。(Item 1にもともとある内容なのでここでは消した様子。)
- 再初期化メソッドを作らないことの説明の例に
TimerTask
を使っているところがCountDownlatch
に変わった。(欠点があるクラスがその代替に変わった。)
Item 18: Favor composition over inheritance
特に特徴的な変化はない。
Item 19: Design and document for inheritance or else prohibit it
-
@implSpec
タグについての記載が追加。
Item 20: Prefer interfaces to abstract classes
- インターフェースにデフォルトメソッドが入った件が加味されている。
- P102の2~3段落目に追記変更。
- 最終ページの3段落削除。
Item 21: Design interfaces for posterity
新項目
- 将来を見越してインターフェースを設計しましょう。
- インターフェースのデフォルトメソッドはよく注意して使いましょう。
- デフォルトメソッドの導入が既存の実装と常に整合するとは限りません。
Item 22: Use interfaces only to define types
- 数字リテラルをアンダースコア区切りで書けることについて追記。
Item 23: Prefer class hierarchies to tagged classes
全体的な内容に変更は無い。
Item 24: Favor static member classes over nonstatic
- 匿名クラスの使い所の説明にラムダについての記述が加味された。
Item 25: Limit source files to a single top-level class
新項目。
- 1個のソースには1個のトップレベルクラスのみを入れるようにすること。(いわれてみれば当たり前のことではあるが。)
Chapter 5: Generics
Item 26: Don’t use raw types
- 2版では don't use raw types in new code. で、 新規コードで原型を使うなという内容だったが、3版では新規コードという制限はなくなった。(新旧いずれでも、原型は使うなという意味と思われる。)
-
java.util.Date
を使った例が、BigInteger
に代わっている。(例としても出さないという意図か)
Item 27: Eliminate unchecked warnings
- ダイヤモンドオペレータが使えることの記述が入った。
Item 28: Prefer lists to arrays
- 後半のサンプルコードがシンプルなものに変更された。
Item 29: Favor generic types
- 途中の説明の1段落が簡素な内容に書き換わっている。
Item 30: Favor generic methods
- Java7の総称型の省略でまかなえる内容を持つ段落が削除。 P130 中程~P131開始4分の1まで。
- サンプルコードがラムダ式を意識した例に一部書き換わっている。
Item 31: Use bounded wildcards to increase API flexibility
- PECSの説明に使うソースが変更されている。
- Explicit type parameterについて、「Java8より前ではExplicit type parameterが必要」という説明になっている。
- max() メソッドに修正が必要という段落が消えている。(事実であるが、このItem の主張ではないから?)
Item 32: Combine generics and varargs judiciously
新項目。
Item 33: Consider typesafe heterogeneous containers
- ほとんど変化なし。小さな修正と補足説明が入った程度。
Chapter 6: Enums and Annotations
Item 34: Use enums instead of int constants
- Enumの値を削除したときの挙動についての段落が追加。
- enum 値のコンストラクタからEnumのstaticフィールドにアクセスする出来ないことについての段落が加筆。
- 説明のサンプルソースをOptionalを使うようにする。
- enum 値固有の振る舞いを定義するStrategy enum patternについての説明の段落が1個追加。
- enumはパフォーマンスでわずかに不利という説明に、携帯電話のようなリソースが厳しい環境を除くと言う記述が追加。
- enumをいつ使うべきかという段落に加筆多少。
Item 35: Use instance fields instead of ordinals
- 全体的な趣旨に変化なし。
Item 36: Use EnumSet instead of bit fields
- 不利な点について記述してある段落に加筆。
Item 37: Use EnumMap instead of ordinal indexing
- サンプルソースの修正いくつか
- mapの初期化をStreamで行うように変更されていて、そのことの解説を行う段落がいくつか追加された。
- 型の名前がいくつか変更。 Harb -> Plant とか
Item 38: Emulate extensible enums with interfaces
- このパターンが標準APIで使われている例について説明する段落が追加されている。
Item 39: Prefer annotations to naming patterns
- Java8 からの、同じアノテーションを複数記述させるやり方 (
@Repeatable
アノテーション)による例について説明する段落が追加されている。
Item 40: Consistently use the Override annotation
- 終わりの方の段落が一部修正されている。(
@Override
アノテーションが必須ではない場合での利用について)
Item 41: Use marker interfaces to define types
- デフォルトメソッドの導入で一部内容が変わっている。
- インターフェースとアノテーションの選択についての説明が一部書き換わっている。
Chapter 7: Lambdas and Streams
新しい章!
本記事では扱いません。
- Item 42: Prefer lambdas to anonymous classes
- Item 43: Prefer method references to lambdas
- Item 44: Favor the use of standard functional interfaces
- Item 45: Use streams judiciously
- Item 46: Prefer side-effect-free functions in streams
- Item 47: Prefer Collection to Stream as a return type
- Item 48: Use caution when making streams parallel
Chapter 8: Methods
Item 49: Check parameters for validity
- クラスのメソッドすべての共通する仕様はクラスのドキュメントに書くべきという段落が追加。
- Java7での
java.util.Objects
#requireNonNull(T)
が紹介されるなど。 - Java9での
java.util.Objects
の新しい範囲チェックメソッドが紹介される段落が追加。
Item 50: Make defensive copies when needed
- サンプルコードについて、Java8以降では
Date
を使うべきでないという趣旨の段落が入る。
Item 51: Design method signatures carefully
- 全体的な内容に変化は無い。
Item 52: Use overloading judiciously
- Java8のラムダとメソッド参照が、オーバーロードに与えた影響についての例についての記述が追加。 「異なる関数型インターフェースを引数に取るメソッドオーバーロードをやってはいけない。」とのこと
「inexact method reference」
参考:https://mjg123.github.io/2017/10/23/Java-Inexact-Method-References.html
Item 53: Use varargs judiciously
- 「修正できるというだけの理由で配列をパラメータに持つメソッドを可変長引数に変更するべきでない」事を言っている段落が全部消えている。(紙幅を調整したのか、これをやってしまう人が以外少なかったのかもしれない。)
Item 54: Return empty collections or arrays, not nulls
- 話の展開が、配列よりコレクションを先に出した構成に変わっている。
- C言語的な最適化に触れた内容が削除された。(C言語プログラマがJavaを始めるケースが減っているからかも)
Item 55: Return optionals judiciously
新項目。この項目では割愛します。
Item 56: Write doc comments for all exposed API elements
全体的な主張に変化は無いが、色々追加されている。
- Java8,Java9で追加されたdoc tagについて追記。
@implSpec
-
@index
を使うと、javadocでの検索でのサジェスチョンに出てくるので便利。
-
module-info.java
への言及が追加(踏み込んだ詳細なの内容は無い。) - このItemの内容に従っているかどうかをチェックする機能についての説明段落が追加。JavaDoc ツール、checkstyle 、W3C-Validator
- このItemの内容に従い、明確なドキュメントになっているかを確認するには実際に読むべき、という説明の段落が追加。
-
@return
タグがメソッドの記述と同じである場合は省略して良いという一文が追加。 -
@code
タグの機能説明が追加。 - メソッドやコンストラクタの最初の一文は3人称で記述するという一文が追加。
(ドキュメントの書き方についてはやはりありがたいかもしれない。)
Chapter 9: General Programming
Item 57: Minimize the scope of local variables
全体的な内容に変化は無い。
Item 58: Prefer for-each loops to traditional for loops
- 伝統的forループでは走査対象をコレクションと配列に変更する時コード修正が必要、という一文が追加。
- for-eachが使えないケースについての段落の構造が変わった(内容変化なし)
- for-eachが使えないケースでコレクションをフィルターする場合は、
Collection#removeIf
が使えるという一文が追加。(便利そう!)
Item 59: Know and use the libraries
- 乱数生成においてJava7以降では
ThreadLocalRandom
を紹介する段落が追加。 - Java9で入った
InputStream.html#transferTo()
を紹介する段落が追加。 - ライブラリに欲しい機能がなかった場合は、高品質なサードパーティライブラリを使うように本文の趣旨変更。(以前は自前実装せよという内容だった。)
Item 60: Avoid float and double if exact answers are required
- サンプルソースの説明に、正確な値が入るようにするためStringを引数に取るコンストラクタを使っていることが追記されている。
Item 61: Prefer primitive types to boxed primitives
全体的な趣旨に変化は無い。
Item 62: Avoid strings where other types are more appropriate
全体的な趣旨に変化は無い。
Item 63: Beware the performance of string concatenation
全体的な趣旨に変化は無い。
Item 64: Refer to objects by their interfaces
-
Vector
での例が、LinkedHashSet
に替わっている。(Vectorは古いからだろうか。) -
ThreadLocal
による説明が、HashMap
による説明に替わっている。(趣旨は変わらず。)
Item 65: Prefer interfaces to reflection
- 「ルールとして、通常のアプリでは実行時にオブジェクトをリフレクションでアクセスするべきではない。」という1段落が消去されている。(通常とそうでないものの境界線が曖昧になっているから?)
- リフレクションでの例外について
ReflectiveOperationException
という共通親クラスへの言及が追加。(Java7
で追加されたクラス。マルチキャッチ向きになっている。)
Item 66: Use native methods judiciously
- Java3 以降のBigIntegerの進化について説明する段落が追加。(Java 1.3 はJava3 と書くようにしたらしい。そういうものかな)
Item 67: Optimize judiciously
- 1段落追加。曰く、「このItemが最初に書かれて20年近く経って、プログラムの動作環境は変化した。このItemでいわれているソフトウェア要素の組み合わせでパフォーマンスは予想しにくくなり、計測の必要性が相対的に上がった。」
- 「20%のコードが80%の時間を取っている」、という記述が、「10%のコードが90%の時間」に変わっている
(色々大規模化しているからかもしれない。) - Java Microbenchmark Harnessへの言及が追加。http://openjdk.java.net/projects/code-tools/jmh/ (有益かも)
Item 68: Adhere to generally accepted naming conventions
- メソッドのパラメータの命名についての記述が追加。
- Java Beansの命名規則に乗ることについての勧め方が弱くなった。(シリアライゼーションが絡んでくるから?)
Chapter 10: Exceptions
Item 69: Use exceptions only for exceptional conditions
- 状態を確認するメソッドと個別の値のどちらを使うかの議論にOptionalの事が加味されている。
Item 70: Use checked exceptions for recoverable conditions and runtime exceptions for programming errors
- 要約してまとめている段落の構成が変わっている。
Item 71: Avoid unnecessary use of checked exceptions
-
CloneNotSupportedException
についての段落が削除されている。 -
Optional
を使って検査例外をなくす方法についての段落が追加。 - 要約してまとめている段落が追加。
Item 72: Favor the use of standard exceptions
-
Exception
,RuntimeException
,Throwable
,Error
を直接使わないようにするという内容の段落が追加。 - 標準APIの例外クラスの子クラスを作ることについて、例外クラスはシリアライズ可能であることに触れる一文が追加。
- 例外クラスの選択について、
IllegalStateException
,IllegalArgumentException
のどれを使うかについての基準が具体的に提示されるようになった。
Item 73: Throw exceptions appropriate to the abstraction
- 全体的な趣旨に変化は無い。
Item 74: Document all exceptions thrown by each method
-
main
メソッドのみthrows Exception
や、Throwable
としてよいという記述が追加。 - メソッドでの例外のドキュメントは、
@throws
タグで書かれるべきという一文が追加。
Item 75: Include failure-capture information in detail messages
- パスワードや暗号化キーなどを詳細メッセージに含めない。という一段落が追加。
- ユーザ向けメッセージと例外の詳細メッセージの違いについて追記。
- Java9での
IndexOutOfBoundException
で追加されたコンストラクタについて追記
Item 76: Strive for failure atomicity
- オブジェクトの状態を維持する方法の3番と4番が入れ替わっている。
-
Error
が起きたときの維持について、内容が明確な書き方になった。
Item 77: Don’t ignore exceptions
- 例外を意図的に無視する場合の変数名として
ignored
とする内容が追加。 - サンプルソース追加。
Chapter 11: Concurrency
Item 78: Synchronize access to shared mutable data
-
java.util.concurrent.atmic
パッケージの説明が小さく追記された。
Item 79: Avoid excessive synchronization
- 1段落追加 P318の真ん中
BiConsumer
をそのまま使わなかった理由を追記。 - P319に1段落追加。サンプルソースの補足として、ラムダはそれ自身のインスタンスにアクセスできないので、匿名クラスを使ったことが追記。
- P320 先頭に一段落 複数の例外をcatchするのにJava7以降のマルチキャッチを使っていることが追記。
- P322の1段落に加筆。2段落に分けられる。スレッドの同期化をクラスの外部に任せるか、クラス内部で実施するかについての議論が詳細化されている。
- staticフィールドにアクセスするメソッドを同期化することについての段落に多少の加筆がされている。
Item 80: Prefer executors, tasks, and streams to threads
-
java.util.Timer
クラスの代替として Executor Framework を説明する段落が、fork-join
タスクについての内容に替えられている。(紙幅と重要度の問題か。)
Item 81: Prefer concurrency utilities to wait and notify
- 細かい加筆がいくつかある。
-
Synchronizers
の紹介に、Phaser
というものが名前だけ紹介されている。(Java7から導入された) - 正確なパフォーマンス計測について触れる一文が追加。(ここでもJMHについて触れられている)
Item 82: Document thread safety
- 細かい加筆が多少あるが、全体的な趣旨に変化は無い。
- ロックフィールドは常にfinalであるべきということが明示されるようになった。
Item 83: Use lazy initialization judiciously
- 全体的な趣旨に変化は無い。
- Double Check が、1.5以降のメモリモデルの修正によりきちんと使えるようになったことを説明する段落が削除(Java1.4以前の話はもうしないということか)
Item 84: Don’t depend on the thread scheduler
全体的な趣旨に変化は無い。
- 唯一のyieldの利用用途について初版には書かれていたことについての段落が削除
Chapter 12: Serialization
Item 85: Prefer alternatives to Java serialization
新項目
- このChapter全体でシリアライゼーションを勧めない内容になった。その概要説明のような段落である。
- 「シリアライゼーションは危険であり避けるべき。新規システムの場合はJSONや、Protocol Buffersを使うこと」とのこと。
Item 86: Implement Serializable with great caution
- シリアル化可能なインスタンスフィールドを持つクラスを実装する際のリスクについての記述にfinalizer attackの可能性について追加。
- 継承を意図したクラスがSerializable を実装しない場合の注意点について、詳細に解説した段落いくつか消えている。(およそ2ページ弱ほど。紙幅の問題?)
Item 87: Consider using a custom serialized form
- パフォーマンス計測状況が3版での結果で修正されている。
-
defaultWriteObject()
の扱いについての記述が変更。(仕様によって呼ぶことが求められているとのこと) - 意図的に非互換を出したいのでないならば、 serial version UID を変えてはならないことが明示されている。
Item 88: Write readObject methods defensively
- サンプルコードで
byte
にキャストしている理由について補足が入っている(整数リテラルは int であり、 128以上の数をbyte
として扱うには明示的なキャストが必要であるから。127以下なら不要。2進数で表現した際の最上位ビットが0
ならint
とbyte
で同じであるから。) -
writeUnshared
,readUnshared
メソッドについての段落が削除(マニアック過ぎたから?)
Item 89: For instance control, prefer enum types to readResolve
- 「enum にすればJava がシングルトンを保証してくれる」という下りに、「攻撃者が、AccessibleObject.setAccessible を濫用しないかぎり」という但し書きが追加される。(それを言われたら元も子もないのですが、全体的に悲観的なトーンに寄っている。)
Item 90: Consider serialization proxies instead of serialized instances
全体的な趣旨に変化は無い。
終わりに
それぞれの版の発売日は以下のとおりです。
版数 | 発売日 | 参考リンク |
---|---|---|
1版 | 2001-06-15 | http://amzn.asia/j2dqKSw |
2版 | 2008-05-08 | http://amzn.asia/6lVZFi4 |
3版 | 2018-01-06 | http://amzn.asia/hI5g2sy |
次の Effective Java 4th edition はおおよそ9年後の2027年とざっくり勝手に予想します。その頃のJavaのバージョンは現在のリリースモデル 2 によれば 30 になっているはずで、どんな新機能があるか想像もできません。それでは皆様、また9年後にお会いしましょう。(さすがにもう書かれないかなー?)
-
新規の Item については別途記事を起こそうと思います。 ↩