3
3

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

Gakken LEAPAdvent Calendar 2024

Day 20

抽象クラスとジェネリクスを組み合わせて活用する

Last updated at Posted at 2024-12-19

はじめに

こんにちは、Gakken LEAP のエンジニアの okamoto です。
Javaで複数種類のCSVファイルを読み込み、それぞれに対応した処理を行うコードを実装した際、ファイルごとに異なる型でデータを管理する必要がありました。
この際に、抽象クラスとジェネリクスを活用することで、拡張性や再利用性が向上しました。
本記事ではその際に活用した各技術の簡単な紹介と、それらを組み合わせたアプローチを紹介します。

ジェネリクス(総称型)とは

簡潔に表すと「いろんな型に対応できる箱を作る仕組み」 です。
ジェネリクスを使って定義する際には<>の中に任意の文字列を入れられますが、
T(type)を入れるのが一般的です。
※他にもE(element)やK(key)V(value)などもあります。

// 型を後から決める箱
class Box<T> {
  // ...省略
}

// 使うときに自由に型を決められる
Box<Integer> intBox = new Box<>();
Box<String> strBox = new Box<>();
Box<Object> objBox = new Box<>();

ジェネリクスにおけるワイルドカードとは

上記のように<>内で型を明確に指定しなくても宣言することができる仕組みで、
「箱の中身の型が曖昧」な場合に利用できます。
型の上限(? extends [Type])、下限(? super [Type])を制限することもできますが、本記事では割愛します。

// 型を後から決める箱
class Box<T> {
  // ...省略
}
// どんな型のBoxでも参照可能
Box<?> wcBox = new Box<String>();
wcBox = new Box<Integer>();

Object data = wcBox.get(); // 取り出すときはObject型になる

抽象クラスとは

すべての実装を行わず、サブクラスたちで共通化したい処理のみ実装し、
その他は曖昧なまま定義を行い、それを継承したサブクラス側で具体的な実装をする、
「共通の機能を持たせるための『ひな形』」です。

// 抽象クラス
abstract class Hoge {
    abstract void printClassName();
    void printHoge() {
        System.out.println("Hoge");
    }
}
// サブクラス1
class Fuga extends Hoge {
    @Override
    void printClassName() {
        System.out.println("Fuga");
    }
}
// サブクラス2
class Piyo extends Hoge {
    @Override
    void printClassName() {
        System.out.println("Piyo");
    }
}
Hoge fuga = new Fuga();
fuga.printHoge();      // 出力:Hoge
fuga.printClassName(); // 出力:Fuga
Hoge piyo = new Piyo();
piyo.printHoge();      // 出力:Hoge
piyo.printClassName(); // 出力:Piyo

抽象クラスとジェネリクスの組み合わせ

以下はジェネリクスを使用した抽象クラスと、それぞれ異なる型でデータ管理するサブクラスです。

// 抽象クラスをジェネリクスで定義する
abstract class InputCsv<T> {
    private T csvData;
    public T getCsvData() {
        return csvData;
    };
    public void setCsvData(T inputCsvData) {
        csvData = inputCsvData;
    };
}
// サブクラス1
class Csv1 extends InputCsv<Entity1> {
    // csv1用の処理
}
// サブクラス2
class Csv2 extends InputCsv<Entity2> {
    // csv2用の処理
}

これらのクラスを作成し、下記のようにループの中でワイルドカードを使用してインスタンスを生成することで、処理対象のCSVファイルが増えたとしても大きく手を加える必要がなくなります。

// 読み取り対象のCSVリストを順に処理する
for(String csv : csvList) {
    // ファイル名から利用するインスタンスを取得する
    InputCsv<?> inputCsv = getCsvInstanceService.get(csv);
    // ...後続の処理
}

おわりに

本記事では、抽象クラスとジェネリクスを組み合わせた柔軟で拡張性が高い実装を紹介しました。
さらに型安全性を高めるには今回は割愛しているワイルドカードの上限を設定することで実現できるかと思います。

エンジニア募集

Gakken LEAP では教育をアップデートしていきたいエンジニアを絶賛大募集しています!
ぜひお気軽にカジュアル面談へお越しください!
https://gakken-leap.co.jp/recruit/

3
3
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
3
3

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?