Javaのクラスには次の役割があります。
- オブジェクトのひな型
- 責務の分離
- 詳細を隠蔽する抽象化
😼 オブジェクトのライフサイクル管理
オブジェクトのライフサイクルを適切に管理する次の技法があります。
- 変数のスコープにちゅうして、不要に長い寿命のオブジェクトを減らします
- 寿命の短いオブジェクトと、寿命の長いオブジェクトを適切に分離します
- ファクトリパターンやDI(Dependency Injection)など、オブジェクト生成向けの技法を活用します
- オブジェクトのライフサイクル管理をフレームワークなどの層に隠蔽する
🎂 クラスの設計指針
- 共通したコードをまとめる
- 変わりやすい部分と変わりにくい部分を分ける
- (テンプレートメソッドパターン、ストラテジパターン、パラメータ化)
- クラス間の依存を減らす。依存関係を単純化する
- 外部から呼び出しやすいコードにする
👽 コンストラクタ
コンストラクタはオブジェクトの生成時によばれる処理です。
class Book { |
コンストラクタの意義はオブジェクトの生成時に適切な初期化を強制できる点です。super
を使って親クラスのコンストラクタを呼び出すことも可能です。
コンストラクタを書かなかったクラスには暗黙にデフォルトコンストラクタが生成されます。
1つでもコンストラクタを書き足すと自動的にデフォルトコンストラクタは消滅します。
コンストラクタのルール
コンストラクタのルールは次のとおりです。
- メソッド名をクラス名と同じにすること
- 戻り値型は記述できない
- newと一緒にしか使えない(インスタンス生成時以外は呼び出せない)
this
を使ってオーバーロードされたコンストラクタを呼び出す場合はその前に別の処理はできない
🏈 複数クラスの共通処理
複数クラスの共通処理をくくりだすには次の方法があります。
- 共通部をクラスとして、そのオブジェクト参照をフィールドにもち処理を委譲します
- 共通部を基底クラスとして、クラスを継承して階層管理します
出典:改訂2版 パーフェクトJava
- 単純に共通処理をくくりだすだけなら、委譲を使ってください
- 上位型は下位型の汎用型となるような型定義の共通部なら、拡張継承にしてください
🎉 拡張継承によるクラスの構成要素の変更
クラスの構成要素が、継承した先でクラスの構成要素を変更できるかを紹介します。
名称 | 追加 | 削除 | 変更 |
---|---|---|---|
フィールド | 可能 | 不可 | 隠蔽 |
クラスフィールド | 可能 | 不可 | 隠蔽 |
メソッド | 可能 | 不可 | オーバーライド |
クラスメソッド | 可能 | 不可 | 不可 |
内部クラス | 可能 | 不可 | 隠蔽 |
staticなネストしたクラス | 可能 | 不可 | 隠蔽 |
staticなネストしたインターフェース | 可能 | 不可 | 隠蔽 |
コンストラクタ | 可能 | 不可 | 不可 |
初期化ブロック | 可能 | 不可 | 不可 |
static初期化ブロック | 可能 | 不可 | 不可 |
🍣 フィールド変数の隠蔽
継承した先のクラスで継承元と同名のフィールドを宣言すると、継承元のフィールドを隠蔽します。
class Parent { |
一般的に変数の隠蔽はコードの可読性を落とすため、避けるべきです。
🎃 オーバーライド
オーバーライドとは親クラスのメソッドを子クラスで定義をし直す(上書きする)ことです。
class Book { |
隠蔽では変数の型で呼ばれるフィールドが変わりましたが、オーバーライドでは呼ばれるオブジェクトの型でメソッドが決まります。
オーバーライドのルール
オーバーライドには、次のようなルールがあります。
- オーバーライドは、メソッド名、引数リストがまったく同じメソッドをサブクラスで定義すること
- 返り値の型は、スーパークラスと同じものか、もしくはその返り値のサブクラスであれば利用可
- アクセス制御は「スーパークラスと同じものかそれよりも公開範囲が広ければ利用可」
オーバーライドにおける例外処理(throws)について
メソッドをオーバーライドする際に例外処理(throws)を記述する際のルールについてです。
- throwsには、スーパークラスのメソッドがthrowsに指定した例外クラス(サブクラス)が指定できる
- throwsには、RuntimeExceptionクラス(サブクラス)も指定可能
- スーパークラスのメソッドにthrowsがあっても、throwsをしてしなくてもいい
🐡 super参照
オーバーライドされた元メソッドがprivate
でなければ、super
を通じて元メソッドを呼び出すことができます。
class Book { |
🐹 抽象クラス
abstract
修飾子をつけたクラスを抽象クラスと呼び、インスタンス化ができません。
抽象クラスになる条件は次のとおりです。
- 抽象メソッドをもつクラス
- interfaceを継承して、interfaceのすべてのメソッドの実装をもたないクラス
- 抽象クラスを継承して、すべての抽象メソッドの実装をもたないクラス
🐰 ユーティリティクラス
オブジェクト生成を禁止し、クラスの直接利用のみを行うクラスを「ユーティリティクラス」と呼びます。
public final class Math { // final で無用な継承を禁止 |
意味のまとまりのあるメソッドを一ヵ所にまとめることができる点が、ユーティリティクラスの利点です。
🐝 匿名クラス
匿名クラスのオブジェクトはメソッドの引数として引き渡すために生成します。
public static void main(String[] args) { |
🐮 クラス設計に関する用語
DTO:データを運ぶクラス
DTO(Data Transfer Object)クラスは、データを運ぶ役割を分離してモジュール間の関係を単純化するために使います。
DAO:DBとの境界を担うクラス
DAO(Data Access Object)クラスは、データベースとの境界を担うクラスを作ることで役割を分離します。
😸 補足:JVMがメソッドを探す流れ
JVMが実行すべきメソッドを探すために使う方法は次の3つです。
- 参照 (どのインスタンスのメソッドなのか)
- クラス名 (どのクラスに定義されているstaticなメソッドなのか)
- シグネチャ (メソッド名と引数のセット、オーバーロードがあるため)
🏀 補足:クラス継承で引き継がないもの
クラス継承で引き継がないのは「コンストラクタ」と「private
なフィールドやメソッド」です。
🍮 補足:フィールドはオーバーライドしない
- Javaではメソッドとフィールドは明確に区別されており、オーバーライドできない
- 親クラスと小クラスで同名フィールドを定義すると変数の型によってどちらかのフィールドが参照
- アクセスできないフィールドが発生しかねず、バグの温床になるので絶対に行わない
🚜 補足:継承関係のクラスの同名フィールドの優先順位
次のようなスーパークラスとサブクラスがあったとします。
出典:徹底攻略Java SE 8 Silver問題集
class A { |
この場合どちらのフィールドが使われるかは次のルールに従います。
- フィールドを参照した場合は、変数の型で宣言されたほうを使う
- メソッドを呼び出した場合は、メソッド内の指示に従う
🚌 継承関係のあるクラスのコンストラクタ
継承関係にあるクラスのコンストラクタについてです。次のような継承関係があるとします。
class A { |
この場合、
- スーパークラスのインスタンスがもつコンストラクタが先に実行されなければいけない
- サブクラスのコンストラクタには、スーパークラスのコンストラクタを呼び出す「
super();
」がコンパイラによって追加