『Java:The Good Parts』 一流のプログラマになるには言語の“コア”を理解せよ
Java:The Good Parts Jim Waldo(著) 矢野勉(監訳)笹井崇司(翻訳) オライリージャパン 2011年2月 ISBN-10: 487311487X ISBN-13: 978-4873114873 2310円(税込) |
■言語ごとの特性を理解し、使い分ける人が一流のプログラマ
あらゆる面で完ぺきなソフトウェアは存在しない。プログラミング言語もソフトウェアである。ゆえに、あらゆる面で完ぺきなプログラミング言語は存在しない。
人間の言語が現実世界をうまく表現できないのであれば、まず間違いなく、プログラミング言語が現実世界をもっとうまく表現できるとは期待できないだろう。プログラミング言語は現実世界にあるものをモデル化するための抽象概念を提供するが、その言語の表現力は現実世界と言語とを直接対応付けるには不十分なところがある(p111)。
完ぺきなプログラミング言語は存在しない。ならば、プログラム言語ごとの特性を理解して使い分けるのが、一流のプログラマだと思う。
現在、使われているプログラミング言語で「実現できること」に極端な差はない。大抵のソフトウェアは他のプログラミング言語に移植できる。
もちろん、プログラミング言語ごとに得手不得手はあるだろう。私はJava言語をメインのプログラミング言語としている。だが、果たしてそのクセを理解して使いこなしているだろうか? 長所と短所をきちんと把握しているだろうか?
■Java言語の“コア”を知るための本
本書は、Java言語開発当初から関わったベテランエンジニア Jim Waldo氏が、Java言語の設計思想やJavaの秀でた部分について語るものだ。
目次を並べてみると、「初学者用の本か?」と思うぐらい基本的な項目が並ぶ。
1章 Javaについて
2章 型システム
3章 例外
4章 パッケージ
5章 ガベージコレクション
6章 Java仮想マシン
7章 JavaDoc
8章 コレクション
9章 リモートメソッド呼び出し(RMI)とオブジェクトシリアライゼーション
10章 並行処理
11章 開発者のエコロジー
この本では、JPAやDIといった派手なことは解説しない。書かれているのはJava言語の「コアな部分」である。以下、Javaエンジニアとして私が気になった部分をいくつか紹介しよう。
■型システム
クラスとインターフェイス、その中間にある抽象クラス。Javaの型は3つしかない。
歴史的に見ると、型はコンパイラがオブジェクトに割り当てる容量を決定するためにコンピューター言語で使われてきた(p8)。
Javaでは、型システムに別の役割がある。クラスだけでも実装はできる。インスタンス化できないインターフェイスや抽象クラスでは、オブジェクトのサイズを計ることはできない。実装を持つことが可能な抽象クラスと違って、インターフェイスを使えば使うほど、余計なコードをタイプをしなければならない。試行錯誤で実装する時は、インターフェイスがわずらわしいと思うことさえある。
なぜ、わざわざ実体を持たないインターフェイスを使う必要があるのか。
オブジェクトに対するインターフェイスは実のところユーザーインターフェイスであり、ここで言うところのユーザーとはプログラマのことである(p11)。
Java言語は、大規模開発に使われることが多い。インターフェイスは作成した本人を含めて、誰かに使ってもらうプログラムを前提としている。師匠の1人にかつて、「Javaではインターフェイスに向かってプログラムしろ」と言われたことを思い出した。
Java言語ではクラスは単一継承しかできないが、インターフェイスは複数を1つのクラスに持てる。実装すべきインターフェイスが複数ある場合、同じ名前、同じシグニチャを持つメソッドが存在する場合がある。このことは制約であるがJava言語の限界ではない。視点を変えると1つの設計指針にもなる。何をするべきメソッドなのか、メソッド名で明確にする必要があるからだ。
public interface Family { public String getName(); } public interface Person { public String getName(); } public class PersonImpl implements Family, Person{ @Override public String getName() { return "姓?名?"; } }
となる場合、getName()はFamilyとPersonのどちらの意味を実装するべきな のだろうか。
public String getName() というシグニチャからは名前を返すメソッドであることが分かる。これが人の名前を返すようなメソッドである場合、「フルネームなのか」「姓を返すメソッドのか」「名を返すメソッドなのか」といった曖昧さが生まれる。そのため、getFamilyName() getFirstName()などの名前を検討する。メソッド名は、別の意味を持つ可能性を常に考慮しなければならない。
これが、method1()といったどうとでも取れるメソッド名をつけてはならない理由の1つでもある。
■例外
例外は例外として処理するべきである。シグニチャと異なるクラスを返すために使用するべきではない。
Javaのもともとの目的は、長年にわたって理解可能でメンテナンス可能な、大規模なシステムを書くのを助けること(p.27)
処理されない例外は、コールスタックにある次のメソッドで処理される。古いJavaの設計では、必ずしも例外を処理する必要はなかった。呼び出し側がどの例外をキャッチしなければならないか、知っておく必要があった。
例外のメカニズムが変更されたのは、Java1.0の時代だ。スローする例外はメソッドシグニチャに明記しなければならない。スローされた例外は、キャッチして処理するか再スローするかの判断を行う。例外を無視したければ「無視をする」という選択をしなければならない。
その思想に反する例外クラスがある。「非検査例外」とも呼ばれるRuntimeExceptionだ。このクラスは、仮想マシンや実行しているOSの都合で起こる例外を表現するためにある。システムの土台で起こりうる例外にスロー宣言が必要ならば、すべてのメソッドに例外を記述する必要が出てくる。仮想マシンの例外に対してアプリケーションができることはほとんどない。ゆえにRuntimeExceptionが存在する。
悪に抵抗しよう。新しいRuntimeExceptionを宣言してはいけない(p.38)。
フレームワークを多用していると、フレームワークが持つ「新しいRuntimeException」とやらによく出合う。これはある種の“土台”で起こる例外であり、よくできたフレームワークはその例外処理をきちんと行っている。このことを意識して、OSSのソースコードを追いかけていくと、面白い発見が得られるだろう。
■パッケージ
無名パッケージは名前空間の変獄のようなもので、頭が混乱していたり、固かったり、怠惰なプログラマが書いたコードが、彼らが高度生命体に進化するまで置かれる場所だ。無名パッケージに何かを置くべき正当な理由はない。したがって、そうしてはいけない(p.43)。
名前空間というものがなぜ存在するのか。パッケージがユニークである必要性はいまさら語るまでもないだろう。
私がずっとひっかかっていた疑問がある。なぜ、パッケージ名がファイルシステムに依存するのか? なぜ、パッケージ名と同じディレクトリ構成にするのか?
当初のJava言語では、バージョン管理システムのようなものを組み込みたいという意図があった。そのためには、データベースにソースやバイナリを格納したい。データベースからデータを特定するためには、主キーになるものが必要であった。
データベースの安価なエミュレータとしてファイルシステムを使うという決定がなされた。
その決定は「歴史が愚かさを明らかにする」という。現在ではIDEの補助により軽減されたが、根本的な問題は解決されていない。
本書は、Javaの「負の遺産」を隠さない。Javaという言語の根幹を、丁寧にひも解いていく。
■正しく使うこと
プログラミングは創作活動だと思う。音楽や絵画を「美しい」と感じるように、プログラムにも美しい世界が存在する。画家は鮮やかな色使いを求め、音楽家は心地よい旋律を探す。プログラマはロジックと機能のために、正しい言語の使い方を覚えたい。
なぜ、正しい言葉使いが大切なのか。答えはシンプルである。
言語の表現力を超えてまで言語を酷使すると、間違った設計に繋がるためだ(p.112)。
作家がよい言葉を捜すように、哲学者が厳密に言葉を定義するように、プログラマはプログラミング言語という言葉を重ねる。
本書は技術書の中でも気軽に読めて、かつ「言語の哲学」に触れられる。ある程度、Java言語が分かれば、初心者から上級者まで誰が読んでも楽しめる良書である。時間をかけてコーヒーをドリップするように、言葉について考える1冊。
(『フリーなスキル』コラムニスト はがねのつるぎ)