はじめに
継承は、1つのクラスを全て引き継いで新しいクラスを作成する方法です。既にあるクラスへ属性や機能を継ぎ足して新しいクラスを作成する方法です。すでにあるクラスに属性や機能を継ぎ足して新しいクラスを作る差分プログラミングを可能にします。この章では継承の仕組みを学習します。
目次
- 継承
- クラスの階層
- protected修飾子
- 参照型の自動型変換
- ダウンキャスト
- instanceof演算子
- 引数における参照型の自動型変換
- 戻り値における参照型の自動型変換
- 章のまとめ
目標
継承を使ったクラスを作成できる様にすること。
継承
継承
は、オブジェクトの設計図であるクラスを再利用
するための技術です。
文字通り、既存のクラス
を引き継いだ上で、さらに機能をつけて加えたり
、あるいは一部の機能を変えたり
して、新しいクラスを作ることができます。
例えば、既に上記のような社員情報を管理するEmployeeクラスが存在していたとします。その後、社員情報に加えて、営業情報も追加した新しいクラスを作成したいと依頼があった場合、一からクラスを新しく定義し直すと効率が悪いです。そんな時に継承
を利用します。
例題(継承)
実際に継承を利用したクラスを作成してみましょう。
今回は、Employeeクラスというクラスを作成します。Employeeクラスは、属性として社員名前と社員IDの情報を保持し、操作として社員情報を表示することもできるクラスです。
項目 | 名前 |
---|---|
プロジェクト | alj_study |
パッケージ | chapter8 |
クラス名 | Employee |
package chapter8; |
Employeeクラスを継承し、営業情報も追加したクラス(SalesEmpクラス)を作成します。
項目 | 名前 |
---|---|
プロジェクト | alj_study |
パッケージ | chapter8 |
クラス名 | SalesEmp |
継承 | Employee |
package chapter8; |
実行するためのクラス(EmpExecクラス)を作成します。
項目 | 名前 |
---|---|
プロジェクト | alj_study |
パッケージ | chapter8 |
クラス名 | EmpExec |
package chapter8; |
コンパイル例:> javac chapter8¥Employee.java
> javac chapter8¥SalesEmp.java
> javac chapter8¥EmpExec.java
実行例:> java chapter8.EmpExec
営業部番号:5000
個人売上高120000000
継承の定義方法
クラスを継承するには、extends
キーワードを使用します。
extendsキーワードの構文は以下です。
SalesEmpクラス
を宣言する際に付与されているextends Employee
が継承を意味します。今回はEmployeeクラス
を継承
するという意味になります。
SalesEmp
クラスはEmployee
クラスに定義されているメンバ変数やメソッドを引き継いでいます。
継承を利用した場合extends
キーワードで指定されているクラス(継承元のクラス)をスーパークラス
といいます。
スーパークラスをを継承して作成する新しいクラスをサブクラス
といいます。
上記の場合、SalesEmp
クラスがスーパークラス
、Employee
クラスがサブクラス
です。
サブクラスのインスタンス化
Java言語の規約では、サブクラスのコンストラクタ
は、最初にスーパークラスのコンストラクタを実行しなければならない
と決められています。
スーパークラスのコンストラクタを呼び出すためにはsuper
キーワードを利用します。
[スーパクラスのコンストラスタをサブクラスから呼び出す場合]super(引数リスト);
今回の場合、スーパークラスであるEmployeeクラス
のコンストラクタが以下のように定義されています。
public Employee(String empName, int empId){ |
サブクラスのコンストラクタ
は、最初にスーパークラスのコンストラクタを実行しなければならない
ので、スーパークラスのコンストラクタの定義に合わせて呼び出しを行わなければなりません
。
今回の場合、サブクラスであるSalesEmp
クラスのコンストラクタ内で、最初にEmployee
クラスのコンストラクタを呼び出す必要があります。そのため、Employee
クラスではコンストラクタを以下のように定義します。
public SalesEmp(String empName, int empId, int salesId, int salesVolume){ |
スーパークラスのコンストラクタが明示的に定義されている場合は、サブクラスのコンストラクタ内で必ず一番最初にスーパークラスのコンストラクタの呼び出しを定義しておかないと、コンパイルエラーになりますので注意して下さい。
スーパークラスのメンバ変数の呼び出し
サブクラス内
でスーパクラスのメンバ変数
を利用するためには、スーパークラスに定義してあるメンバ変数名
をそのまま指定
します。
先ほど作成しました、EmployeeクラスとSalesEmpクラスを以下のように修正してみましょう。
項目 | 名前 |
---|---|
プロジェクト | alj_study |
パッケージ | chapter8 |
クラス名 | Employee |
package chapter8; |
項目 | 名前 |
---|---|
プロジェクト | alj_study |
パッケージ | chapter8 |
クラス名 | SalesEmp |
継承 | Employee |
package chapter8; |
コンパイル例:> javac chapter8¥Employee.java
> javac chapter8¥SalesEmp.java
実行例:> java chapter8.EmpExec
入社日:2019/10/01
誕生日:2000/01/01
また、スーパークラスで定義したメンバ変数と同じ名前の変数
をサブクラスのメンバ変数
として定義
することも可能です。
そうした場合、サブクラス内
でスーパークラスのメンバ変数
を呼び出したい場合は以下のように定義します。
[スーパークラスで定義しているメンバ変数を、サブクラス内で利用したい場合]super.スーパークラスで定義したメンバ変数名
SalesEmpクラスを以下のように修正してみましょう。
項目 | 名前 |
---|---|
プロジェクト | alj_study |
パッケージ | chapter8 |
クラス名 | SalesEmp |
継承 | Employee |
package chapter8; |
コンパイル例:> javac chapter8¥Employee.java
> javac chapter8¥SalesEmp.java
実行例:> java chapter8.EmpExec
誕生日(スーパークラス):2000/01/01
誕生日(サブクラス):1980/01/01
サブクラス内
で定義しているメンバ変数
と、スーパークラス
で定義しているメンバ変数
が同じ名前
の場合は、super
キーワードを指定すると、スーパークラス
で定義しているメンバ変数
を利用することができます。
スーパークラスのメンバメソッドの呼びだし
サブクラス内
でスーパークラス
のメンバメソッド
を呼び出したい場合、スーパクラスのメソッド名
を指定します。
SalesEmpクラスの以下の箇所を修正し、実行してみましょう。
項目 | 名前 |
---|---|
プロジェクト | alj_study |
パッケージ | chapter8 |
クラス名 | SalesEmp |
継承 | Employee |
package chapter8; |
コンパイル例:> javac chapter8¥SalesEmp.java
実行例:> java chapter8.EmpExec
社員名:福山雅治
社員番号1000
継承とis-a関係
is-a関係
とは、「A is a B
」という関係
を表す表現技法のことです。
ある2つのオブジェクトを、Aはサブクラス、Bはスーパークラスとして当てはめた時に「A is a B」という表現が成りたつと、AとBは継承している
と証明することができます。
先ほど作成した、Employee
クラスとSalesEmp
クラスをis-a関係で表現すると以下になります。
SalesEmpクラスはEmployeeクラスの一種です。 |
上記、is-a関係
でSalesEmp
クラスとEmployee
クラスは成立
する為、継承関係である
ことが証明
できます。
継承されないもの
サブクラスへ継承されるものは、オブジェクト
です。オブジェクト
の中に存在しない以下の2つは、継承の対象ではありません。
コンストラクタ
- 静的メンバ(
static
修飾子が付加されたメンバ)
例題(static修飾子が付加されたメンバ)
static
修飾子が付加されたメンバは、以下のようにアクセスも可能ですので注意して下さい。
項目 | 名前 |
---|---|
プロジェクト | alj_study |
パッケージ | chapter8 |
クラス名 | AAAA |
package chapter8; |
項目 | 名前 |
---|---|
プロジェクト | alj_study |
パッケージ | chapter8 |
クラス名 | BBBB |
継承 | AAAA |
package chapter8; |
項目 | 名前 |
---|---|
プロジェクト | alj_study |
パッケージ | chapter8 |
クラス名 | Exec |
package chapter8; |
コンパイル例:> javac chapter8¥AAAA.java
> javac chapter8¥BBBB.java
> javac chapter8¥Exec.java
実行例:> java chapter8.Exec
20
アクセスが制限されたメンバ
static
修飾子が付加された変数はクラス変数
で、インスタンス化しなくても「クラス名.変数名
」で利用ができます。
インスタンス化し、参照型の変数を通してもアクセスもできますが、本来は「クラス名.変数名」でアクセスすることが一般的です。
また、次の修飾子によって、アクセスが制限されたメンバも継承できません。
final
修飾子が付加されたクラス
private
修飾子されたメンバfinal
修飾子は「変更ができない
」ことを表す修飾子です。クラス
に付与すると、継承できないクラス
になります。クラス以外にもメソッド
やメンバ変数
へfinal
修飾子を付与することができます。final
修飾子が付与されたメンバ変数
は、初期設定
した内容を変更できなく
なります。final
修飾子が付与されたメソッドは、オーバーライド
(後述で説明)出来なくなります。
private
修飾子が付与されているメンバ(メンバ変数
やメソッド
)は、クラス内のみ
アクセス可能な修飾子です。
例題(アクセスが制限されたメンバ)
先ほど作成した
項目 | 名前 |
---|---|
プロジェクト | alj_study |
パッケージ | chapter8 |
クラス名 | AAAA |
package chapter8; |
項目 | 名前 |
---|---|
プロジェクト | alj_study |
パッケージ | chapter8 |
クラス名 | BBBB |
継承 | AAAA |
package chapter8; |
コンパイル例:> javac chapter8¥AAAA.java
> javac chapter8¥BBBB.java
chapter8/BBBB.java:7: エラー: final AAAAからは継承できません
public class BBBB extends AAAA {
^
エラー1個
クラスの階層
継承関係
を図示
したものを、継承ツリー
といいます。Java言語では、Object
クラスを起源として階層構造で継承しています。
すべてのクラスはObject
クラスのサブクラス
です。クラス宣言
に何も継承されていない場合
は、コンパイラ
が「extends Object
」を自動的に挿入
します。
単一継承
1つ
のクラスを継承
することを単一継承
といいます。逆に2つ
以上のクラスを同時に継承することを多重継承
といいます。Java言語
は単一継承
です。多重継承
を行いたい場合は、インターフェース
を利用します。
Objectクラスのメソッド
Objectクラスには、いくつかの基本的なメソッドが定義されています。これらは全てのオブジェクトに継承されるメソッドです。
Objectクラスのメソッド
メソッド名 | 機能 |
---|---|
toString() | オブジェクトの文字列表現を返す |
clone() | このオブジェクトのコピーを作成して返す |
equals(Object obj) | このオブジェクトと他のオブジェクトが等しいかどうか示す |
hashCode() | オブジェクトのハッシュコードを返す |
finalize() | ガベージコレクション用 |
getClass() | このオブジェクトの実行時クラスを返す |
notify() | マルチスレッド処理用。待機中のスレッドを一つ再開する。 |
notifyAll() | マルチスレッド処理用。待機中のスレッドを全て再開する。 |
wait() | マルチスレッド処理用。スレッドを待機させる。 |
wait(timeout) | マルチスレッド処理用。制限時間付きでスレッドを待機させる。 |
wait(timeout , nanos) | マルチスレッド処理用。制限時間付きでスレッドを待機させる。 |
Objectクラスは全てのクラスに必要の為、明示的に継承しなくてもコンパイル時に自動的に継承される仕組みになっています。つまり全てのクラスは、暗黙
のうちにObjectクラスを継承したクラス
なのです。
継承によるデフォルトコンストラクタ
サブクラスのコンストラクタでは、最初にスーパークラスのコンストラクタを実行して、スーパークラスのオブジェクトを初期化する必要がありました。
サブクラスのコンストラクタ内
に、スーパクラスのコンストラクタの呼び出し
が明示的に定義されていない
場合、スーパークラスのコンストラクタを呼び出すためのデフォルトコンストラクタ(super();)
が、コンパイラによって自動的に挿入
されます。
例題(継承によるデフォルトコンストラクタ)
それでは、以下のConstructorChainクラスを作成し、実行結果を確認してみてください。
項目 | 名前 | 備考 |
---|---|---|
プロジェクト | alj_study | |
パッケージ | chapter8 | |
クラス名 | Data | |
クラス名 | DataA | Dataクラスを継承 |
クラス名 | DataB | DataAクラスを継承 |
クラス名 | ConstructorChainExec | |
ファイル名 | ConstructorChainExec.java |
package chapter8; |
コンパイル例:> javac chapter8¥ConstructorChainExec.java
実行例:> java chapter8.ConstructorChainExec
Data
DataA
DataB
;
上記の場合、サブクラスには、スーパークラスのコンストラクタの呼び出しが明示的にされていない為、コンパイルする際に暗黙の内にsuper()が挿入されます。
その為、Testクラスで「new DataB()」を実行すると、まずDataAクラスのコンストラクタが起動されます。
/** |
しかしながら、DataAクラスはDataクラスを継承しているので、Dataクラスのコンストラクタが起動します。
/** |
Dataクラスではextendsキーワードが指定されていないので、明示的には何も継承されていませんが、明示的に継承されていない場合には、暗黙的にObjectクラスのコンストラクタが継承されます。その為、最初に実行されるコンストラクタはObjectクラスのコンストラクタが実行されます。
/**
* Dataのコンストラクタ
*/
public Data(){
super(); //コンパイラによって暗黙の内に挿入される(Objectクラスのコンストラクタの呼び出し)
System.out.println("Data");
}
その為、Dataクラスのコンストラクタ、DataAクラスのコンストラクタ、DataBクラスのコンストラクタが実行され、以下のようにコンソールへ出力されます。
Data |
protected修飾子
今までpublic、private、デフォルトアクセスについて解説してきましたが、もう一つアクセスするための修飾子があります。それはprotected
修飾子です。protected
修飾子は、継承
と関わりがある修飾子です。
protected修飾子の役割
protected
修飾子の役割は以下の2つです。
サブクラス
からアクセスできる
ようにする(別のパッケージ
からでも、継承
していればアクセスすることができる
)同じパッケージ
のクラスからアクセスできる
ようにする(デフォルト修飾子と同じ役割
)
protected
修飾子はデフォルトアクセス
修飾子に、サブクラスの範囲
が追加
した修飾子です。
protected
修飾子は、サブクラスを設計しやすくするため
に設けられた修飾子で、デフォルトアクセス修飾子よりもカプセル化
をさらに弱める効果
があります。
例題(別パッケージのクラスを継承している場合)
項目 | 名前 |
---|---|
プロジェクト | alj_study |
パッケージ | chapter8A |
クラス名 | C |
package chapter8A; |
項目 | 名前 |
---|---|
プロジェクト | alj_study |
パッケージ | chapter8B |
クラス名 | D |
継承 | chapter8A.C |
package chapter8B; |
項目 | 名前 |
---|---|
プロジェクト | alj_study |
パッケージ | chapter8B |
クラス名 | ExecCD |
package chapter8B; |
コンパイル例:> javac chapter8A¥C.java
> javac chapter8B¥D.java
> javac chapter8B¥ExecCD.java
実行例:> java chapter8B.ExecCD
1.0
Cクラスです。
protected修飾子が付加されているメンバは、別のパッケージからでも、継承していればアクセスすることができます。
例題(同じパッケージのクラスを継承している場合)
新たに以下のクラスを作成し、確認してみましょう。
項目 | 名前 |
---|---|
プロジェクト | alj_study |
パッケージ | chapter8 |
クラス名 | A |
package chapter8; |
項目 | 名前 | 備考 |
---|---|---|
プロジェクト | alj_study | |
パッケージ | chapter8 | |
クラス名 | B | |
継承 | chapter8.A |
package chapter8; |
項目 | 名前 |
---|---|
プロジェクト | alj_study |
パッケージ | chapter8 |
クラス名 | ExecAB |
package chapter8; |
コンパイル例:> javac chapter8¥A.java
> javac chapter8¥B.java
> javac chapter8¥ExecAB.java
実行例:> java chapter8.ExecAB
1.0
Aクラスです
protected修飾子を付加したメンバは、同じパッケージからもアクセスできます。
(デフォルト修飾子と同じ役割を果たします。)
アクセス修飾子のまとめ
修飾子 | 意味 |
---|---|
public | パッケージを問わず、どのクラスからでも直接アクセスが可能 |
protected | 同一パッケージの外部クラス、またはしたサブクラスからであればアクセスが可能 |
修飾子なし | 同一パッケージの外部クラスからであればアクセス可能 |
private | 外部クラスからのアクセスはできず、同一クラス内のメソッドからのみアクセス可能 |
アクセス修飾子の適用場所
アクセス修飾子はクラス、コンストラクタ、クラスメンバ(メンバ変数、メソッド)、インターフェースにつけることができます。
(ただし、classにはprivateとprotectedは適用できません。またインターフェースはpublic以外は適用できません。)
アクセス修飾子 | class | コンストラクタ | メンバ変数 | メソッド | インターフェース |
---|---|---|---|---|---|
public | ● | ● | ● | ● | ● |
protected | × | ● | ● | ● | × |
修飾子なし | ● | ● | ● | ● | × |
private | × | ● | ● | ● | × |
参照型の自動型変換
基本データ型では自動型変換
やキャスト
することで、型は違っていても代入することができる仕組みがありました。オブジェクトを扱う参照型
も同様な仕組みがあります。
ここでは参照型
の自動型変換
について学習します。
参照型の自動型変換の条件
参照型
の自動型変換
をおこなう場合、次のような条件があります。
- 同じ継承ツリーの中に限る
- 継承ツリーの矢印の向きにのみ可能
例題(参照型の自動型変換)
以下、Company←Employee←SalesEmpという同じ継承ツリーにあるクラスで、参照をスーパークラス(親クラス)の変数に代入して使用できるかどうかを確認している例題です。
以下のクラスを作成し、実行結果を確認してみましょう。
項目 | 名前 | 備考 |
---|---|---|
プロジェクト | alj_study | |
パッケージ | chapter8 | |
クラス名 | Company |
package chapter8; |
項目 | 名前 | 備考 |
---|---|---|
プロジェクト | alj_study | |
パッケージ | chapter8 | |
クラス名 | Employee | |
継承 | Company |
package chapter8; |
項目 | 名前 | 備考 |
---|---|---|
プロジェクト | alj_study | |
パッケージ | chapter8 | |
クラス名 | SalesEmp | |
継承 | Employee |
package chapter8; |
項目 | 名前 | 備考 |
---|---|---|
プロジェクト | alj_study | |
パッケージ | chapter8 | |
クラス名 | Exec |
package chapter8; |
コンパイル例:> javac chapter¥SalesEmp.java
> javac chapter8¥Employee.java
> javac chapter8¥SalesEmp.java
> javac chapter8¥Exec.java
実行例:> java chapter8.Exec
-----sの変数範囲でアクセス------
SalesEmp class
Employee class
Company class
-----eの変数範囲でアクセス------
Employee class
Company class
-----cの変数範囲でアクセス------
Company class
上記、SalesEmpクラスのオブジェクトを一つだけ作成し、その参照をeとcといった、スーパクラスの変数へ代入しています。
eとcはそれぞれのクラスの該当部分を参照し、empDisp()メソッドやcomDisp()メソッドを実行できていることがわかります。
尚、Execクラスへ以下を追記すると、コンパイルエラー
になります。
e.salesDisp(); |
参照範囲外
のメンバ
へアクセスはできない
仕組みになってます。
ダウンキャスト
通常、継承ツリーの矢印と逆向き
に代入
しようとすると、コンパイルエラー
が発生します。
コンパイルエラーを発生させずに代入するには、ダウンキャスト
をおこないます。
ダウンキャストの定義方法
ダウンキャストするためには、括弧を使用して明示的に型を指定します。
サブクラス型 変数名 = (サブクラス型)スーパークラス型の参照 |
例題(ダウンキャスト)
先ほど作成したExecクラスを以下のように修正してみましょう。
項目 | 名前 | 備考 |
---|---|---|
プロジェクト | alj_study | |
パッケージ | chapter8 | |
クラス名 | Exec |
package chapter8; |
コンパイル例:> javac chapter8¥Exec.java
chapter8/Exec.java:20: エラー: 不適合な型: EmployeeをSalesEmpに変換できません:
SalesEmp s1 = e; //Employee型の変数eを、SalesEmp型の変数s1へ代入するとコンパイルエラーが発生する
^
エラー1個
上記、スーパークラス(Employee)をサブクラス(SalesEmp)へ代入しようとしていて、コンパイルエラーが発生しています。
Execクラスを以下のように修正してください。
項目 | 名前 | 備考 |
---|---|---|
プロジェクト | alj_study | |
パッケージ | chapter8 | |
クラス名 | Exec |
package chapter8; |
コンパイル例:> javac chapter8¥Exec.java
実行例:> java chapter8.Exec
-----s1の変数範囲でアクセス------
SalesEmp class
Employee class
Company class
ダウンキャストをおこなうと、コンパイルエラーが発生せず実行する事ができます。
元々メモリ上に、SalesEmpオブジェクトが生成されているので、ダウンキャストしても参照範囲が一致する為、問題なく実行することができます。
ダウンキャストしても、メモリ上に作成されているオブジェクトがスーパークラスの場合は、実行時に例外が発生します。
Execクラスを以下のように修正してください。
項目 | 名前 | 備考 |
---|---|---|
プロジェクト | alj_study | |
パッケージ | chapter8 | |
クラス名 | Exec |
package chapter8; |
コンパイル例:> javac chapter8¥Exec.java
実行例:> java chapter8.Exec
Exception in thread "main" java.lang.ClassCastException: class chapter8.Employee cannot be cast to class chapter8.SalesEmp (chapter8.Employee and chapter8.SalesEmp are in unnamed module of loader 'app')
at chapter8.Exec.main(Exec.java:19)
メモリー上に作成されているオブジェクトはスーパークラスであるEmployeeオブジェクトです。しかしながら
s1の変数にはサブクラス型の参照が代入されています。オブジェクトのメンバを超えて参照することができる為、実行時例外(java.lang.ClassCastException)
が発生します。
※ダウンキャストを行う際は、注意して定義してください。
instanceof演算子
参照型の変数が、どんな型のオブジェクトか確認したい場合、instanceof演算子を利用します。
instanceof演算子は以下のように利用します。変数 instanceof 型
変数
が参照
しているオブジェクトの型
が、同じ
かあるいはそのサブクラス
ならtrue
を返します。
例題(instanceof演算子)
以下クラスを作成し、実行結果を確認してみましょう。
項目 | 名前 | 備考 |
---|---|---|
プロジェクト | alj_study | |
パッケージ | chapter8 | |
クラス名 | Food |
package chapter8; |
項目 | 名前 | 備考 |
---|---|---|
プロジェクト | alj_study | |
パッケージ | chapter8 | |
クラス名 | Fruit | |
継承 | Food |
package chapter8; |
項目 | 名前 | 備考 |
---|---|---|
プロジェクト | alj_study | |
パッケージ | chapter8 | |
クラス名 | Apple | |
継承 | Fruit |
package chapter8; |
項目 | 名前 | 備考 |
---|---|---|
プロジェクト | alj_study | |
パッケージ | chapter8 | |
クラス名 | AppleObjExec |
package chapter8; |
コンパイル例:> javac chapter8¥Food.java
> javac chapter8¥Fruit.java
> javac chapter8¥Apple.java
> javac chapter8¥AppleObjExec.java
実行例:> java chapter8.AppleObjExec
ダウンキャスト実行します
is-a関係とinstanceofの実行結果
is-a関係
とは、継承
を表す表現技法
です。
X is a Z |
- XはZ型である
- XはZを継承している
- XはZの派生クラスである
- XはZのサブクラスである
instanceof演算子
の結果がtrue
の場合は、is-a関係が成立
します。
先ほど作成しましたAppleObjExecクラスを修正し、実行結果を確認してみましょう。
項目 | 名前 | 備考 |
---|---|---|
プロジェクト | alj_study | |
パッケージ | chapter8 | |
クラス名 | AppleObjExec |
package chapter8; |
コンパイル例:> javac chapter8¥Food.java
> javac chapter8¥Fruit.java
> javac chapter8¥Apple.java
> javac chapter8¥AppleObjExec.java
実行例:> java chapter8.AppleObjExec
-----Appleクラス-----
true
true
true
-----Fruitクラス-----
true
true
false
-----Foodクラス-----
true
false
false
上記の場合、変数appleはFoodクラスのサブクラスのサブクラスであり、Fruitクラスのサブクラス、Appleクラスである為、
全てtrueを返します。
変数fruitはFoodクラスのサブクラスであり、Fruitクラスである為、trueを返します。Appleクラスとの関係はスーパークラスにあたる為、falseを返します。
変数foodはFoodクラスの為、trueを返します。
引数における参照型の自動型変換
メソッドの引数(仮引数
)が参照型の場合、メソッドを実行するときの引数(実引数
)へ、サブクラスの引数
を指定
することができます。
例題(引数における参照型の自動型変換)
新たに、CookクラスとCookObjExecクラスを作成し、実行結果を確認してください。
項目 | 名前 | 備考 |
---|---|---|
プロジェクト | alj_study | |
パッケージ | chapter8 | |
クラス名 | Cook |
package chapter8; |
項目 | 名前 | 備考 |
---|---|---|
プロジェクト | alj_study | |
パッケージ | chapter8 | |
クラス名 | CookObjExec |
package chapter8; |
コンパイル例:> javac chapter8¥Cook.java
> javac chapter8¥CookObjExec.java
実行例:> java chapter8.CookObjExec
---Appleオブジェクトを実引数に指定---
Food class
---Fruitオブジェクトを実引数に指定---
Food class
Cookクラスのbakeメソッドは引数(仮引数)にFood型の参照が定義されています。
「cook.bake(apple);」では、Food型のサブクラスであるApple型の変数appleを引数(実引数)へ指定しています。
実行すると、サブクラスであるappleが自動型変換
されて、スーパークラス型のfoodへ代入されます。
そしてFoodクラスのメソッドfoodDisp()メソッドが実行され、「Food class」とコンソール上に表示されます。
※「cook.bake(fruit);」も同様に自動変換され、「Food class」とコンソール上に表示されます。
戻り値における参照型の自動型変換
メソッドの戻り値が参照型の場合、戻り値型に指定されている型のサブクラスを返すことができます。
例題(戻り値における参照型の自動型変換)
新たに以下を作成し、確認してください。
項目 | 名前 | 備考 |
---|---|---|
プロジェクト | alj_study | |
パッケージ | chapter8 | |
クラス名 | FoodCheck |
package chapter8; |
項目 | 名前 | 備考 |
---|---|---|
プロジェクト | alj_study | |
パッケージ | chapter8 | |
クラス名 | FoodCheckExec |
package chapter8; |
FoodCheckクラスのget()メソッドは引数にint型の1を指定するとFood型のサブクラスのAppleオブジェクトを返します。
1以外を指定すると、Food型のサブクラスであるFruitオブジェクトを返します。上記の場合、「fch.get(1);」を指定しており、実行するとAppleオブジェクトが返されます。
まとめ
以下の要点をしっかり理解してから次の章へ進んでください。
継承
継承
は、オブジェクトの設計図であるクラス
を再利用
するための技術である継承
は、既存のクラス
を引き継いだ
上で、さらに機能をつけて加えたり
、あるいは一部の機能を変えたり
して、新しいクラス
を作ることができる- クラスを継承するには、
extends
キーワードを使用する extends
キーワードの後
に、継承したいクラス名
を指定するextends
キーワードの後に指定したクラス(継承元)
をスーパークラス(親クラス)
という継承
によって新たに作成するクラス(継承先)
をサブクラス(子クラス)
という- サブクラス(子クラス)の定義には、
スーパクラスに追加したい定義
を追記
する - extendsキーワードの後に指定できるクラスは1つのみである(
単一継承
) - サブクラス(子クラス)は、スーパークラス(親クラス)のメンバ(フィールド)やメソッドを自分のものとして扱えることができる
super
キーワードを利用すると、スーパークラス
のメンバ(変数やメソッド)
やコンストラクタ
を呼び出す
ことができる- サブクラスをインスタンス化すると、スーパークラス、サブクラスの順に生成される
- サブクラスのコンストラクタが呼ばれると、スーパクラスのコンストラクタが呼ばれる
- サブクラスのコンストラクタは、最初にスーパークラスのコンストラクタを実行しなければならない
- スーパークラスのコンストラクタの定義に合わせて呼び出しを行わなければならない
- スーパークラスのコンストラクタを呼び出す場合は「super(引数リスト);」と記載する
- サブクラス内でスーパークラスのメンバ変数を利用したい場合、スーパークラスのメンバ変数名がそのまま利用できる。
- スーパークラスのメンバ変数と同じ名前のメンバ変数をサブクラス内に定義することもできる
- 呼び出す場合には、superキーワードを利用する
- サブクラス内でスーパークラスのメンバメソッドを利用したい場合、スーパークラスのメンバメソッド名で利用できる。
- is-a関係とは、「A is a B」という関係を表す表現技法のこと
- Aがサブクラス、Bがスーパークラスとして当てはめた時、「A is a B」の文章が成り立つと継承関係であることが証明できる
- オブジェクト内に存在しないコンストラクタや静的メンバ(static修飾子)は継承されない
- final修飾子やprivate修飾子が付加されたメンバは継承されない
クラスの階層
- 継承関係を図示したものを、継承ツリーといい、Objectクラスを起源として継承している
- すべてのクラスはObjectクラスのサブクラスである
- クラス宣言に何も継承されていない場合は、コンパイラが「extends Object」を自動的に挿入する
- 1つのクラスを継承することを単一継承という
- 2つ以上のクラスを同時に継承することを多重継承という
- Javaは単一継承である
- 多重継承を行いたい場合は、インターフェースを利用する
- オブジェクトクラスには、いくつかの基本的なメソッドが定義されている
- Objectクラスで定義されているメソッドは、全てのオブジェクトへ継承されるメソッドである
- サブクラスのコンストラクタにスーパークラスのコンストラクタの呼び出しが定義されていない場合、コンパイラは、スーパークラスを呼び出すためのデフォルトコンストラクタ(super();)を暗黙の内に挿入する
- デフォルトコンストラクタのアクセス修飾子はクラスのアクセス修飾子と同じになる
- 継承では、サクブラスにおけるコンストラクタの呼び出しが次々とスーパークラスに波及する
- extendsを明示的に記述していないクラスは、Objectクラスを継承している
protected修飾子
- protected修飾子は、サブクラスを設計しやすくするために設けられた修飾子
- 同じパッケージのクラスからアクセスできるようにする(デフォルト修飾子と同じ役割)
- サブクラスからアクセスできるようにする(別のパッケージからでも、継承していればアクセスすることができる)
アクセス修飾子 | class | コンストラクタ | メンバ変数 | メソッド | インターフェース |
---|---|---|---|---|---|
public | ● | ● | ● | ● | ● |
protected | × | ● | ● | ● | × |
修飾子なし | ● | ● | ● | ● | × |
private | × | ● | ● | ● | × |
参照型の自動型変換
- 基本データ型と同じように参照型でも
自動型変換
される仕組みがある - 参照型の自動型変換対象は、
同じ継承ツリー内
に限られる サブクラスのオブジェクトの参照
は、スーパクラスの変数
へ代入できる(自動型変換
可能)参照型の変数
が、オブジェクトへアクセスできる範囲(参照範囲
)は、変数の型
によって決まる- 自動型変換により代入されると、
スーパクラス
で定義されているメンバ名
の範囲で、サブクラス
のオブジェクト
へアクセス(参照
)することができる - 自動型変換により代入されると、スーパクラスで定義されているメンバ名の
範囲外
はアクセスすることができない(アクセス(参照)しようとするとコンパイルエラー
が発生する)
ダウンキャスト
- 継承ツリーの矢印と
逆向き
へ代入しようとすると、コンパイルエラー
が発生する - 継承ツリーの矢印と
逆向き
へプログラマが明示的
に代入
することをダウンキャスト
という ダウンキャスト
は、同じ継承ツリー内
であれば、どんなクラスも変換可能- オブジェクトが元々サブクラスのオブジェクトであれば問題ないが、そうでない場合は
実行時例外
が発生
する可能性
がある ダウンキャスト
するとコンパイルエラー
にならない
- 元からサブクラス型のオブジェクトを
参照している
と、実行時例外は発生しない - 元からサブクラス型のオブジェクトを
参照していない
と実行時例外(java.lang.ClassCastException)
が発生し、停止する
instanceof演算子
instanceof演算子
は、変数が参照しているオブジェクトの型
を調べる
演算子である- 変数が参照しているオブジェクトの型が、
同じ
かあるいはそのサブクラス
ならtrue
を返す is-a関係
とは、継承を表す
表現技法であるinstanceof演算子
の結果がtrue
の場合、is-a関係が成立
する
引数における参照型の自動型変換
- メソッドの
引数(仮引数)
が参照型
の場合、メソッドを実行するときの引数(実引数
)へサブクラス
の引数
を指定することができる
戻り値における参照型の自動型変換
- メソッドの
戻り値
が参照型
の場合、戻り値型に指定されている型のサブクラス
を返す
ことができる