Javaの基礎Java

Javaの基礎:高度な継承を解説(抽象クラスとインターフェース)

Java高度な継承解説ページのアイキャッチ画像 Javaの基礎

この記事では、Javaにおける高度な継承の概念、抽象クラスとインターフェースの使用方法と違いについて理解することができます。具体的なプログラム例を通じて、これらの概念の実践的な方法を学ぶことができます。

はじめに

この記事のコードをコピペしてEclipseで出力結果を確認してみよう

高度な継承の基本

Javaの高度な継承では、抽象クラスインターフェースが重要な役割を果たします。これらを使用することで、より柔軟で再利用性の高いコードを作成できます。

抽象クラス

抽象クラスは、完全に実装されていないメソッド(抽象メソッド)を含むクラスです。抽象クラスは直接インスタンス化できず、必ず継承されて使用されます。

インターフェース

インターフェースは、メソッドの仕様のみを定義し、実装はサブクラスに任せる仕組みです。複数のインターフェースを実装できるため、多重継承の一部を実現できます。

プログラム例1: 抽象クラスを使用した図形の面積計算

Shape.java

Java
// 抽象クラス Shape
abstract class Shape {
    // 抽象メソッド
    public abstract double calculateArea();

    // 通常のメソッド
    public void printArea() {
        System.out.println("面積: " + calculateArea());
    }
}

Circle.java

Java
// Shapeを継承した具体クラス Circle
class Circle extends Shape {
    private double radius;

    public Circle(double radius) {
        this.radius = radius;
    }

    // 抽象メソッドの実装
    @Override
    public double calculateArea() {
        return Math.PI * radius * radius;
    }
}

Rectangle.java

Java
// Shapeを継承した具体クラス Rectangle
class Rectangle extends Shape {
    private double width;
    private double height;

    public Rectangle(double width, double height) {
        this.width = width;
        this.height = height;
    }

    // 抽象メソッドの実装
    @Override
    public double calculateArea() {
        return width * height;
    }
}

ShapeExample.java

Java
// メインクラス
public class ShapeExample {
    public static void main(String[] args) {
        Shape circle = new Circle(5);
        Shape rectangle = new Rectangle(4, 6);

        circle.printArea();
        rectangle.printArea();
    }
}

出力結果:

面積: 78.53981633974483
面積: 24.0

Shape.javaの解説

抽象クラスの宣言

Java
abstract class Shape {
    // クラスの中身
}
  • abstract キーワードを使用して、Shapeクラスを抽象クラスとして宣言しています。
  • 抽象クラスは、直接インスタンス化できないクラスです。
  • 抽象クラスは、共通の振る舞いを定義し、サブクラスで具体的な実装を提供するためのテンプレートとして機能します。

抽象メソッドの宣言

Java
public abstract double calculateArea();
  • abstract キーワードを使用して、抽象メソッドを宣言しています。
  • 抽象メソッドは本体を持たず、セミコロンで終わります。
  • このメソッドは、各具体的な形状(サブクラス)で必ず実装しなければなりません。
  • double 型の戻り値を持つことが指定されています。

通常のメソッドの定義

Java
public void printArea() {
    System.out.println("面積: " + calculateArea());
}
  • これは通常のメソッド(具象メソッド)です。
  • 抽象クラス内でも、通常のメソッドを定義できます。
  • このメソッドは、calculateArea() メソッドを呼び出して面積を計算し、結果を出力します。
  • サブクラスは、このメソッドをそのまま継承するか、オーバーライドすることができます。

このコードは、形状を表す抽象的な概念を定義しています。Shape クラスを継承する具体的な形状クラス(例:円、四角形など)は、calculateArea() メソッドを実装して、その形状特有の面積計算ロジックを提供する必要があります。これにより、多様な形状に対して統一的なインターフェースを提供しながら、各形状の特性に応じた実装の柔軟性を確保しています。

Circle.javaの解説

クラスの宣言と継承

Java
class Circle extends Shape {
    // クラスの中身
}
  • Circle クラスを宣言し、extends キーワードを使用して Shape クラスを継承しています。
  • これにより、CircleShape具象サブクラスとなります。

フィールドの宣言

Java
private double radius;
  • radius というプライベートフィールドを宣言しています。
  • このフィールドは円の半径を表し、カプセル化されています(外部から直接アクセスできません)。

コンストラクタの定義

Java
public Circle(double radius) {
    this.radius = radius;
}
  • Circle クラスのコンストラクタを定義しています。
  • コンストラクタは、オブジェクト生成時に呼び出され、初期化を行います。
  • this.radius = radius; で、引数で受け取った値をフィールドに代入しています。

抽象メソッドの実装

Java
@Override
public double calculateArea() {
    return Math.PI * radius * radius;
}
  • Shape クラスで宣言された抽象メソッド calculateArea()オーバーライドして実装しています。
  • @Override アノテーションは、このメソッドが親クラスのメソッドをオーバーライドしていることを明示します。
  • 円の面積計算公式(π * r^2)を使用して、面積を計算し返却しています。
  • Math.PI は円周率πの値を表す定数です。

このコードは、抽象クラス Shape を継承した具体的な形状クラス Circle を定義しています。Circle クラスは以下の特徴を持ちます:

  1. Shape クラスの抽象メソッドを具体的に実装しています。
  2. 円特有のプロパティ(半径)を持ち、カプセル化しています。
  3. コンストラクタを通じて初期化が可能です。
  4. 円の面積を計算する具体的なロジックを提供しています。

この実装により、Circle オブジェクトは Shape として扱うことができ、多態性を活用したプログラミングが可能になります。また、Shape クラスで定義された printArea() メソッドも継承されるため、円の面積を簡単に出力することができます。

Rectangle.javaの解説

クラスの宣言と継承

Java
class Rectangle extends Shape {
    // クラスの中身
}
  • Rectangle クラスを宣言し、extends キーワードを使用して Shape クラスを継承しています。
  • これにより、RectangleShape具象サブクラスとなります。

フィールドの宣言

Java
private double width;
private double height;
  • widthheight という2つのプライベートフィールドを宣言しています。
  • これらのフィールドは長方形の幅と高さを表し、カプセル化されています(外部から直接アクセスできません)。

コンストラクタの定義

Java
public Rectangle(double width, double height) {
    this.width = width;
    this.height = height;
}
  • Rectangle クラスのコンストラクタを定義しています。
  • コンストラクタは、オブジェクト生成時に呼び出され、初期化を行います。
  • this.width = width;this.height = height; で、引数で受け取った値をそれぞれのフィールドに代入しています。

抽象メソッドの実装

Java
@Override
public double calculateArea() {
    return width * height;
}
  • Shape クラスで宣言された抽象メソッド calculateArea()オーバーライドして実装しています。
  • @Override アノテーションは、このメソッドが親クラスのメソッドをオーバーライドしていることを明示します。
  • 長方形の面積計算公式(幅 * 高さ)を使用して、面積を計算し返却しています。

このコードは、抽象クラス Shape を継承した具体的な形状クラス Rectangle を定義しています。Rectangle クラスは以下の特徴を持ちます:

  1. Shape クラスの抽象メソッドを具体的に実装しています。
  2. 長方形特有のプロパティ(幅と高さ)を持ち、カプセル化しています。
  3. コンストラクタを通じて両方のプロパティを初期化できます。
  4. 長方形の面積を計算する具体的なロジックを提供しています。

この実装により、Rectangle オブジェクトは Shape として扱うことができ、多態性を活用したプログラミングが可能になります。また、Shape クラスで定義された printArea() メソッドも継承されるため、長方形の面積を簡単に出力することができます。

Circle クラスと比較すると、Rectangle クラスは2つのパラメータ(幅と高さ)を持つ点が異なりますが、Shape の抽象概念を具体化するという点では同じ役割を果たしています。

ShapeExample.javaの解説

クラスの宣言

Java
public class ShapeExample {
    // クラスの中身
}
  • ShapeExample という公開クラスを宣言しています。
  • このクラスは、プログラムのエントリーポイントとなる main メソッドを含みます。

mainメソッドの宣言

Java
public static void main(String[] args) {
    // メソッドの中身
}
  • main メソッドを宣言しています。これは、プログラムの実行開始点です。
  • public static void キーワードは、このメソッドが公開された、静的な、戻り値のないメソッドであることを示します。

オブジェクトの生成

Java
Shape circle = new Circle(5);
Shape rectangle = new Rectangle(4, 6);
  • Shape 型の変数に、CircleRectangle のインスタンスを代入しています。
  • これは多態性の例です。具体的なサブクラスのオブジェクトを、抽象的な基底クラスの型で参照しています。
  • Circle のコンストラクタには半径 5 を、Rectangle のコンストラクタには幅 4 と高さ 6 を渡しています。

メソッドの呼び出し

Java
circle.printArea();
rectangle.printArea();
  • 生成したオブジェクトの printArea() メソッドを呼び出しています。
  • このメソッドは Shape クラスで定義されていますが、実際の面積計算は各サブクラスの calculateArea() メソッドで行われます。

このコードは、抽象クラス Shape とそのサブクラス Circle および Rectangle を使用して、多態性を実演しています。重要なポイントは以下の通りです:

  1. 抽象クラス型の変数で具体的なサブクラスのオブジェクトを参照しています。
  2. 同じメソッドprintArea())を呼び出していますが、各オブジェクトの具体的な実装に基づいて異なる結果が得られます。
  3. この設計により、新しい形状(例:三角形)を追加する際に、既存のコードを変更せずに拡張できます。

この例は、オブジェクト指向プログラミングの重要な概念である継承多態性抽象化を実践的に示しています。これにより、柔軟で拡張性の高いコードを作成できることがわかります。

プログラム例2: インターフェースを使用した動物の鳴き声

Animal.java

Java
// 動物の行動を定義するインターフェース
interface Animal {
    void makeSound(); // 抽象メソッド
}

Dog.java

Java
// Animalインターフェースを実装する具体クラス Dog
class Dog implements Animal {
    @Override
    public void makeSound() {
        System.out.println("ワンワン!");
    }
}

Cat.java

Java
// Animalインターフェースを実装する具体クラス Cat
class Cat implements Animal {
    @Override
    public void makeSound() {
        System.out.println("ニャーニャー!");
    }
}

AnimalExample.java

Java
// メインクラス
public class AnimalExample {
    public static void main(String[] args) {
        Animal dog = new Dog();
        Animal cat = new Cat();

        dog.makeSound();
        cat.makeSound();
    }
}

出力結果:

ワンワン!
ニャーニャー!

Animal.javaの解説

インターフェースの宣言

Java
interface Animal {
    // インターフェースの中身
}
  • interface キーワードを使用して、Animal というインターフェースを宣言しています。
  • インターフェースは、抽象メソッドの集合を定義するための特殊な型です。
  • インターフェースは、クラスが実装すべき契約を定義します。

抽象メソッドの宣言

Java
void makeSound();
  • makeSound() という抽象メソッドを宣言しています。
  • インターフェース内のメソッドは、デフォルトで public と abstract になるため、これらのキーワードは省略されています。
  • このメソッドは本体を持たず、セミコロンで終わっています。
  • 戻り値の型は void で、パラメータはありません。

このコードは、Animal インターフェースを定義しています。このインターフェースの特徴と重要性は以下の通りです:

  1. 抽象化: Animal インターフェースは、動物の基本的な行動(この場合は音を出すこと)を抽象化しています。
  2. 契約: このインターフェースを実装するクラスは、makeSound() メソッドを必ず実装しなければなりません。
  3. 多態性: 異なる動物クラスが同じインターフェースを実装することで、多態的な振る舞いが可能になります。
  4. 拡張性: 新しい動物クラスを追加する際、このインターフェースを実装するだけで、既存のコードとの互換性が保たれます。
  5. 疎結合: インターフェースを使用することで、具体的な実装からコードを分離し、より柔軟なシステム設計が可能になります。

このインターフェースを使用することで、例えば Dog クラスや Cat クラスなど、様々な動物クラスを作成し、それぞれが独自の方法で makeSound() メソッドを実装することができます。これにより、多様な動物の鳴き声を表現しつつ、統一的な方法でそれらを扱うことが可能になります。

Dog.javaの解説

クラスの宣言とインターフェースの実装

Java
class Dog implements Animal {
    // クラスの中身
}
  • Dog クラスを宣言しています。
  • implements キーワードを使用して、Animal インターフェースを実装していることを示しています。
  • これにより、Dog クラスは Animal インターフェースで定義されたすべてのメソッドを実装する義務を負います。

インターフェースメソッドの実装

Java
@Override
public void makeSound() {
    System.out.println("ワンワン!");
}
  • Animal インターフェースで定義された makeSound() メソッドを実装しています。
  • @Override アノテーションは、このメソッドがインターフェースのメソッドを実装(オーバーライド)していることを明示します。
  • メソッドは public として宣言されています。インターフェースのメソッドを実装する際は、必ず public にする必要があります。
  • メソッド内で、犬の鳴き声(”ワンワン!”)を出力しています。

このコードは、Animal インターフェースを実装した具体的な Dog クラスを定義しています。Dog クラスの特徴と重要性は以下の通りです:

  1. インターフェースの実装: Animal インターフェースを実装することで、Dog クラスは動物としての契約を満たしています。
  2. 具体的な振る舞いの提供: makeSound() メソッドに具体的な実装を提供しています。これは犬特有の鳴き方を表現しています。
  3. 多態性の実現: このクラスのインスタンスは、Animal 型の変数に代入できます。これにより、多態的なコードが書けるようになります。
  4. カプセル化: インターフェースを実装することで、Dog クラスの内部実装の詳細を隠蔽しつつ、必要な機能を公開しています。
  5. 拡張性: 将来的に Dog クラスに新しい機能を追加する際も、Animal インターフェースとの互換性を保ったまま拡張できます。

このような実装により、例えば以下のようなコードが可能になります:

Java
Animal myDog = new Dog();
myDog.makeSound(); // 出力: ワンワン!

これは、オブジェクト指向プログラミングの重要な概念であるポリモーフィズム(多態性)を示しています。Animal 型の変数で Dog オブジェクトを参照し、インターフェースで定義されたメソッドを呼び出すことができます。

Cat.javaの解説

クラスの宣言とインターフェースの実装

Java
class Cat implements Animal {
    // クラスの中身
}
  • Cat クラスを宣言しています。
  • implements キーワードを使用して、Animal インターフェースを実装していることを示しています。
  • これにより、Cat クラスは Animal インターフェースで定義されたすべてのメソッドを実装する義務を負います。

インターフェースメソッドの実装

Java
@Override
public void makeSound() {
    System.out.println("ニャーニャー!");
}
  • Animal インターフェースで定義された makeSound() メソッドを具体的に実装しています。
  • @Override アノテーションは、このメソッドがインターフェースのメソッドを実装(オーバーライド)していることを明示します。
  • メソッドは public として宣言されています。インターフェースのメソッドを実装する際は、必ず public にする必要があります。
  • メソッド内で、猫の鳴き声(”ニャーニャー!”)を出力しています。

このコードは、Animal インターフェースを実装した具体的な Cat クラスを定義しています。Cat クラスの特徴と重要性は以下の通りです:

  1. インターフェースの実装: Animal インターフェースを実装することで、Cat クラスは動物としての契約を満たしています。
  2. 具体的な振る舞いの提供: makeSound() メソッドに猫特有の実装を提供しています。これは Dog クラスとは異なる、猫の鳴き方を表現しています。
  3. 多態性の実現: このクラスのインスタンスも、Animal 型の変数に代入できます。これにより、多態的なコードが書けるようになります。
  4. 一貫性と多様性: Dog クラスと同じインターフェースを実装しながら、異なる振る舞いを提供しています。これはインターフェースの強力さを示しています。
  5. 拡張性: 将来的に Cat クラスに新しい機能(例:毛づくろいなど)を追加する際も、Animal インターフェースとの互換性を保ったまま拡張できます。

このような実装により、以下のようなコードが可能になります:

Java
Animal myCat = new Cat();
myCat.makeSound(); // 出力: ニャーニャー!

これは、Dog クラスの例と同様にポリモーフィズム(多態性)を示しています。Animal 型の変数で Cat オブジェクトを参照し、インターフェースで定義されたメソッドを呼び出すことができます。

Dog クラスと Cat クラスの実装を比較すると、同じインターフェースを実装しながら、それぞれの動物に特有の振る舞いを提供していることがわかります。これにより、共通の型(Animal)を使用しつつ、個々の動物の特性を表現することができます。

AnimalExample.javaの解説

クラスの宣言

Java
public class AnimalExample {
    // クラスの中身
}
  • AnimalExample という公開クラスを宣言しています。
  • このクラスは、プログラムのエントリーポイントとなる main メソッドを含みます。

mainメソッドの宣言

Java
public static void main(String[] args) {
    // メソッドの中身
}
  • main メソッドを宣言しています。これは、プログラムの実行開始点です。
  • public static void キーワードは、このメソッドが公開された、静的な、戻り値のないメソッドであることを示します。

オブジェクトの生成と変数の宣言

Java
Animal dog = new Dog();
Animal cat = new Cat();
  • Animal 型の変数に、DogCat のインスタンスをそれぞれ代入しています。
  • これはポリモーフィズム(多態性)の例です。インターフェース型の変数で、そのインターフェースを実装したクラスのオブジェクトを参照しています。

メソッドの呼び出し

Java
dog.makeSound();
cat.makeSound();
  • 生成したオブジェクトの makeSound() メソッドを呼び出しています。
  • このメソッドは Animal インターフェースで定義されていますが、実際の実装は各具体クラス(DogCat)で行われています。

このコードは、Animal インターフェースとそれを実装した Dog クラスと Cat クラスを使用して、ポリモーフィズムを実演しています。重要なポイントは以下の通りです:

  1. インターフェース型の変数で具体的な実装クラスのオブジェクトを参照しています。
  2. 同じメソッドmakeSound())を呼び出していますが、各オブジェクトの具体的な実装に基づいて異なる結果が得られます。
  3. この設計により、新しい動物クラス(例:Bird)を追加する際に、既存のコードを変更せずに拡張できます。

この例は、オブジェクト指向プログラミングの重要な概念であるインターフェースポリモーフィズム抽象化を実践的に示しています。これにより:

  • 柔軟性: 異なる動物クラスを同じ方法で扱えます。
  • 拡張性: 新しい動物クラスの追加が容易です。
  • 保守性: インターフェースを変更せずに実装を変更できます。

このような設計は、大規模なシステムや将来の拡張を考慮したプログラミングにおいて非常に有用です。

まとめ

  • 抽象クラスは共通の振る舞いと状態を持つ関連クラスのための基本クラスとして使用されます。
  • インターフェースはクラスが実装すべきメソッドの契約を定義します。
  • 抽象クラスは単一継承のみ可能ですが、インターフェースは多重実装が可能です。
  • 抽象クラスはprotectedメンバーを持つことができますが、インターフェースのメンバーはすべてpublicです。
  • 抽象クラスは「is-a」関係を表現し、インターフェースは「can-do」関係を表現します。

重要ポイント: 抽象クラスは「何であるか」を、インターフェースは「何ができるか」を定義します。適切に使い分けることで、柔軟で拡張性の高い設計が可能になります。

抽象クラスと「is-a」関係
  • 意味: あるものが別のものの一種であることを表す
  • : 「犬は動物である」
  • 特徴:
    • 共通の特性と振る舞いを定義
    • 単一継承
    • 階層構造を表現
インターフェースと「can-do」関係関係
  • 意味: あるものが特定の能力や行動を持つことを表す
  • : 「鳥は飛ぶことができる」
  • 特徴:
    • 特定の機能セットを定義
    • 多重実装が可能
    • クラス間の疎結合を促進

これらの概念を適切に使用することで、柔軟で拡張性の高いオブジェクト指向設計が可能になります。抽象クラスとインターフェースの特性を理解し、状況に応じて適切に選択することが重要です。

未経験でもWEBスキルを取得したい方
  • Javaコース
    Javaプログラミングを基礎から応用まで、体系的に学べる実践的なカリキュラムが特徴です。「マインクラフト」を通じてJavaの基本文法とフレームワークの概念を学びます。また、実際のプロジェクト開発を通じて、データベース連携やWebアプリケーション開発のスキルを身につけることができます。

コメント

タイトルとURLをコピーしました