市販の入門書で不足している説明
本稿で紹介した本をすべて読破したとしても、入門者が犯すバグをなくすことはできません。表1.Java入門書比較表の評価項目の中から数個抽出し、バグをさらに減らすための補足説明を紹介します。
いずれかの本を読んだ後に読むと理解しやすいですが、今はざっと目を通すくらいにしておいて、一通り入門編を学習した後に再読してもらえればと考えています。
その際、ここで説明したことがすぐに理解できなくても、次のステップに行く前に少し踏みとどまって確実に理解することで、頑丈なプログラムを作れるようになります。
式
式についてきちんと説明できているのは『明解Java』のみです。この本では式を、「変数・リテラル・変数やリテラルを演算子で結合したもの」と定義しています。式とは乱暴な表現をすれば「値を返してくれるもの」です。変数も調べれば値を返します。リテラルも同様です。変数とリテラルを演算子で結合したものも値を返します。
式という概念を導入しないと説明が冗長になったり、不明確になります。例として『わかりやすいJava入門編』p286のswitch文の説明に「switch()では、()内に検査する変数や式を書きます」とあります。これは上記の定義によると冗長な表現です。変数も式に含まれるので「swtich()では、()内に検査する式を書きます」と簡潔で明確に記述できます。
また、『わかりやすいJava入門編』p398-399に「メソッド呼び出しでの実引数の指定方法」として、
- 変数の実引数
- 式の実引数
- 標準メソッドの実引数
が挙げられています。変数と式は説明したとおりですが、標準メソッドも実引数になれるとわざわざ説明しているのは、標準メソッドが式であるという概念を導入していないからです。
『明解Java』p226では「○○演算子を用いた式のことを、○○式と呼ぶのでしたね。従って、メソッド呼出し演算子を用いた式は、メソッド呼出し式(method invocation expression)と呼ばれます」との説明があります。つまり、標準メソッドでvoid型ではないものは式なのです。従って、変数・式・標準メソッドはすべて式という言葉に置き換えることができ、頭の整理ができます。
『わかりやすいJava入門編』が悪いということを言いたいわけではありません。むしろここまで踏み込んでメソッドの実引数の説明をしている本を見たことがありません。『明解Java』にもこのような具体的な説明はありません。ただ、式という概念を正確に定義しておけば、よりすっきりした説明が可能なのではないかと考えているだけです。
上記のとおり、実は「あなたにふさわしい本」は当記事で紹介した中の1冊だけではありません。それぞれ強みを持っています。『明解Java』は言葉の正確な定義があり、『わかりやすいJava入門編』は正確な定義にはこだわらず、分かりやすい表現で具体的に説明しています。
長文を読むのが苦ではなく少し金銭的に余裕があれば、『明解Java』と『わかりやすいJava』2巻だけで十分と筆者は考えます。
if文の詳細な構文
すべての本の記述に誤りはありません。それはif、else if、elseの後を必ずブロック{}で囲んであるからです。
ただし、囲まなかった場合にバグを生む危険性を、『明解Java』ではp65「Column 3-2 if文の構文に関する補足」で指摘してしています。入門者がよく犯す間違いの一つです。
筆者はどのような簡単なif文でもブロックで囲むよう指導しています。これはバグの防止という意味もありますし、複雑なif文の場合可読性を上げてくれる効果もあります。
短絡評価(short circuit evaluation)
『明解Java』と『わかりやすいJava』に説明がありますが、その危険性には触れていません。ここで補足します。
短絡評価とはif文、while文などで一般的に用いられる「||」論理和演算子や「&&」論理積演算子を使う場合の評価ルールのことです。短絡評価を使うと、左側を評価した時点で全体の評価結果が明らかな場合は右側を評価しません。
一方「|」論理和演算子や「&」論理積演算子もありますが、実務ではほとんど見かけません。これらはオペランドを全て評価することでパフォーマンスを低下させる可能性があるためです。通常は短絡評価を用いることをお薦めします。『わかいやすいJava入門編』p219にも「|」よりも「||」を、「&」よりも「&&」を使用することが薦められています。
ただし、短絡評価にはパフォーマンスを向上させる反面、落とし穴があります。言葉だけでは分かりづらいためコードを使用して説明します。
001:if ((x > y) && ((z = a.myMethod()) < 100)) {
002: // 何かの処理
003:}
004:z += 100;
非常にシンプルなif文です。ここでは短絡評価を知らずにプログラミングしたと仮定します。
短絡評価を知らないわけですから、xが10、yが5、a.myMethodの戻り値が50だとすると、左側のオペランドがtrueになり、右側のオペランドもtrueなので全体としてもtrueとなり、z += 100;の評価は150となります。
次にxが5、yが10、a.myMethodの戻り値が50の場合はどうでしょうか。左側のオペランドがfalseとなるため、z = a.myMethod() < 100は評価されません。つまり、a.myMethodは実行されないのです。短絡評価を知らないと、右側のオペランドも評価されると思い込み、z += 100;が実行され、150という値がzに設定されると勘違いすることになります。
この例のように右側のオペランドに代入式がある場合、その代入式が評価されない可能性に注意する必要があります。できればオペランドの中に代入式を用いない方法を考えた方が無難です。発見が難しいバグの一つです(EclipseやNetBeansなどの統合開発環境でデバッグすると簡単に見つけることはできますが、短絡評価を知らないとデバッガにバグがあるのではと思いたくなるバグです)。
ガーベジコレクション
ガーベジコレクションとは、参照されなくなったオブジェクトをメモリから解放し、メモリ領域を空けることを指します。Javaといえども適切な処理を行わないとメモリリークを起こします。メモリリークが大きくなると使用可能なメモリ領域が不足し、処理が継続できなくなるため絶対軽視してはならないことの一つです。
初心者の方はまだ分からないと思いますが、一例を挙げると、Webアプリケーションからデータベースに貼ったコネクションのcloseし忘れなどで、よくメモリリークが発生します。
配列のコピー(配列変数の代入との違い)
Javaが用意している型は大きく「基本型」と「参照型」の2つに分けることができます。配列が参照型に属することを知らないためにバグが発生する場合があります。基本形と参照系の違いは紹介した本の全てで丁寧な説明があるため割愛します。結論を言えば、配列から要素を順繰りに取り出し、代入を繰り返すことによってしかコピーするしかありません。
マジックナンバーの定数化
これを扱った本が『Javaプログラミングレッスン 上』だけであることに少しショックを覚えます。これは非常に大切なことを言っているからです。
マジックナンバーとはプログラム中に直接書き込まれた数値リテラル(5や10)のことです。数値リテラルの例としては数学や物理で用いられる3.1415(円周率)や1.414(2の平方根)などが挙げられます。プログラムを書いた本人はその数字の意味を知っていますが、他の開発者が見てもそれが何は分かりません。数字を書き込んだ行にコメントを残せばこれを避けられるように思えますが、仕様変更が発生した場合コメントも必ず変更されるという性善説に立った考えです。
この問題を解決する方法が定数化です。数値リテラルは数学や物理で使用されるものだけでなく、事務処理系でも普通に使用します。例えば10という数字がスーパーのディスカントデーである場合、プログラムに直接10と書き込んでも意味が伝わりません。そのような場合、定数化すればいいわけです。
この場合であれば、final static int DISCOUNT_DAY = 10;(アクセス修飾子は状況により付加してください)のように定義すれば、ただの10という数字であったものがディスカウントデーであることが一目瞭然と分かるようになるのです。オブジェクト指向は名前が命です。DISCOUTN_DAYではなく、DDAYなどと定義しても定数化する意味がなくなるので注意してください。
さらに、ディスカウントデーが10日ではなく25日に変わった場合、この定数値をfinal static int DISCOUNT_DAY = 25;のように変更すればいいわけです。プログラムの中のディスカウントデーを保持したDISCOUNT_DAYの内容が一斉に変るため、保守性に優れたプログラムとなります。このような方法を取らず、プログラムの中に10という数字が複数か所に記述してある場合、その中のどれがディスカウントデーに使われているのかは作った本人しか知らず、プロジェクトを離任していたり、最悪会社を辞めたりしていた場合、このプログラムをメンテするのは至難の業となります。
さらに言うとこれは数字に限った話ではありません。リテラルを使用するということは保守性や可読性を著しく低めるため、ほとんどのプロジェクトでは禁止されています。よく見かけるのはswitch文です。文字リテラルで'a'とか'b'などと書かれても意味が分かりません。リテラルが現れたら定数化できないか考える癖をつけてください。
