🚀

オブジェクト指向のあれこれ

2024/04/29に公開

1.初めに
オブジェクト指向の考え方として、データと処理を1つの塊として捉える。
この塊を定義するのがクラスというもの。
データを定義する部分を「インスタンスフィールド」(public 型名 変数名a)
処理を定義する部分を「インスタンスメソッド」という。(public 返却型 メソッド名b)
つまりクラスとは、一般化したオブジェクトのこと。
そして、そのクラスの各項目について具体的なデータを渡したものをオブジェクトと言ったりインスタンスと言ったりする。
クラス名 変数名I = new クラス名();
こうすることで、変数に何の情報も持っていないインスタンスを作成できる。

2.フィールドアクセス
作成したインスタンスのインスタンスフィールドに定義されている各データに具体的な値を代入する場合は、I.a = とすることで、インスタンスが持っているaという変数への値の代入ができる。
I.b();で、インスタンスが持っているメソッドbを実行できる。

3.this1
インスタンスメソッド内の処理bで、各インスタンスに対応したインスタンスフィールドの値を使いたい場合は、this.a記述する。このメソッドbがインスタンスIによって呼び出されたとき、
thisは呼び出し元のインスタンス名に書き換わるので、それぞれのインスタンスが持っているデータへアクセスし、作成されたインスタンスごとに個別の処理ができる。
(メソッドb内で、新しく変数を定義しなくてもインスタンスフィールドの変数aは使える)

4.this2
インスタンスメソッド内のあるメソッドから、別のメソッドを呼び出す際にもthisが使え。
例えばメソッドb内でメソッドcを使いたい場合は、
this.c();をb内で記述すると使えるようになる。

5.コンストラクタ
1で説明した方法でも、インスタンスフィールドの個々の変数に値を代入することはできるが、インスタンスが所持する変数の数が多くなると非常に面倒、、、
そこで、インスタンスの生成時に自動で実行される特別なメソッド「コンストラクタ」を定義する。定義は簡単。
クラス名(-,-,-,){
処理内容;
}
注意点として
・コンストラクタはクラス名と同じにする。
・voidやpublicは付けない。(理由は後述)
コンストラクタに引数を渡したい場合は、インストラクタ生成時のnew クラス名(ココ);
「ココ」でコンストラクタが受け取る引数の順番にそれぞれ引数を渡す。

6.クラスフィールド/クラスメソッド
クラスcで共通して扱うデータや処理のこと。クラスフィールドは各インスタンスで保持するのではなくクラス自身が保持する。また、クラスメソッドもクラスが保持しているので、インスタンス生成前に利用できる。
・クラスフィールド public static データ型 変数名cf(=初期値);
クラス名c.クラスフィールド名cfでアクセスできる。
↑ どこからでもアクセスできる。コンストラクタ内からでも、別のファイルのメインクラスからでもアクセスできる。
・クラスメソッド public static 戻り値型 メソッド名cm(){
処理内容;
}
クラス名c.クラスメソッド名cmでアクセスできる。
↑ どこからでもアクセスできる。コンストラクタ内からでも、別のファイルのメインクラスからでもアクセスできる。

7.コンストラクタのオーバーライド
コンストラクタは同一名のものしか定義できないが、1つしか定義できないわけではない。
例えば、引数の数が異なるコンストラクタを定義したい場合
CONST(a,b,c){
a,b,cについてのそれぞれの処理-★;
}
ここで、dも追加で受け取れるコンストラクタも作りたいとすると、「オーバーライド」を行う。
CONST(a,b,c,d){
this(a,b,c)←これで、上の★と同じ処理を行う
dについての処理のみを追加する;
}
注意点
・thisはコンストラクタの先頭でしか呼び出せない。

8.コンストラクタ2
コンストラクタが複数ある時、引数が少ないほうのコンストラクタを呼び出すインスタンスについて、引き渡していないインスタンスフィールドの変数は「NULL」となる。
具体的には7のようにクラスの中のコンストラクタがオーバライドされているときに、インスタンスを変数sampleに代入するとして、
CONST sample = new CONST(a,b,c);
としたとき、クラス内で定義されているインスタンスフィールドの変数dについて値が引き渡されていないので、sampleが持っている変数dの値は「NULL」となる。

9.カプセル化
今まで変数はpublicとして定義してきたが、これはクラスを定義したファイル以外のファイルからも無制限に参照や更新ができてしまう。これは予期せぬ書き換えなどが発生するので、初心者の間のクラスファイルの定義規則として
・メソッドはpublic
・フィールドはprivate
として定義することを推奨している。
ここで、あるクラス内Cでprivateとして定義したフィールドFは、コンストラクタでの更新を機に、他のファイルから値の書き換えや参照を一切禁止する。
例えば、メインクラス内からCのインスタンスを生成し、そのごC.Fとしてアクセスしようとしても禁止される。
privateフィールドの値を書き換えたり参照したりする場合は、get/setというメソッドをクラス内に定義することで実現する。
public Fの型名 getF(){
return this.F;
} //getter
public void setF(型名 更新値){
this.F = 更新値;
} //setter

こうすることで、特殊なメソッドget/setを呼び出さない限りは、不用意なフィールドへの操作を禁止することができる。
注意点として、同一クラスC内の他のメソッドからは、this.Fとすれば操作は可能である。
カプセル化は、他のソースファイルからの不用意な操作を制限するための技法である。

10.継承
複数のクラスに同じフィールドやメソッドがある場合、それらをまとめて1つのクラスに定義し、そのクラス(スーパークラス)から継承させて複数のクラスを作る(サブクラス)ことができる。
継承させる場合、スーパークラスをSupC、サブクラスをSubCとすると
class SubC extends SupC{
}として記述する。
それぞれのサブクラス独自のフィールドやメソッドを定義することもできる。
「オーバーライド」
スーパークラスに定義されているメソッドの一部に追加で機能を持たせたメソッドをサブクラスで定義したい場合、オーバーライドができる。
スーパークラス内のメソッドと同名のメソッドをサブクラス内に作り、必要に応じてカスタマイズすることができるが、同名のメソッドを作った時点で、呼び出し時にはサブクラス側のメソッドが優先される。重複している部分の処理については、サブクラス内のオーバーライドをするメソッド内に、「super.メソッド名()」と記述することで、スーパークラス内のメソッドの機能をそのまま使用できる。スーパークラス内のフィールドにはアクセスできないので、getやsetを用いて(スーパークラス内に定義されている)、サブクラス内でthis.get/this.setなどとしてスーパークラスのフィールドを操作する。
スーパークラスとサブクラスからのアクセスを許可するフィールドについては「protected」を設定することで、サブクラスからもthisで指定できる。public < protected < private

11.SupCとSubCのコンストラクタ
SupCに定義されているコンストラクタは、SubCに定義されているコンストラクタの先頭で必ず呼び出すようにし、引数などもそろえる。

SupC(String name1, String name2){

}として記述されている場合、サブクラスのコンストラクタでは、
SubC(String name1, String name2){
super(name1,name2);
}
としてSupCのコンストラクタを呼び出す。

12.抽象クラス
全てのサブクラスに必ず定義しておきたいメソッドで、且つサブクラスごとに処理が異なるようなものを抽象メソッドとして定義する。public abstract 抽象メソッド名(-);
この抽象メソッドは必ずサブクラス内でオーバーライドされなければならない。
また、抽象メソッドを持つクラスは抽象クラスとなり、abstract class クラス名{-}で定義しなければならない。抽象クラスから直接インスタンスを生成することはできない。

Discussion