スッキリわかるJava入門を読みました。
前置き
気がつけば前回の記事投稿から20日も経ってしまった...。まあ、GWだったんで言い訳の余地なくサボってましたけどね。時間が空いて改めて感じるのはブログにまとめると頭の片隅に残るけど、サクッと読んだ本は何も覚えてないということです。ちょうど20日までにWebの本を読んだはずですが綺麗にさっぱり。サボらないでブログにまとめようと決意しました。この理由だと、完全に自分専用のまとめノート的な役割です。
背景
最近アプリ制作のアルバイトを始めました。アプリ開発の途中から入ったため、すでにある膨大なコードを読み解いて機能を実装していく必要があります。単に実装をするだけならできなくもないのですが、そこにはキチンとルールがあるわけで理解していない僕は結構注意を受けたり呆れられたりしてます。僕自身もコードの構造になぜ?などの疑問を感じることも多く、その原因を考えるとおそらくオブジェクト指向プログラミングの知識が不十分というのが大きいでしょう。今まで1人で書くことが多くコードも大規模になることがなかったため目をそらしてきた部分でもありますが、もう逃げられません。そこでオブジェクト指向の復習としてこの本のいくつかの章を読むことにしました。
この本は2年くらい前に購入して、おそらく今回で3周(満遍なくではない)したことになるはずです。もともとは大学の講義でJavaを学習するためその参考書として購入したのですが、オブジェクト指向についてわかりやすく解説されている本なので、該当部分だけ読むことにしました。ちなみにアプリの開発言語はC#です。
目的
実際に開発はC#なので文法的な面は違っていたりすると思います。今回はこの本の内容を理解することで、C#で似たような場面に出くわした時に、「多分こういうことだろう」という仮説を立てることができ、その仮説に沿って調べることができるということを目指します。
つまり、オブジェクト指向の基礎を抑えます。
内容
読んだ章は
- 11章 継承
- 12章 高度な継承
- 13章 多態性
です。
今更がっつり全部を読む時間もないですし。自分があんまりよくわかってないと思う部部分を読んだつもりです。
11章 継承
数えるほとですが、コード量を抑える目的で継承を使ったことはあります。本の内容も概ねその通りで、規模が大きくなると似通ったクラスがたくさんできてしまうため、追加・修正の手間と把握や管理をキチンとするために継承を使うと書かれていました。
文法的には以下のようになり、extendsで継承します。
class クラス名 extends 継承するクラス名{ 親クラスとの差分メンバ; }
差分に関しては、親クラスに同じメンバがない場合はそのメンバは新しく追加され、同じメンバがある場合はそのメンバを上書き(override)します。
C#書いてたときは override を記述してオーバーライドしてた気がする...。
あとは、クラスの宣言時にfinalをつければ継承できなくなる(メソッドは子クラスでオーバーライドできない)。
superで親インスタンスにアクセスできる。
上記のsuperとは別にsuper();で親のコンストラクタを呼び出す。
最後は「is - a」の関係を守って継承をしようという話でした。
まあこの辺りの内容は多分自分でググればなんとかなる内容でした。はい、次!
12章 高度な継承
ここからです。おそらく、1,2回目のときはこの辺りからわからなくなったんだと思います。
最初にこの章に関しては「あいまいなクラスたち」の定義を学習すると述べられていました。
この章を学習するにあたって開発の立場を2つに分け、片方の立場の人が使うと考えると理解しやすいと書かれています。
立場1.ゴリゴリ開発してる人(既存のクラスを継承して子クラスを作る)
立場2.最初に別の開発者のためにクラスを準備する人(親クラスを作る)
アルバイトの僕の立場は1です。完全には理解していませんが、あるクラスを継承しないとコンパイルが通らないことが多いので継承してどんどん機能を付け足しています。やっぱり継承先はある程度把握しておこうと思って、とりあえず親クラスをたどっていくと、ほとんど何も書かれていないクラスにたどり着きます。つまりこのほとんど何も書かれていないクラスを作る立場2の人目線で考えるとわかりやすいとのことです。
高度な継承に関する不都合として、
- クラスを作る段階で詳細が未定なメソッドがある。
- そのクラスを継承して使うのかインスタンス化して使うのか。
そこで抽象クラスというもの出てきます。抽象クラスはnewによるインスタンス化が禁止され、(Javaでは)抽象メソッドを含むクラスは必ず抽象クラスにしなければならないという決まりがあります。
抽象メソッド例
アクセス修飾子 abstract 戻り値 メソッド名(引数);
詳細未定なメソッドを抽象メソッドとして宣言すると、将来の開発者にオーバーライドを強制できる...ん?なんかめちゃくちゃ覚えがある(笑)
バイトであるメソッド呼び出したいのにエラーで呼び出せず、親クラスまで辿るとただの定義しかなくて、先輩に確認したらオーバーライドしてって言われたやんけ!!ちなみに先輩はそのあとに「...いかんなぁ」とこぼしてます。これもう口癖になったんじゃないかねぇ。
な・る・ほ・ど!
次の話はインターフェース。
インターフェースとして扱うには2つの条件を満たす必要あり。
- 全てのメソッドは抽象メソッド
- 基本的にフィールドを持たない
すると、
アクセス修飾子 interface インターフェース名 {
}
としてインターフェースを定義できる。
クラス名ではなくインターフェース名となってるところが地味にポイント。インターフェースを継承して子クラスを定義する場合は、extendsではなくimplementsを使う。僕が最近勉強しているGoでも出てきましたが、インターフェースくんはすごい。
- 同じインタフェースをimplementsする子クラスたちに共通のメソッド群の実装を強制できる(うぅ!)
- あるクラスがインターフェースをimplimentsしていればそのインターフェースが定めたメソッドを持っていることが保証されちゃう。(ちゃんとしないとエラーはかれちゃうからね)
Javaでは多重継承は禁止されているが、インターフェースには許されているのだ!!
はい、この章の内容を事前に抑えておけばアルバイトで苦労しなかったであろうことがわかりましたね!次が最後!
13章 多態性
これは特別は文法があるわけではなくて、考え方の話。
書籍中では『「あるものを、あえてザックリ捉えること」でさまざまなメリットを享受しようというもの』と記述されています。
これまでで、抽象クラスやインタフェースはインスタンス化することができないと述べましたが、型として利用することは可能なんです。
僕の理解が不十分かもしれませんが、ここではその型がポイントになりまして、型を決定することでどのメソッドが「呼び出せるか」を決定することができ、型の中身(実際の型)でメソッドオン動作を決定することができるみたいです。
何が嬉しいのか。
確かに型、型、型を厳密に決めてしまうと色々不便になりますね。
この辺はGoのインターフェースと照らし合わせて理解ができました。
インタフェースの決まりさえ満たせば、同じものとして扱えちゃいますからね。
まとめ
今回読んだ内容は、今の僕にとってかなりの収穫になったんじゃないかと思います。良い具合にピースが当てはまったという感触でした。ただ、さすがに1回読んだだけでこの内容を理解することは相当難しいと思います。
最近はバイト先で「Singletonって何ですか?」って聞いたら「...いかんなぁ」っていわれたので次はデザインパターンを抑えていくべきでしょうかね。やっぱりみんなで開発するときは共通のルールを理解しておかないと足引っ張るってことがわかりました。