Javaのabstractクラスとは? 使い方を具体例でわかりやすく解説【初心者・Bronze対策】

こんにちは、湯本です。
 
先日、Java Bronzeの試験勉強を行っていた新入社員から「abstractって何ですか?」「どういう時に使うものですか?」と質問がありました。abstractクラスは、Javaの継承を理解する上で重要な概念です。この記事では、具体的なコード例を使って、abstractクラスの仕組みと使い方を解説します。

abstract(抽象)クラスとは?

abstractクラスとは、処理の流れを親クラスで定義し、実装の詳細を子クラスに任せるクラスです。abstractは「抽象的な」という意味を持つため、抽象クラスと呼ばれます。具体的には、以下の特徴があります。

  • 実装されていないメソッド(抽象メソッド)を含めることができる
  • 自分自身ではインスタンス化できない
  • 継承されることを前提として設計されている

abstractクラスは、複数のクラスに共通する処理フローや構造を定義し、具体的な実装の詳細は子クラスに任せることで、柔軟で保守性の高いコードを書くことができます。
 
なお、Javaには似た仕組みとして interface もあります。interfaceはすべてのメソッドを子クラスで実装する必要があるのに対し、abstractクラスは共通処理は親クラスに持ち、カスタマイズが必要な部分だけを子クラスで実装できるという違いがあります。
 
この記事では、abstractクラスに絞って解説します。

具体例で理解する

ファイル処理を例に考えてみましょう。CSVファイルもExcelファイルも、以下のような共通の処理フローがあります。

  1. ファイルを開く
  2. データを読み込む
  3. データを処理する
  4. ファイルを閉じる

このうち、「ファイルを開く」「ファイルを閉じる」はどのファイル形式でも同じですが、「データを読み込む」「データを処理する」はファイル形式によって異なります。このような場合にabstractクラスが活躍します。
 

ファイル処理のabstractクラス

abstract class DataProcessor {
    // 共通の処理フロー(テンプレート)
    public void process() {
        openFile();      // ①ファイルを開く
        readData();      // ②データを読む(カスタマイズ可能)
        processData();   // ③データを処理(カスタマイズ可能)
        closeFile();     // ④ファイルを閉じる
    }
    
    // 共通処理:すべてのファイル形式で同じ
    // ①ファイルを開く
    void openFile() {
        System.out.println("ファイルを開きます");
    }
    
    // ④ファイルを閉じる
    void closeFile() {
        System.out.println("ファイルを閉じます");
    }
    
    // 抽象メソッド:子クラスで実装が必要
    // ②データを読む
    abstract void readData();
    // ③データを処理
    abstract void processData();
}

CSVファイル用の実装

class CsvProcessor extends DataProcessor {
    @Override
    void readData() {
        System.out.println("CSVデータを読み込み");
    }
    
    @Override
    void processData() {
        System.out.println("CSV形式で処理");
    }
}

Excelファイル用の実装

class ExcelProcessor extends DataProcessor {
    @Override
    void readData() {
        System.out.println("Excelデータを読み込み");
    }
    
    @Override
    void processData() {
        System.out.println("Excel形式で処理");
    }
}

実行例

public class Main {
    public static void main(String[] args) {
        // CSV処理
        DataProcessor csvProcessor = new CsvProcessor();
        csvProcessor.process();
        
        System.out.println("---");
        
        // Excel処理
        DataProcessor excelProcessor = new ExcelProcessor();
        excelProcessor.process();
    }
}

出力結果

ファイルを開きます
CSVデータを読み込み
CSV形式で処理
ファイルを閉じます
---
ファイルを開きます
Excelデータを読み込み
Excel形式で処理
ファイルを閉じます

abstractクラスの重要なポイント

1. インスタンス化できない

abstractクラスは抽象的な概念を表すため、直接インスタンスを作ることはできません
必ず子クラスを作って具体化し、そこでインスタンス化します。

// ❌ エラー!抽象クラスはインスタンス化できない
DataProcessor processor = new DataProcessor()

2. 抽象メソッドは実装が必須

子クラスで抽象メソッドを実装しないと、コンパイルエラーになります。

class JsonProcessor extends DataProcessor {
    @Override
    void readData() {
        System.out.println("JSONデータを読み込み");
    }
    
    // ❌ processData()を実装していないのでエラー!
}

この「強制力」が重要です。抽象クラスを継承したら、必要なメソッドを実装し忘れることがありません。

3. 共通処理とカスタマイズ部分の両方を持てる

これがabstractクラスの最大の特徴です。

・共通処理:openFile()、closeFile()、process() → すべての子クラスで共有
・カスタマイズ部分:readData()、processData() → 子クラスごとに実装

abstractクラスのメリット

1. コードの重複を防ぐ

共通処理を親クラスに書くことで、同じコードを何度も書く必要がなくなります。

2. 処理フローを統一できる

process()メソッドで処理の順序を定義しているため、どの子クラスでも同じ流れで処理が実行されます。

3. 保守性が高い

共通処理に変更があった場合、親クラスを修正するだけで、すべての子クラスに反映されます。
例えば、ファイルを開く前にログを出力したい場合

void openFile() {
    System.out.println("[LOG] ファイル処理を開始");  // 追加
    System.out.println("ファイルを開きます");
}

この変更だけで、CsvProcessorもExcelProcessorも、すべての子クラスでログが出力されるようになります。

4. 設計の意図を明確にできる

「このクラスを継承したら、これらのメソッドを実装してください」という設計者の意図が、コードで表現されます。

Java Bronze試験対策のポイント

試験でよく問われるポイントをまとめます。

ポイント1:インスタンス化

abstract class Animal {
    abstract void makeSound();
}

// ❌ コンパイルエラー
Animal animal = new Animal();

// ✅ 正しい
class Dog extends Animal {
    void makeSound() {
        System.out.println("ワンワン");
    }
}
Animal dog = new Dog();

ポイント2:抽象メソッドの実装義務

抽象クラスを継承したら、すべての抽象メソッドを実装する必要があります。実装しない場合は、その子クラスもabstractにする必要があります。

ポイント3:通常のメソッドも持てる

abstract class Test {
    // 通常のメソッド(実装あり)
    void normalMethod() {
        System.out.println("通常のメソッド");
    }
    
    // 抽象メソッド(実装なし)
    abstract void abstractMethod();
}

ポイント4:抽象メソッド内から抽象メソッドを呼べる

abstract class Test {
    public void foo() {
        bar();  // 抽象メソッドを呼び出せる
    }
    
    abstract void bar();
}

class Sub extends Test {
    void bar() {
        System.out.println("bar");
    }
}

// 実行
Test t = new Sub();
t.foo();  // "bar"と出力される

この場合、foo()からbar()が呼ばれ、実際のインスタンスの型(Sub)のbar()が実行されます。これはポリモーフィズムと呼ばれる重要な概念です。

まとめ

abstractクラスは、以下のような特徴を持つ強力な仕組みです。

  • 共通処理を一箇所にまとめられる
  • カスタマイズが必要な部分だけを子クラスに任せられる
  • 実装の強制により、実装漏れを防げる
  • 保守性と拡張性の高いコードが書ける

最初は「なぜわざわざ抽象クラスを作るのか?」と疑問に思うかもしれません。しかし、実際のプロジェクトでは、共通の処理フローを持ちながら、細かい部分はケースバイケースで変えたいという場面が頻繁にあります。

abstractクラスは、そのような枠組みは決めるけど、詳細は柔軟にという設計を実現するための、Javaの重要な仕組みです。
 
 
 
 
《関連記事》

Java Bronze試験で間違えやすい問題6選
技術
2020.12.16(Wed)

【Java初心者の登竜門! Java Bronze試験で間違えやすい問題6選

#プログラム #Java

Javaの具体的なスキルと勉強法
技術
2021.10.25(Mon)

【段階別】未経験必見! 業務で求められるJavaの具体的なスキルと勉強法

#まとめ #プログラム #Java

Javaでよく出るエラーと対策5選
技術
2022.1.17(Mon)

これさえ見ればもう怖くない! Javaでよく出るエラーと対策5選

#まとめ #Java

記事をシェア
MOST VIEWED ARTICLES