Java入門 STEP14|抽象クラスを学ぼう

karin@taishi@felmia

抽象クラスとは?

STEP13では、オーバーライドを使って、親クラスから継承したメソッドの処理を子クラス側で変更しました。

例えば、Animalを継承したDogとCatで、それぞれ鳴き声を変えていましたね。

ここで少し考えてみます。

Animalという「動物」には鳴くという共通点がありますが、どんな鳴き声なのかは動物によって違います。

犬なら「ワン!」、猫なら「ニャー!」です。

このような場合、親クラス側では、

「鳴く機能は必要だけど、具体的な処理は子クラスに任せたい」

と考えることができます。

そこで使えるのが抽象クラスです。

抽象クラスとは?

継承されることを前提として作るクラスです。

共通するフィールドやメソッドをまとめながら、具体的な処理を子クラス側に任せることができます。

ルミィ
ルミィ

親クラスで全部決めなくてもいいんだ!
「ここは子クラスで決めてね」ってできるんだね♪

abstractを使ってみよう

抽象クラスを作るときは、classの前にabstractを付けます。

public abstract class Animal {

}

これでAnimalは通常のクラスではなく、抽象クラスになります。

書き方は、

public abstract class クラス名 {

}

です。

まずは、

abstract class → 抽象クラス

と覚えておけば大丈夫です。

抽象メソッドとは?

抽象クラスでは、抽象メソッドを定義することができます。

例えば、「鳴く」という動作を抽象メソッドにすると、次のように書けます。

public abstract void makeSound();

いつものメソッドと少し違いますね。

これまでなら、

public void makeSound() {
    System.out.println("鳴きます");
}

のように、{ }の中に処理を書いていました。

抽象メソッドでは具体的な処理を書きません。

そのため、

public abstract void makeSound();

のように、最後を;で終わらせます。

抽象メソッドとは?

具体的な処理を持たず、子クラス側で処理を定義するためのメソッドです。

メソッドにabstractを付け、{ }の代わりに;で終わらせます。

Animalを抽象クラスにしてみよう

ここまでの内容を使って、Animalクラスを作ります。

public abstract class Animal {

    public abstract void makeSound();
}

AnimalにはmakeSound()がありますが、具体的な処理は書かれていません。

「動物は鳴く」ということだけを決めて、どう鳴くのかは子クラスに任せています。

次に、このAnimalをDogで継承します。

public class Dog extends Animal {

    @Override
    public void makeSound() {
        System.out.println("ワン!");
    }
}

STEP13で学んだ@Overrideが出てきました。

Animal側では、

public abstract void makeSound();

とだけ決めていました。

その具体的な処理を、Dog側の、

@Override
public void makeSound() {
    System.out.println("ワン!");
}

で定義しています。

Catも作ってみよう

同じAnimalを継承してCatも作れます。

public class Cat extends Animal {

    @Override
    public void makeSound() {
        System.out.println("ニャー!");
    }
}

DogとCatはどちらもAnimalを継承しています。

ただし、makeSound()の処理はそれぞれ違います。

Dog

ワン!

Cat

ニャー!

親クラスでは「makeSound()を持つ」という共通部分だけを決め、実際の処理はそれぞれの子クラスで作っています。

ルミィ
ルミィ

Animalでは「鳴いてね」まで決めて、
どう鳴くかはDogやCatに任せてるんだね♪

抽象メソッドは子クラスで実装する

ここは抽象クラスを使ううえで大切なところです。

Animalに、

public abstract void makeSound();

がある状態で、Dogを次のように作ったとします。

public class Dog extends Animal {

}

これではエラーになります。

Animalにある抽象メソッドmakeSound()の処理を、Dog側で定義していないためです。

そのため、

@Override
public void makeSound() {
    System.out.println("ワン!");
}

を追加する必要があります。

抽象メソッドを継承したら

抽象クラスを継承した通常の子クラスでは、親クラスにある抽象メソッドを実装する必要があります。

これによって、子クラス側で必要なメソッドの作り忘れを防ぐこともできます。

抽象クラスからnewはできない

もうひとつ大きな特徴があります。

通常のクラスなら、

Dog dog = new Dog();

のようにオブジェクトを作ることができます。

しかし、抽象クラスであるAnimalから、

Animal animal = new Animal();

とすることはできません。

抽象クラスは、子クラスに継承して使うことを前提としたクラスだからです。

今回のAnimalも、

動物という共通部分をまとめる

ためのクラスであり、

Animalという動物そのものを作る

ためのクラスではありません。

ルミィ
ルミィ

AnimalはDogやCatの共通部分をまとめる役なんだね。
だからAnimalそのものはnewしないんだ!

通常のメソッドも作れる

「抽象クラスには抽象メソッドしか書けないの?」

と思うかもしれませんが、そんなことはありません。

抽象クラスには、これまでと同じ通常のメソッドも定義できます。

例えば、すべての動物が「食べる」という共通の処理を持つことにします。

public abstract class Animal {

    public void eat() {
        System.out.println("ごはんを食べます");
    }

    public abstract void makeSound();
}

eat()には具体的な処理があります。

一方、makeSound()は抽象メソッドなので、処理がありません。

このAnimalを継承したDogでは、

public class Dog extends Animal {

    @Override
    public void makeSound() {
        System.out.println("ワン!");
    }
}

とします。

Mainクラスから呼び出します。

public class Main {

    public static void main(String[] args) {

        Dog dog = new Dog();

        dog.eat();
        dog.makeSound();
    }
}

実行結果

ごはんを食べます
ワン!

eat()は親クラスの処理をそのまま使い、makeSound()はDog側で決めています。

つまり抽象クラスでは、

共通する処理 → 親クラスに書く

子クラスごとに変わる処理 → 抽象メソッドにする

という分け方ができます。

どんなときに使う?

抽象クラスは、複数のクラスに共通する部分と、それぞれ異なる部分がある場合に使うことができます。

今回なら、

Animal

という共通の親クラスがあり、

Dog

Cat

という子クラスがあります。

「食べる」は共通の処理としてAnimalに書き、

「鳴く」は動物によって違うので抽象メソッドにしました。

抽象クラスの考え方

共通する処理は親クラスにまとめる。

子クラスごとに処理が変わる部分は、抽象メソッドとして子クラスに実装させる。

すべてを抽象メソッドにする必要はありません。

最初から「どこで抽象クラスを使えばいいのか」を完璧に判断する必要はありません。

まずは、共通部分をまとめながら、子クラス側に処理を任せることができるという役割を理解しておきましょう。

よくある間違い

抽象クラスにabstractを付け忘れる

抽象メソッドを持つクラスは、クラス側にもabstractが必要です。

public abstract class Animal {

    public abstract void makeSound();
}

makeSound()だけにabstractを付ければよいわけではありません。

抽象メソッドに処理を書いてしまう

抽象メソッドは、

public abstract void makeSound();

のように、具体的な処理を書かずに定義します。

処理を書く場合は通常のメソッドとして定義します。

抽象クラスをnewしようとする

Animal animal = new Animal();

抽象クラスから直接オブジェクトを作ることはできません。

Dogなどの子クラスを作り、

Dog dog = new Dog();

のように使用します。

STEP14のまとめ

STEP14のポイント

  • 抽象クラスにはabstractを付ける
  • 抽象クラスは継承されることを前提として使う
  • 抽象メソッドには具体的な処理を書かない
  • 抽象メソッドを継承した通常の子クラスでは、そのメソッドを実装する
  • 抽象クラスには通常のメソッドも定義できる
  • 抽象クラスから直接オブジェクトを作ることはできない
  • 共通する処理をまとめながら、異なる処理を子クラス側に任せることができる

最初は、「共通部分は親クラスにまとめて、具体的な処理の一部を子クラスに任せることができる」と理解できれば大丈夫です。

ルミィ
ルミィ

STEP14クリア!
共通するところはまとめつつ、子クラスに「ここはお願い!」ってできるのが抽象クラスなんだね♪

次のSTEPへ

次のSTEPでは、抽象クラスと少し似た役割を持つ「インターフェース」について学びます。

例えば、DogやCatとはまったく違うクラスでも、

「走ることができる」

「飛ぶことができる」

など、同じ機能を持たせたいことがあります。

継承では基本的に1つのクラスを親クラスとして指定しますが、インターフェースを使うと、クラスの種類とは別に「この機能を持っています」という共通のルールを作ることができます。

抽象クラスとの違いも含めて確認していきます。

→ STEP15「インターフェースを学ぼう」

学習内容を確認したいときは、Java入門の一覧から各STEPを確認できます。

→ Java入門|初心者が基礎から学べるJava学習ガイド

記事URLをコピーしました