オブジェクト指向を学習する上でかかせない継承についての解説です。
実は、継承の記述自体や考え方は難しくありません。しかし、仕事などで継承を考える場合は設計などが絡むため、本当に継承が必要かどうか?親子関係は正しいかどうか?などを突き詰めていくと奥が深いものとなっています。
このサイトでは継承のサンプルや練習問題などは記述や利用方法を重点的に書いてありますので、設計も考慮したサンプルおよび練習問題とはなっていません。
継承とは
クラスの継承は他クラスのフィールドやメソッドなど(以下、メンバ)をあたかも自分のクラスのメンバのように扱えるようにする機能のことです。他のクラスのメンバを自分のクラスで扱えるようにすることで、クラスの再利用性を高めることができます。
継承を行うクラス(=自クラス)をサブクラス、継承される他のクラスはスーパークラスと呼ばれます。
※サブクラスは子クラス、スーパークラスを親クラスと呼ぶこともあります
他のクラスを継承する際にはextendsキーワードを利用して、クラス名の右に記述します。
クラスの継承 | class サブクラス名 extends スーパークラス{ |
---|---|
記述例 | class Car extends Vehicle{ |
例えば、乗り物クラスを作成して、乗り物に必要な機能をクラスに持たせます。
イメージ図
また、バイククラスを継承したクラスは乗り物クラスの部品(状態と操作)も持つこととなります。赤字の部分が継承された部品となります。
イメージ図
上記図のように継承したクラスをさらに継承することも可能です。
継承の特徴
Javaでは直接継承できるクラスは一つのみというルールがあり「単一継承」と呼ばれます。(前述の図解のように継承したスーパークラスが他のクラスを継承している場合はありますが)
class定義時にfinalを付けるとそのクラスは継承不可となります。
例:final class ABC{
サブクラスではスーパークラスのメンバを利用できます。
(スーパークラスではサブクラスのメンバは利用できない)
※private宣言が行われている場合は継承関係にあっても、サブクラス側からメンバは利用できません
継承 サンプルプログラム
以下のプログラムを「Vehicle.java」「Autobike.java」「Number33.java」という名前でworkフォルダ内に保存します。
保存が完了したら、コマンドプロンプトを起動し、Number33.javaをコンパイルおよび実行をしてみましょう。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 |
//乗り物クラス class Vehicle{ //フィールド private int speed; private int handle; //アクセルのメソッド、引数はアクセルの強さ void accel(int s){ //アクセルの強さは1-10 if(s > 0 && s < 11) speed = speed + s; //スピードは300が上限 if(speed > 300) speed = 300; } //ブレーキのメソッド、引数はブレーキの強さ void brake(int s){ //ブレーキの強さは1-10 if(s > 0 && s < 11) speed = speed - s; //スピードはマイナスはなし if(speed < 0) speed = 0; } //ハンドル操作のメソッド、引数は角度 void handleOperation(int c){ if(c >= -90 && c <= 90) handle = c; } void display(){ //ハンドルが0度の時は直進 String direction = "直進中"; //ハンドルがマイナス度数は左折中、プラス度数は右折中 if(handle < 0 ) direction = "左折中"; else if(handle > 0 ) direction = "右折中"; System.out.println("ただ今" + direction + "です。"); System.out.println("スピードは" + speed + "km/hです。"); } } |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
//乗り物クラスを継承するオートバイクラス(サブクラス) class Autobike extends Vehicle{ private double fuel ; //ガソリンを入れるメソッド void setFuel(double f){ if(f > 0 && f <= 10) fuel = f; } //ガソリン量を表示 void fuelDisplay(){ System.out.println("ガソリンの量は" + fuel + "リットルです。"); } } |
1 2 3 4 5 6 7 8 9 10 11 12 |
class Number33{ public static void main(String[] args){ Autobike autobike = new Autobike(); autobike.accel(10); //アクセル autobike.brake(2); //ブレーキ autobike.accel(8); //再度アクセル autobike.handleOperation(30); //ハンドルを切る autobike.display(); //表示 autobike.setFuel(10); //ガソリンを入れる autobike.fuelDisplay(); //ガソリン量表示 } } |
実行例
C:\work>javac Number33.java
C:\work>java Number33
ただ今右折中です。
スピードは16km/hです。
ガソリンの量は10.0リットルです。
※実行例はコンパイルおよび実行までの例を表示しています。
サンプルプログラムの補足
Autobike.javaではclass名の右にextendsキーワードを利用して、Vehicleクラスを継承しています。
1 |
class Autobike extends Vehicle{ |
また、Number33.javaではAutobikeクラスのインスタンスautobikeのメソッドを利用しています。autobikeの型であるAutobikeクラスでは以下のメソッドは定義されていませんが、Vehicleクラスを継承しているためスーパークラス(Vehicle)のメソッドも利用できます。
1 2 3 4 5 |
autobike.accel(10); autobike.brake(2); autobike.accel(8); autobike.handleOperation(30); autobike.display(); |
もちろん、Autobikeクラスで定義されている以下の2つのメソッドも利用できます。
1 2 |
autobike.setFuel(10); autobike.fuelDisplay(); |
補足1 継承とインスタンス化
継承はスーパークラスのメンバを利用できるようになります。他クラスのメンバを利用する際には継承を行わずともインスタンスを生成すれば可能です。
クラスのメンバを再利用したい(共通した記述を再利用)という観点から見ると、継承もインスタンス化も同じように思えます。
どちらか悩むようなら、基本的にインスタンス生成を選ぶとよいでしょう。
継承の記述自体は簡単ですが、継承は設計との兼ね合いもある為、冒頭でも述べたような「継承関係を持たせるべきかどうか」「親子関係は正しいか」を検証したうえでスーパークラスやサブクラスを作成します。
例えば、記述ベースで考えていると「あ!前に作ったクラスは再利用しやすいから継承しよう」となってしまいますがちですが、これは良い継承とは言えません。(間違いでもないですが)
本来の継承は設計ありきの機能です。基本的に継承はあるプログラムを作る際に「ベースとなるクラス」があり「そのクラスから独自機能を持つ派生するクラス」が想定される際に利用します。
補足2 継承関係を考える
継承関係を持たせるかどうかを判別する一つの手段として「is-a関係」が成り立つかどうかで判断することができます。
※is-a関係が成り立たないから絶対に継承NG、成り立つから絶対に継承OKとなるわけではありません(あくまで目安です)
is-a関係とは「A is a B」の関係が成り立つかどうかというもので、直訳すると「AはBである」となります。
例えば、「オートバイ(A)は乗り物(B)である」の関係となり違和感はないと思います。しかし、AとBを入れ替えると「乗り物(A)はオートバイ(B)である」となり文脈がおかしくなります。
言い換えると、「概念Aは概念Bに含まれるかどうか」と考えることができ、Bの方が大きい括りとなります。
A is a Bという関係では、Bはスーパークラス、Aはサブクラスとなります。
また、Bという括りに含まれるAを特化、Aを含んでいる大きい括りであるBを汎化と呼ぶこともあります。
継承 復習問題
- 継承の説明として正しいものを選んでください
- 解答群
- スーパークラスから継承するサブクラスを設定する
- 継承関係があれば、スーパークラスからサブクラスのメソッドを呼び出すことができる
- スーパークラス側でextendsを指定してサブクラスを指定する
- スーパークラス側でclass定義時にfinalを記述すると継承禁止となる
- 継承の説明として正しいものを選んでください
- 解答群
- サブクラス側でclass定義時にfinalを記述するとextndsを利用した継承が不可となる
- 継承で利用できるメンバはメソッドのみである
- サブクラス側でextendsを利用してスーパークラスを指定する
- 直接継承できるクラスは複数可能である
class Apple extends Fruit{
の記述の説明として正しいものを選んでください- 解答群
- Fruitは特化、Appleは汎化の関係である
- Fruitは汎化、Appleは特化の関係である
- Fruitはサブクラスであり、特化である
- Appleはスーパークラスであり、汎化である
- お疲れ様でした。
まとめ
- 継承を利用すると他クラスのメンバ(フィールドやメソッド)をあたかも自分のクラスのメンバのように利用できる
- 継承はextendsキーワードを利用する
- 継承されるクラスをスーパークラス、継承するクラスをサブクラスと呼ぶ
- 継承できるクラスは一つのみに制限されている(単一継承)
- 記述は容易だが、考え方は難しい
- 継承関係は設計にも関わる