クラスの継承とObjectクラスの関係

1.5 クラスの継承とObjectクラスの関係

 本章の学習でクラスを拡張する様々な仕組みについてみてきました。クラスの拡張と継承についての関係をさらに詳しくみていきましょう。

1.5.1 クラスの階層について

 これまでのプログラム中で普通に行ってきましたが、複数のサブクラスに1つの同じスーパークラスを継承することもできます。この時のクラス同士の関係は以下の図1.5.1のようになります。

図 1.5.1: 複数のサブクラスで1つのスーパークラスを継承

 また、サブクラスを継承したさらに新しいサブクラスを作成することもできます。新しいサブクラスから見ると最初のサブクラスがスーパークラスになります。新しいサブクラスには、サブクラスのメンバとして大元のスーパークラスのメンバも継承されることになります。関係は以下の図1.5.2になります。

図 1.5.2: サブクラスを拡張

 なお、複数のサブクラスに1つの同じスーパークラスを継承することはできますが、1つのサブクラスに複数のスーパークラスを継承することはできないようになっています。

図 1.5.3: 多重拡張(継承)の禁止

1.5.2 Objectクラスの仕組みを知る

 本章でクラス拡張の仕組みを行う以前では、スーパークラスを特に指定せずにクラスを作成してきました。Javaではクラス作成時にスーパークラスを指定しなかった場合、そのクラスは、Objectクラスをスーパークラスにもつという決まりになっています。これまで目にしてきた、以下の図1.5.4ようなクラスは実はObjectクラスをスーパークラスとしたサブクラスになっていた訳です。

図 1.5.4: クラス作成時のObjectクラスの自動拡張(継承)

 この仕組みによりスーパークラスの指定が無いクラスは全てObjectクラスを継承したクラスとなり、そのクラスを継承したクラスも自動的にメンバ全てを継承することになります。結果Javaの全クラスは必ずObjectクラスのメンバを継承している仕組みになっています。

図 1.5.5: クラスはObjectクラスのメンバを継承

ポイント
  • スーパークラスを指定しないクラスは、自動的にObjectクラスのサブクラスになり、結果としてスーパークラスを継承するとObjectクラスのメンバも継承したことになる

 次項よりObjectクラスとは一体どのようなクラスなのかみていきましょう。

1.5.3 Objectクラスについて

 Objectクラスを簡単に表すと「全てのJavaクラスを形作るためのクラス」となり、全てのクラスが継承しなければいけないクラスとなります。よってスーパークラスの指定が無い場合は、暗黙的にObjectクラスを継承する仕組みになっています。
 ではObjectクラスにはどのようなメンバをもつクラスなのか、以下の表1.5-1を見て下さい。

表 1.5.1

 それほどメンバも多くなく重要な機能を提供するメソッドもありません。あくまでベースとなる基本クラスのため最低限のメンバが用意されているのです。
 では次の項より実際にObjectクラスのメンバをいくつか使ったプログラムを紹介していきいます。

1.5.4 ObjectクラスのtoStringメソッドを利用したプログラム

 ObjectクラスがもつtoStringメソッドを利用して、オブジェクトの文字表現を画面に出力します。さらにオーバーライド機能を利用して任意のオブジェクト文字表現を表示できるようにします。

toStringメソッドの仕組み
 toStringメソッドは「オブジェクトの文字列表現を返す」と定義されています。オブジェクトを指し示すクラス変数を標準出力処理する時に、このメソッドが呼び出されることになっています。または直接toStringメソッドを呼び出しても結果は同じになります。

凡例:オブジェクトの文字表現を出力する
 デフォルトのオブジェクトの文字表現はパッケージ名.クラス名@数値が表示されます。

ソースコード

① ソース・フォルダー      :myproj_basic/src
② パッケージ          :jp.co.f1.basic.ch11
③ 名前             :Computer5

➢ Computer5.java ※ObjectクラスのtoStringメソッドをオーバーライドしていない従来のクラス
package jp.co.f1.basic.ch11;

public class Computer5 {
	private String os;
	private int memory;

	public Computer5(){
		this.os = null;
		this.memory = 0;
		System.out.println("パソコンを作成しました。");
	}
	public void setOsMemory(String os,int memory){
		this.os = os;
		this.memory = memory;
		System.out.println("OSを「" + os + "」にメモリを「" + memory + "MByte」に変更しました。");
	}
	public void show(){
		System.out.println("パソコンのOSは「" + os + "」です。");
		System.out.println("メモリサイズは「" + memory + "MByte」です。");
	}
}

① ソース・フォルダー      :myproj_basic/src
② パッケージ          :jp.co.f1.basic.ch11
③ 名前             :Computer6

➢ Computer6.java ※ObjectクラスのtoStringメソッドをオーバーライドしているクラス
package jp.co.f1.basic.ch11;
public class Computer6 {
	private String os;
	private int memory;

	public Computer6(){
		this.os = null;
		this.memory = 0;
		System.out.println("パソコンを作成しました。");
	}
	public void setOsMemory(String os,int memory){
		this.os = os;
		this.memory = memory;
		System.out.println("OSを「" + os + "」にメモリを「" + memory + "MByte」に変更しました。");
	}
	public void show(){
		System.out.println("パソコンのOSは「" + os + "」です。");
		System.out.println("メモリサイズは「" + memory + "MByte」です。");
	}
	//オーバーライドしたtoStringメソッド
	public String toString(){
		String str = "OS:" + this.os + " " + "Memory:" + this.memory;
		return str;
	}
}

① ソース・フォルダー      :myproj_basic/src
② パッケージ          :jp.co.f1.basic.ch11
③ 名前             :UseToStringMethod
④ 作成するメソッド・スタブの選択:public static void main(String[] args) にチェックを入れる

➢ UseToStringMethod.java ※mainメソッドを持つ実行クラス
package jp.co.f1.basic.ch11;

public class UseToStringMethod {
	public static void main(String[] args) {
		//Computer5クラスのオブジェクト生成
		Computer5 com1 = new Computer5();
		System.out.println(com1);

		//Computer6クラスのオブジェクト生成
		Computer6 com2 = new Computer6();
		com2.setOsMemory("WindowVista", 2048);
		System.out.println(com2);
	}
}

実行結果

解説

 Computer5クラスは従来の方法でクラス定義を行っています。
 もう1つのComputer6クラスはObjectクラスのメンバであるtoStringクラスをオーバーライドしています。それがComputer6.javaの22~25行目の部分になり、パソコン情報を戻り値に設定するようにしています。
   22:public String toString(){
   23: String str = "OS:" + this.os + " " + "Memory:" + this.memory;
   24: return str;
   25:}

 UseToStringMethodクラスの6行目で、toStringメソッドのオーバーライドを行っていないComputer5クラスのオブジェクト生成を行っています。そして7行目でクラス変数com1の標準出力処理を行っています。
   6:Computer5 com1 = new Computer5();
   7:System.out.println(com1);

 10行目ではtoStringメソッドをオーバーライドしているComputer6クラスのオブジェクト生成を行っています。11行目でsetOsMemoryメソッドを利用して情報の設定、12行目でクラス変数com2の標準出力処理を行っています。
   10:Computer6 com2 = new Computer6();
   11:com2.setOsMemory("WindowsVista", 2048);
   12:System.out.println(com2);

 実行結果を見ても分かるように、オーバーライドしていないComputer5クラスのオブジェクト文字列表現は「パッケージ名.クラス名@数値」となり、オーバーライドしているComputer6クラスは「パソコン情報」が正しく表示されているのが確認できます。

 今回のプログラムのように、クラス変数の標準出力(toStringメソッドを呼び出し)を行うとオブジェクトの文字列表現した情報を表示することができます。実際出力されるものは余り意味の無い文字列表現のため、頻繁に出力処理を行う場合にはtoStringメソッドをオーバーライドして任意の値を出すようにしておけば大変便利です。

ポイント
  • スーパークラスと同じ定義のメソッドを持つサブクラスから、そのメソッドを呼び出すとサブクラスのメソッドが呼び出される。

1.4.2 スーパークラスとサブクラスでsuperとthisを同時に使用するプログラム

 サブクラスのオーバーライドしたメソッドから、スーパークラスのメソッドを呼び出せることを確認します。

ソースコード

➢ Computer2.java ※スーパークラス

 1.3.7で作成したクラスを利用します。

① ソース・フォルダー      :myproj_basic/src
② パッケージ          :jp.co.f1.basic.ch11
③ 名前             :NotePc6

➢ NotePc6.java ※サブクラス
package jp.co.f1.basic.ch11;

//Computer2クラスを継承
public class NotePc6 extends Computer2 {
	private String useType; //用途タイプ

	//コンストラクタ(引数なし)
	public NotePc6() {
		this.useType = null;
 		System.out.println("ノートパソコンを作成しました。");
 	}
 	//コンストラクタ(引数あり)
 	public NotePc6(String os , int memory , String useType){
 		//スーパークラスのコンストラクタを呼び出す
 		super(os,memory);
 		this.useType = useType;
 		System.out.println("タイプは" + this.useType + "用のノートパソコンを作成しました。");
 	}
 	public void setUseType(String useType){
 		this.useType = useType;
 		System.out.println("タイプは" + this.useType + "用にしました。");
 	}
 	//スーパークラスと同じメソッドを定義
 	public void show(){
 		super.show();
 		System.out.println("タイプは" + this.useType + "用です。");
 	}
 }

解説

 このNotePc6クラスはNotePc5クラスの内容から、show()メソッドの処理内容のみが変更になっているクラスです。その変更部分について説明します。
 25行目でスーパークラスのshow()メソッドを呼び出しています。「super」キーワードをつけることで、サブクラス内のオーバーライドしたメソッド内からでも、スーパークラスのメソッドが呼び出すことができます。
 今回のプログラムと違ってオーバーライドされたメソッドが無ければ「super」キーワードは省略可能です。
   24:public void show(){
   25: super.show();
   26: System.out.println("タイプは" + this.useType + "用です。");
   27:}

① ソース・フォルダー      :myproj_basic/src
② パッケージ          :jp.co.f1.basic.ch11
③ 名前             :UseSuperClassMethod
④ 作成するメソッド・スタブの選択:public static void main(String[] args) にチェックを入れる

➢ UseSuperClassMethod.java ※mainメソッドを持つ実行クラス
package jp.co.f1.basic.ch11;

public class UseSuperClassMethod {
	public static void main(String[] args) {
		//サブクラスのオブジェクトを生成
		NotePc6 npc = new NotePc6();

		//スーパークラスのメンバを呼び出す
		npc.setOsMemory("Windows7", 3072);

		//サブクラスのメンバを呼び出す
		npc.setUseType("モバイル");

		//オーバーライドされているメソッドを呼び出す
		npc.show();
	}
}

実行結果

解説

 6行目でNotePc6クラスのオブジェクトを生成しています。扱う変数も同じNotePc6クラスにしています。
   6:NotePc6 npc = new NotePc6();

 9行目でスーパークラスのsetOsMemory()メソッドを利用してOSとmemory情報の設定を行っています。
   9:npc.setOsMemory("Windows7", 3072);

 12行目ではサブクラスのsetUseType()メソッドを利用して用途タイプ情報の設定を行っています。
   12:npc.setUseType("モバイル");

 15行目でオーバーライドしたshow()メソッドを呼び出して、各情報を画面に表示しています。このオーバーライドしたサブクラスのshow()メソッドから、スーパークラスのshow()メソッドが呼び出されています。
   15:npc.show();

 実行結果からも確認できるように、スーパークラスのshow()メソッドとサブクラスのshow()メソッドでの処理が正しく行われています。

ポイント
  • オーバーライドしたサブクラスのメソッド内から、スーパークラスのメソッドを呼び出すことができる。その際はsuperキーワードを使う必要がある。

 繰り返し説明になってしまいますが、今回のようにオーバーライドしたメソッド内からsuperキーワードを利用すればスーパークラスの同じメソッドも利用することができます。部分的な処理をつけ加えるだけのオーバーライドを行いたい場合には、スーパークラスの処理をそのまま利用すればサブクラス内の処理記述を楽に行えるようになります。既にある処理を再利用するという考え方は、プログラムの基本ですので実践できるようにしましょう。

コラム

 サブクラス内ではスーパークラスと同一名のメンバを使うケースも出てきます。
 メソッドの場合はオーバーライドされ呼び出しの優先が変わりますが、フィールド変数にはオーバーライド機能はない為、同一名でもクラス毎の値を持つことになります。
 スーパークラスのメンバは「super」、サブクラスのメンバは「this」をつけてアクセスすれば区別することが可能になります。

クラスの拡張とfinal修飾子の関係性について

 1.4節では、オーバーライド機能について学習してきました。しかし、クラスによってはオーバーライドされては困るメソッドがあるかもしれません。このような場合のクラスにはfinal修飾子を利用すればオーバーライドを禁止することができます。さらにクラス拡張自体させたくない場合も同様に禁止することができます。

final修飾子について
 finalは上書きされないことを意味します。使う場面に応じて様々な処理を禁止することができます。

final修飾子の特徴
 1)メソッドにfinalをつけると、サブクラスからのオーバーライドを禁止できる。
 2)クラスにfinalをつけると、クラスの継承を禁止できる。
 3)フィールド変数にfinalをつけると、値の変更を禁止できる。

 finalを用いた基本構文を以下に示します。

 凡例を見ても分かるように、オーバーライドとクラス拡張を禁止するのはfinalを戻り値の型とclassの前に記述すればいいだけですので大変簡単です。クラスの設計に応じてクラスの拡張や、メソッドのオーバーライドを制御しましょう。
 次項では、クラス、メソッド、フィールド変数にfinalをつけてコンパイルエラーになることを、実際にのエラーが発生している画像を使って確認していきます。

1.4.3 final修飾子を利用して機能を制御する

 スーパークラスにfinal修飾子を用いて、サブクラスからオーバーライドやクラスの拡張ができないことを確認します。

 ・オーバーライドを禁止する

・クラスの拡張を禁止する

ポイント
  • ObjectクラスのtoStringメソッドを利用すると、オブジェクトを文字列表現にした情報を出力できる。
  • さらにサブクラスでオーバーライドすることで、任意の文字列を定めることも可能。

1.5.5 Objectクラスのequalsメソッドを利用したプログラム

 Objectクラスのequalsメソッドを利用して、オブジェクトが同じものかを比較して確認します。

equalsメソッドの仕組み
 このequalsメソッドはStringクラスでも出てきたのは覚えているでしょうか。StringクラスではこのObjectクラスのequalsメソッドがオーバーライドされています。その結果、文字列の比較が正しく行われるようになっています。ではObjectクラスはのequalsメソッドはどのような機能なのかというと、クラス変数が指し示すオブジェクトが同じものかどうかを判定し結果をboolean型で返します。

 ※Objectクラスのequalsメソッドは、オブジェクトの場所情報が同じなのかを比較します。

➢ Computer5.java ※ObjectクラスのtoStringメソッドをオーバーライドしていない従来のクラス
 1.5.4で作成したクラスを利用します。

① ソース・フォルダー      :myproj_basic/src
② パッケージ          :jp.co.f1.basic.ch11
③ 名前             :UseEqualsMethod
④ 作成するメソッド・スタブの選択:public static void main(String[] args) にチェックを入れる

➢ UseEqualsMethod.java

package jp.co.f1.basic.ch11;

public class UseEqualsMethod {
	public static void main(String[] args) {
		Computer5 com1 = new Computer5();
		Computer5 com2 = new Computer5();
		Computer5 com3 = com1;

		boolean check1 = com1.equals(com2);
		boolean check2 = com1.equals(com3);

		System.out.println("com1とcom2の比較結果は" + check1 + "です。");
		System.out.println("com1とcom3の比較結果は" + check2 + "です。");
	}
}

実行結果

解説

 5行目でComputer5クラスのオブジェクトを新しく1つ作成しています。
 6行目でもComputer5クラスのオブジェクトを新しく1つ作成しています。
 7行目はcom3の中に5行目で生成したオブジェクトの場所情報を代入しています。
   5:Computer5 com1 = new Computer5();
   6:Computer5 com2 = new Computer5();
   7:Computer5 com3 = com1;

 9行目でequalsメソッドを利用してcom1とcom2が同じオブジェクトの場所情報を保持しているか比較しています。その結果を変数check1に受け取ります。
 10行目も9行目と同じようにequalsメソッドを利用しています。ここでは比較対象がcom1とcom3に変わっています。そして結果を変数check2に受け取ります。
   9:boolean check1 = com1.equals(com2);
   10:boolean check2 = com1.equals(com3);

 12行目ではcom1とcom2の比較結果を画面に表示しています。
 13行目ではcom1とcom3の比較結果を画面に表示しています。
   12:System.out.println("com1とcom2の比較結果は" + check1 + "です。");
   13:System.out.println("com1とcom3の比較結果は" + check2 + "です。");

 実行結果を見て分かるように、com1とcom2は違うオブジェクトの場所情報を保持している為結果はfalseとなり、逆にcom3はcom1を代入して同じオブジェクトの場所情報を保持している為結果はtrueとなります。

ポイント
  • Objectクラスのequalsメソッドを利用すると、比較したものが同じオブジェクトなのか調べることができる。

1.5.6 ObjectクラスのgetClassメソッドを利用するプログラム

 ObjectクラスのgetClassメソッドを利用して、オブジェクトがどのクラスに所属しているのか確認します。

getClassメソッドの仕組み
 このgetClassメソッドを利用すると、オブジェクトが属するクラスの情報を返してくれます。その際の戻り値はClass型のオブジェクトになりますが、そのまま標準出力することで「class パッケージ名.クラス名」と表示してくれます。

ソースコード
➢ Computer2.java ※スーパークラス
 1.3.7で作成したクラスを利用します。

➢ NotePc6.java ※サブクラス
 1.4.2で作成したクラスを利用します。

① ソース・フォルダー      :myproj_basic/src
② パッケージ          :jp.co.f1.basic.ch11
③ 名前             :UseGetClassMethod
④ 作成するメソッド・スタブの選択:public static void main(String[] args) にチェックを入れる

➢ UseGetClassMethod.java
package jp.co.f1.basic.ch11;

public class UseGetClassMethod {
	public static void main(String[] args) {

		Computer2 com = new Computer2();
		NotePc6 npc = new NotePc6();

		System.out.println("comオブジェクトのクラスは" + com.getClass() + "です。");
		System.out.println("npcオブジェクトのクラスは" + npc.getClass() + "です。");
	}
}

実行結果

解説

 6行目と7行目ではComputer2クラスとNotePc6クラスのオブジェクトを生成しています。
   6:Computer2 com = new Computer2();
   7:NotePc6 npc = new NotePc6();

 9行目、10行目では、各オブジェクトからgetClassメソッドを呼び出して、そのオブジェクトが属しているクラスの情報を画面に出力しています。
   9: System.out.println("comオブジェクトのクラスは" + com.getClass() + "です。");
   10: System.out.println("npcオブジェクトのクラスは" + npc.getClass() + "です。");

 実行結果からも分かるように、そのオブジェクトが属するクラス情報が正しく表示されています。

ポイント
  • ObjectクラスのgetClassメソッドを利用すると、そのオブジェクトが属するクラスの情報が分かる。

 Javaのクラスは必ずObjectクラスのメンバを継承しているので、基本的な機能でも使い方によっては大変便利なものになります。今回紹介したメンバやその他のメンバについても忘れないようにして下さい。


NEXT>> 1.6 本章のまとめ