オブジェクト指向プログラミングの以下の3つの主要な機能
1.カプセル化(Encapsulation)
データとそれに関連する操作を一つのオブジェクトにまとめ、外部からの直接のアクセスを制御します。オブジェクトは自身の内部状態を保持し、外部からは公開されたインターフェースを通じてアクセスされます。
2.継承(Inheritance)
クラス間の階層関係を作成し、親クラス(スーパークラス)の属性やメソッドを子クラス(サブクラス)に引き継ぎます。これにより、コードの再利用性と階層的な関係の表現が可能となります。
3.ポリモーフィズム(Polymorphism)
同じ型やインターフェースを持つオブジェクトが、異なる形で振る舞うことを意味します。異なるクラスが同じメソッド名を持ち、そのメソッドを使うコードがクラスの実際のタイプによらずに書けるため、柔軟性や拡張性が向上します。
個人的に分からなくなったのが以下
- 継承
- ポリモーフィズム(インターフェースの実装)
継承とは
過去に利用したクラスを流用して新しいクラスを簡単に造れる機能のこと
実装としては以下のように書いたりしますね
class 子 extends 親{
}
継承の簡単なコード
親クラス
class Animal {
public void sound() {
System.out.println("動物の音を出します。");
}
}
子クラス
class Dog extends Animal {
@Override
public void sound() {
super.sound();
System.out.println("ワンワン!");
}
public void playFetch() {
System.out.println("ボールを追いかけます。");
}
}
子クラス
class Cat extends Animal {
@Override
public void sound() {
super.sound();
System.out.println("ニャーニャー!");
}
public void playWithToy() {
System.out.println("おもちゃで遊びます。");
}
}
メインクラス
class Main {
public static void main(String[] args) {
Dog dog = new Dog();
Cat cat = new Cat();
dog.sound(); // 出力: 動物の音を出します。ワンワン!
dog.playFetch(); // 出力: ボールを追いかけます。
cat.sound(); // 出力: 動物の音を出します。ニャーニャー!
cat.playWithToy(); // 出力: おもちゃで遊びます。
}
}
継承のメリット
コードの再利用性
継承により、親クラスの属性やメソッドを子クラスに引き継ぐことができます。
そのため、共通の機能を持つクラスを作成し、それを他のクラスで継承することで、同じコードを再利用することができます。
これにより、コードの冗長性を減らし、効率的な開発を促します。
継承のデメリット
割り当てられたクラス数の制限
一つのクラスしか継承できないため、複数のクラスからの特性を継承することができません。
結合度の高さ
継承により、親クラスと子クラスの間に強い結合が生じます。
親クラスの変更が子クラスに影響を及ぼし、子クラスの振る舞いを変える可能性があります。これは、適切な設計や変更管理がなされない場合に問題となることがあります。
参考
継承は親クラスと子クラスの結合度が強い(保守性が低い)
継承では、子クラスがそのまま親クラスのメソッドを利用するため結合度が高くなってしまいます
つまり親クラスに修正が入ると子クラスに大きく影響しまい、保守性(変更容易性)を下げてしまいます
そこで出てくるのが抽象クラスとインターフェースです
抽象クラスとは
抽象クラスとは抽象メソッドを1つ以上持つクラスのこと
抽象クラスは自身だけでは意味を持たずに、サブクラスで継承されることで初めて機能するものです。
abstract class クラス名{
abstract 戻り値の型 メソッド名(引数の型 引数);
}
継承とは異なり、上記のように抽象メソッドを使っている
抽象クラス
abstract class Animal {
protected String name;
public Animal(String name) {
this.name = name;
}
public abstract void speak();
public void sleep() {
System.out.println(name + " is sleeping.");
}
}
抽象クラスを継承
class Dog extends Animal {
public Dog(String name) {
super(name);
}
@Override
public void speak() {
System.out.println("Woof!");
}
}
Mainメソッド
public class Main {
public static void main(String[] args) {
Animal dog = new Dog("Hachiko");
dog.speak(); // Output: Woof!
dog.sleep(); // Output: Hachiko is sleeping.
}
}
こんな感じで抽象メソッドをOverrideして利用します
親クラスでspeakの内部実装を変更しても影響が無い=親クラスと子クラスとの結合度が低くなる
つまり保守性が高まる!
一応抽象クラスの特徴
- 抽象クラスを継承したサブクラスは、抽象クラスにある抽象メソッドのオーバーライド必須
- サブクラスでコンストラクタを記述しなければならない
- 直接インスタンス化できない
- 多重継承はできない
以下を参考にしています
インターフェースとは
メソッドの具体的な処理を記載せず以下を明記したものです
- 変数
- メソッドの型
以下のように記述します
public interface Caluculator {
int calc(int a, int b);
}
インターフェースの簡単なコード例
インターフェースのクラス
interface Animal {
void speak();
}
インターフェースを実装したクラス1
class Dog implements Animal {
@Override
public void speak() {
System.out.println("Woof!");
}
}
インターフェースを実装したクラス2
class Cat implements Animal {
@Override
public void speak() {
System.out.println("Meow!");
}
}
Mainクラス
public class Main {
public static void main(String[] args) {
Animal dog = new Dog();
Animal cat = new Cat();
dog.speak(); // Output: Woof!
cat.speak(); // Output: Meow!
}
}
こんな感じでInterfaceのメソッドををOverrideして利用します
こちらも親クラスでspeakの内部実装を変更しても影響が無い=親クラスと子クラスとの結合度が低くなる
つまり保守性が高まる!
インターフェースの特徴
- インターフェースを実装したクラスは、インターフェースに定義された全てのメソッドを実装する必要がある
- クラスが複数のインターフェースを同時に実装することができる(多重実装)
- インターフェースのメソッドは全て抽象メソッドであり、実装はインターフェースを実装したクラスで行われる
- インターフェースはフィールド(変数)を持つことができない。フィールドは定数(static final)のみを持つことができる
- インターフェースは他のインターフェースを継承することができる(インターフェースの継承)
参考
なぜインターフェースや抽象クラス使う必要あるの?
ここまで見ていただくと、結局子クラスでメソッド書かなけばいけないじゃんとなるので
インターフェースと抽象クラス要らないじゃんと思った方もいると思います。
抽象クラスを実装するメリットは、抽象クラスの特徴1に書いた、"抽象クラスを継承したサブクラスは、抽象クラ>スにある抽象メソッドを必ずオーバーライドしなければならない" です。
これが意味する具体的なメリットは、、、
複数人で開発を行う場合に実装レベルのルールを作れる! です。
そう言われてもこのメリットは、企業での大規模開発を経験してみないと正直わからないと思います。(私は、さっぱりわかりませんでした)
では、ここで大規模開発(複数人で開発を行なっていて、コードの量は数千〜数十万行に及ぶような開発)を行なっているとして、単純な機能追加や画面追加によるプログラムの修正が必要になったとしましょう。
単純な機能追加の場合、メソッドのロジックは少し違えど、やりたいことは既に実装しているクラスと大体処理が同じ場合が多々あります。そんな場合、同じような処理をしているのにクラスによってメソッド名が異なると、何の処理をしているのか把握するのに時間がかかってしまったりします
それが、抽象クラスの特徴である強制的なオーバーライドにより、必ず書かなければいけないようにすることで、以下のような効果が得られます
メソッド名を統一し、ロジックを共通化し、大体何の処理をしているか把握しやすくなる
共通の処理をいちいち全てのクラスに書き込む必要がなくなり、個別の処理も追加しやすくもなる
開発者がサブクラスを定義した際に、メソッドの実装忘れやメソッド名に間違いがあればコンパイルエラーが起き、コーディングミスを防ぐ
抽象クラスを実装すると、このように複数人で開発を行う場合に実装レベルのルールを作れる のです!!
これが抽象クラスを実装する意図であり理由の1つです。
そこで上記記事を読んでください
自分はなるほどなーとなりました
(後はインターフェースとかは拡張性があるので、後から同様機能の追加やJUnitでMockクラス作ったりするのが楽になりますね。テンプレあるのとないのとでは作業工数変わるのと同じ認識でいいと思います)
SuperVS抽象メソッド&インターフェースちょっとした例
抽象クラス/インターフェースをどのように使い分けるべきか?
抽象クラス
継承の階層を作るための共通の属性や振る舞いを持つクラスを作る場合に適している
インターフェース
特定のメソッドの実装を強制する場合や、クラスの柔軟な関係を表現する場合に適しています
他の記事の参考
インターフェースや抽象クラスは、メソッドの具体的な処理内容を記述しないことから、両者に違いはないように思>えるかもしれませんが、インターフェースはクラスで共通する仕様を定義するとき、抽象クラスは他のクラスの処理>の骨組みを定義するときに利用されます。