情報学部 菅沼ホーム 全体目次 演習解答例 付録 索引

第8章 継承

  1. 8.1 継承
    1. (プログラム例 8.1 ) 継承の基本
    2. (プログラム例 8.2 ) フレームの表現
  2. 8.2 抽象クラス
    1. (プログラム例 8.3 ) 抽象クラス
  3. 8.3 インタフェース
    1. (プログラム例 8.4 ) インタフェース
  4. 演習問題8

8.1 継承

  Windows のプログラムを書くような場合について考えてみてください.多くの Windows アプリケーションが存在しますが,その基本的なレイアウトや機能はほとんど同じです.それらのすべてをいちから記述するとすれば大変な作業になります.しかし,Windows の基本機能を実現したプログラムがすでに存在し,そのプログラムを利用しながら必要な箇所の修正,追加を行うことによって可能であるとすれば,作業量は非常に少なくなります.これを実現したのが継承です.

  あるクラスに宣言されているメンバーをすべて受け継ぎ(継承inheritance ),それに新たなメンバーを付加して新しいクラスを宣言することができます.このとき,基になったクラスをスーパークラス,そのクラスを継承したクラスをサブクラスと呼びます.

  Java と C++ の大きな違いは,Java においては,1 つのクラスだけを継承でき,多重継承が許されない点です.継承するクラスは,以下のようにして定義します.もし,extends 節を記述しないと,そのクラスは Java の最上位にある Object というクラスのサブクラスであるとみなされます(今まで書いてきたプログラムのほとんどがこれに当たります).
class クラス名 extends スーパークラス名 {
	変更部分
}		
  メンバーの継承に関する考え方は,基本的に,C++ と同じですが,多少の違いがあります.一つは,親のクラスに対するアクセス権を記述しないため,すべてのメンバーがほとんどそのままの状態( private は private,public は public 等)で継承される点です.

  コンストラクタは継承されませんので,親のコンストラクタの呼び出しが必要な場合は,プログラム例 8.1 に示すように,キーワード super (キーワード this と同様の役割を持ったキーワードであり,this が自分自身を意味するのに対し,super は自分のスーパークラスを意味する)を使用して明示的に呼び出します.

  super はスーパークラスの変数を参照する際にも使用されます.サブクラスにおいてスーパークラスと同じ名前の変数,例えば var,を定義すると,スーパークラスの変数 var がサブクラスからは見えなくなってしまいます.そのようなときは,例えば,
super.var		
のような形で,スーパークラスの変数 var を参照できます.

(プログラム例 8.1 ) 継承の基本 

  クラス Kodomo は,クラス Oya を継承しています.また,変数修飾子の性質を見るため,クラス Oya をクラス Test やクラス Kodomo とは異なるパッケージに入れています.

01	/****************************/
02	/* 継承の基本               */
03	/*      coded by Y.Suganuma */
04	/****************************/
05	import java.io.*;
06	import temp.Oya;
07	
08	public class Test {
09		public static void main(String args[]) throws IOException
10		{
11						// 親
12			Oya oya = new Oya (1, 2, 3, 4);
13			System.out.println("親 : ");
14			System.out.println("  Oyaの関数を利用");
15			oya.output_oya();
16			System.out.println("  from main");
17			System.out.println("    priv1 ?, prot1 ?, pub " + oya.pub + ", ndef1 ?, stat " + Oya.stat);
18						// 子供
19			Kodomo k = new Kodomo (10, 20, 30, 40, 50, 60, 70);
20			System.out.println("子供 : ");
21							// Oyaから継承した部分
22			System.out.println("  Oyaから継承した関数を利用");
23			k.output_oya();
24			System.out.println("  Kodomoの関数を利用");
25			k.output_k();
26			System.out.println("  from main");
27			System.out.println("    priv1 ??, priv2 ??, prot1 ??, prot2 " + k.prot2 + ", pub " + k.pub + ", ndef1 ??, ndef2 " + k.ndef2 + ", stat " + Kodomo.stat);
28		}
29	}
30	
31	/**********************/
32	/* クラスkodomoの定義 */
33	/**********************/
34	class Kodomo extends Oya {
35		private int priv2;
36		protected int prot2;
37		int ndef2;
38						// コンストラクタ
39		Kodomo (int n1, int n2, int n3, int n4, int n5, int n6, int n7)
40		{
41			super (n1, n2, n3, n4);   // 親のコンストラクタの呼び出し
42			priv2 = n5;
43			prot2 = n6;
44			ndef2 = n7;
45		}
46						// 出力
47		void output_k()
48		{
49			System.out.println("    priv1 ??, priv2 " + priv2 + ", prot1 " + prot1 + ", prot2 " + prot2 + ", pub " + pub + ", ndef1 ??, ndef2 " + ndef2 + ", stat " + stat);
50		}
51	}
52	
53	---------------------------------------------------------------------------
54	
55	/*******************/
56	/* クラスOyaの定義 */
57	/*******************/
58	package temp;
59	public class Oya {
60		public static int stat = -1;
61		private int priv1;
62		protected int prot1;
63		public int pub;
64		int ndef1;
65						// コンストラクタ
66		public Oya (int n1, int n2, int n3, int n4)
67		{
68			priv1 = n1;
69			prot1 = n2;
70			pub   = n3;
71			ndef1 = n4;
72		}
73						// 出力
74		public void output_oya()
75		{
76			System.out.println("    priv1 " + priv1 + ", prot1 " + prot1 + ", pub " + pub + ", ndef1 " + ndef1 + ", stat " + stat);
77		}
78	}
		
12 行目~ 17 行目

  クラス Oya のインスタンスを生成し,クラス内に定義されている変数を出力しています.12 行目において,クラス Oya のインスタンスを生成すると,66 行目~ 72 行目のコンストラクタによって,4 つの変数に指定された値が設定されます.

  15 行目では,クラス Oya に定義されているメソッド output_oya を使用して,5 つの変数の値を出力しています.オブジェクト内の変数を,オブジェクト内のメソッドを使用して参照していますので,すべての変数の値を正しく出力できます(出力結果の 03 行目).

  17 行目においては,main メソッドから,直接,Oya オブジェクトの変数の値を出力しています.異なるパッケージの,異なるクラスからの参照ですので,public 指定された変数だけを参照できます(出力結果の 05 行目).

19 行目~ 27 行目

  クラス Oya を継承したクラス Kodomo のインスタンスを生成し,クラス内に定義されている変数を出力しています.19 行目において,クラス Kodomo のインスタンスを生成しています.クラス Kodomo には,3 つの変数が定義されていますが,クラス Oya から継承した変数に値を設定してやる必要があるため,7 つのデータを引数として渡し,その中の 4 つのデータを使用して,コンストラクタの中でクラス Oya のコンストラクタを呼んでいます( 41 行目).

  23 行目では,クラス Oya から継承したメソッド output_oya を使用して,クラス Oya から継承した 5 つの変数の値を出力しています.オブジェクト内の変数を,オブジェクト内のメソッドを使用して参照していますので,すべての変数の値を正しく出力できます(出力結果の 08 行目).

  25 行目においては,クラス Kodomo に定義されたメソッド output_k を使用して Kodomo オブジェクト内の変数を出力しています.継承された変数ではありますが,変数 priv1 は private 指定されておりクラス Oya のオブジェクトだけからしか参照できないため,また,変数 ndef1 には変数修飾子が指定されておらず同じパッケージ及びクラス Oya のオブジェクトだけからしか参照できないため,メソッド output_k からは参照できません.また,異なるパッケージの異なるオブジェクトの変数ではありますが,変数 prot1 は,protected 指定がされているため,クラス Oya を継承したクラス Kodomo のメソッドから参照できます( 出力結果の 10 行目).

  27 行目においては,main メソッドから,直接,Kodomo オブジェクトの変数の値を出力しています.基本的に public 指定された変数だけを参照できますが,同じパッケージ内のクラスからの参照ですので,変数 prot2 及び ndef2 も参照可能です(出力結果の 12 行目).
  このプログラムを実行すると,次のような結果が得られます(行番号は説明のため).
01	親 :
02	  Oyaの関数を利用
03	    priv1 1, prot1 2, pub 3, ndef1 4, stat -1
04	  from main
05	    priv1 ?, prot1 ?, pub 3, ndef1 ?, stat -1
06	子供 :
07	  Oyaから継承した関数を利用
08	    priv1 10, prot1 20, pub 30, ndef1 40, stat -1
09	  Kodomoの関数を利用
10	    priv1 ??, priv2 50, prot1 20, prot2 60, pub 30, ndef1 ??, ndef2 70, stat -1
11	  from main
12	    priv1 ??, priv2 ??, prot1 ??, prot2 60, pub 30, ndef1 ??, ndef2 70, stat -1
		

  クラス Oya をクラス Test や Kodomo と同じパッケージに入れた場合は以下のようになります.出力結果の違いに注意して下さい.

/****************************/
/* 継承の基本               */
/*      coded by Y.Suganuma */
/****************************/
import java.io.*;

public class Test {
	public static void main(String args[]) throws IOException
	{
					// 親
		Oya oya = new Oya (1, 2, 3, 4);
		System.out.println("親 : ");
		System.out.println("  Oyaの関数を利用");
		oya.output_oya();
		System.out.println("  from main");
		System.out.println("    priv1 ?, prot1 " + oya.prot1 + ", pub " + oya.pub + ", ndef1 " + oya.ndef1 + ", stat " + Oya.stat);
					// 子供
		Kodomo k = new Kodomo (10, 20, 30, 40, 50, 60, 70);
		System.out.println("子供 : ");
						// Oyaから継承した部分
		System.out.println("  Oyaから継承した関数を利用");
		k.output_oya();
		System.out.println("  Kodomoの関数を利用");
		k.output_k();
		System.out.println("  from main");
		System.out.println("    priv1 ??, priv2 ??, prot1 " + k.prot1 + ", prot2 " + k.prot2 + ", pub " + k.pub + ", ndef1 " + k.ndef1 + ", ndef2 " + k.ndef2 + ", stat " + Kodomo.stat);
	}
}

/**********************/
/* クラスkodomoの定義 */
/**********************/
class Kodomo extends Oya {
	private int priv2;
	protected int prot2;
	int ndef2;
					// コンストラクタ
	Kodomo (int n1, int n2, int n3, int n4, int n5, int n6, int n7)
	{
		super (n1, n2, n3, n4);   // 親のコンストラクタの呼び出し
		priv2 = n5;
		prot2 = n6;
		ndef2 = n7;
	}
					// 出力
	void output_k()
	{
		System.out.println("    priv1 ??, priv2 " + priv2 + ", prot1 " + prot1 + ", prot2 " + prot2 + ", pub " + pub + ", ndef1 " + ndef1 + ", ndef2 " + ndef2 + ", stat " + stat);
	}
}

/*******************/
/* クラスOyaの定義 */
/*******************/
class Oya {
	public static int stat = -1;
	private int priv1;
	protected int prot1;
	public int pub;
	int ndef1;
					// コンストラクタ
	public Oya (int n1, int n2, int n3, int n4)
	{
		priv1 = n1;
		prot1 = n2;
		pub   = n3;
		ndef1 = n4;
	}
					// 出力
	public void output_oya()
	{
		System.out.println("    priv1 " + priv1 + ", prot1 " + prot1 + ", pub " + pub + ", ndef1 " + ndef1 + ", stat " + stat);
	}
}
		
  このプログラムを実行すると,次のような結果が得られます.
親 :
  Oyaの関数を利用
    priv1 1, prot1 2, pub 3, ndef1 4, stat -1
  from main
    priv1 ?, prot1 2, pub 3, ndef1 4, stat -1
子供 :
  Oyaから継承した関数を利用
    priv1 10, prot1 20, pub 30, ndef1 40, stat -1
  Kodomoの関数を利用
    priv1 ??, priv2 50, prot1 20, prot2 60, pub 30, ndef1 40, ndef2 70, stat -1
  from main
    priv1 ??, priv2 ??, prot1 20, prot2 60, pub 30, ndef1 40, ndef2 70, stat -1
		

(プログラム例 8.2 ) フレームの表現 

  ここでは,人工知能におけるフレーム表現による知識をクラスを使用して表現してみます.今,以下に述べるような 3 つのフレーム(知識)があるとし,それぞれをクラスとして定義します.

クラス Animal
  動物は生物である(生物は 0,その他は 1 )
  動物は酸素呼吸する(酸素呼吸は 0,その他は 1 )
クラス Human
  人間は動物である
  人間の身長は普通 170 ㎝である
  人間の標準体重は,一般的に,(身長 - 100)x 0.9 で算出される
クラス Taro
  太郎は人間である
  太郎の身長は 175 ㎝である

  以下に示すのがその具体的なプログラムです.この場合,クラス Animal のコンストラクタは引数無しでも書けます( 2 つのメンバー変数に 0 を代入すればよい)が,サブクラスのコンストラクタとの関係を説明するためにあえて引数を持たせてあります.

  クラス Human は,クラス Animal のサブクラスでなければコンストラクタを必要としませんが,そのスーパークラスである Animal のコンストラクタにパラメータを渡すためのコンストラクタが必要になります.もちろん,クラス Animal がコンストラクタを持たなかったり,コンストラクタを所有していても引数がない場合は,クラス Human にもコンストラクタは必要ありません.

  クラス Taro のコンストラクタは,クラス Human のコンストラクタに渡すためのパラメータを定数で与えています.このようにすれば,クラス Taro の宣言に必要な引数の数を減らすことができます.

/****************************/
/* 継承(フレームの表現)   */
/*      coded by Y.Suganuma */
/****************************/
import java.io.*;

/**********************/
/* クラスAnimalの定義 */
/**********************/
class Animal {                  /* 動物フレーム */
	protected int ani;                       // 生物:0,その他:1
	protected int ox;                        // 酸素呼吸:0,その他:1
	Animal(int k1, int k2)         // コンストラクタ
	{
		ani = k1;
		ox  = k2;
	}
	void shu(int sw)              // 生物か否かに答える
	{
		if (ani == sw)
			System.out.println("yes");
		else
			System.out.println("no");
	}
}

/*********************/
/* クラスHumanの定義 */
/*********************/
class Human extends Animal {   /* 人間フレーム,クラスAnimalを継承 */
	protected int height;                    // 身長
	protected double weight;                 // 体重
	Human(int x, int y)   // コンストラクタ
	{
		super(x, y);   // クラスAnimalのコンストラクタの呼び出し
	}
	void taiju()   // 体重を返す関数
	{
		System.out.println("体重は " + weight + " Kgです");
	}
}

/********************/
/* クラスTaroの定義 */
/********************/
class Taro extends Human {     /* 太郎フレーム,クラスHumanを継承 */
	Taro(int hei, double wei)   // コンストラクタ
	{
		super(0, 0);    // クラスHumanのコンストラクタの呼び出し
		height = (hei == 0) ? 170 : hei;
		weight = (wei == 0.0) ? 0.9 * (height - 100) : wei;
	}
}

/**************************************/
/* mainメソッドを含むクラスTestの定義 */
/**************************************/
public class Test {
	public static void main(String args[]) throws IOException
	{
		Human h1 = new Human (0, 0);
		Taro t1 = new Taro (175, 0.0);

		h1.shu(1);    // 人間は生物ではない?
		t1.shu(0);    // 太郎は生物?
		t1.taiju();   // 太郎の体重は?
	}
}
		

  このプログラムを実行すると,以下のような結果が得られます.例えば,太郎が生物か否かの問に対して,太郎自身の知識には,何ら記述してありませんが,上位概念(この場合は,クラス Animal )の知識(変数 ani と関数 shu )を継承して,正しい答えをだしています.
no
yes
体重は 67.5 Kgです		

8.2 抽象クラス

  抽象クラスabstract class )は,スーパークラスとしてだけ使えるクラスです.インスタンスを生成するためには使用できません.仕様の詳細を決めずに,抽象的な概念の定義だけを行います.抽象概念は,抽象クラスから派生する非抽象クラスによって表される具体的概念の基礎となります.抽象クラスを継承したクラスは,抽象クラスにおいて abstract 指定されたメソッドをオーバーライドする必要があります.なお,抽象メソッドを含むクラスは,必ず,抽象クラスとして定義する必要があります.

(プログラム例8.3) 抽象クラス 

  次のプログラムは,クラス Figure という抽象クラスを定義し,その派生クラスとして,楕円の面積を計算するクラス Ellipse および多角形(左回りに与えた n 点からなる多角形)の面積を計算するクラス Polygon を定義したものです.

/****************************/
/* 抽象クラス               */
/*      coded by Y.Suganuma */
/****************************/
import java.io.*;

/****************/
/* クラスFigure */
/****************/
abstract class Figure {
	double s;   // 図形の面積
	abstract void input();   // データの入力,抽象メソッド
	abstract void menseki();   // 面積の計算,抽象メソッド
	void print(int sw)   // 結果の出力
	{
		if (sw == 0)
			System.out.println("   与えられた楕円の面積は " + s + " です");
		else
			System.out.println("与えられた多角形の面積は " + s + " です");
	}
}

/*****************/
/* クラスEllipse */
/*****************/
class Ellipse extends Figure {
	private double a, b;   // 楕円の長径と短径
	void input()   // データの入力
	{
		BufferedReader in = new BufferedReader(new InputStreamReader(System.in));
		try {
			System.out.print("楕円の長径は? ");
			a = Double.parseDouble(in.readLine());
			System.out.print("楕円の長径は? ");
			b = Double.parseDouble(in.readLine());
		}
		catch (IOException ignored) {}
	}
	void menseki()   // 楕円の面積
	{
		s = 4.0 * Math.atan(1.0) * a * b;
	}
}

/*****************/
/* クラスPolygon */
/*****************/
class Polygon extends Figure {
	private int n;   // 点の数
	private int [] m;   // 点の番号
	private double [][] p;   // 各点の座標
					// コンストラクタ
	Polygon (int n1)
	{
		if (n1 < 3) {
			System.out.println("3点以上与えてください");
			System.exit(1);
		}
		else {
			n = n1;
			m = new int [n];
			p = new double [n][2];
		}
	}
					// データの入力
	void input()
	{
		BufferedReader in = new BufferedReader(new InputStreamReader(System.in));
		System.out.println("各点の座標の入力(反時計回り)");
		try {
			for (int i1 = 0; i1 < n; i1++) {
				System.out.print("   " + (i1+1) + " 番目の点のx座標は? ");
				p[i1][0] = Double.parseDouble(in.readLine());
				System.out.print("      " + (i1+1) + " 番目の点のy座標は? ");
				p[i1][1] = Double.parseDouble(in.readLine());
			}
		}
		catch (IOException ignored) {}
	}
					// 多角形の面積
	void menseki()
	{
		for (int i1 = 0; i1 < n; i1++)
			m[i1] = i1;
		s = men(n, p, m);
	}

	/******************************/
	/* 多角形の面積を再帰的に計算 */
	/*      n : 点の数            */
	/*      p : 点の座標          */
	/*      m : 点番号            */
	/*      return : 面積         */
	/******************************/
	private double men(int n, double [][] p, int [] m)
	{
		double s = 0.0;
		if (n == 3)   // 三角形
			s = sankaku(n, 0, m, p);
		else {
			int sw1 = 0;
			for (int i1 = 0; i1 < n && sw1 == 0; i1++) {
				int sw = check1(n, i1, m, p);   // 多角形の中?
				if (sw == 0) {
					sw = check2(n, i1, m, p);   // 中に他の点がある?
					if (sw == 0) {
						s = sankaku(n, i1, m, p);   // i1から始まる三角形
						for (int i2 = i1+1; i2 < n-1; i2++)   // 点を除く
							m[i2] = m[i2+1];
						s   += men(n-1, p, m);   // (n-1)この点からなる多角形
						sw1  = 1;
					}
				}
			}
		}
		return s;
	}

	/***********************/
	/* 三角形の面積の計算  */
	/*      n : 点の数     */
	/*      k : 開始点番号 */
	/*      m : 点番号     */
	/*      p : 点の座標   */
	/*      return : 面積  */
	/***********************/
	double sankaku(int n, int k, int [] m, double [][] p)
	{
		int k1 = m[k];
		int k2 = (k == n-1) ? m[0] :m[k+1];
		int k3;
		if (k < n-2)
			k3 = m[k+2];
		else
			k3 = (k == n-2) ? m[0] : m[1];
		double x1 = p[k2][0] - p[k1][0];
		double y1 = p[k2][1] - p[k1][1];
		double r1 = Math.sqrt(x1 * x1 + y1 * y1);
		double t1 = Math.atan2(y1, x1);
		x1 = p[k3][0] - p[k1][0];
		y1 = p[k3][1] - p[k1][1];
		double r2 = Math.sqrt(x1 * x1 + y1 * y1);
		double t2 = Math.atan2(y1, x1);
		double t3 = t2 - t1;
		double s = Math.abs(0.5 * r1 * r2 * Math.sin(t3));
		return s;
	}

	/****************************************/
	/* 三角形が多角形の内部か否かのチェック */
	/*      n : 点の数                      */
	/*      k : 開始点番号                  */
	/*      m : 点番号                      */
	/*      p : 点の座標                    */
	/*      return : =0 : 内部である        */
	/*               =1 : 内部でない        */
	/****************************************/
	int check1(int n, int k, int [] m, double [][] p)
	{
		double pi = 4.0 * Math.atan2(1.0, 1.0);
		int k1 = m[k];
		int k2 = (k == n-1) ? m[0] :m[k+1];
		int k3, sw = 0;
		if (k < n-2)
			k3 = m[k+2];
		else
			k3 = (k == n-2) ? m[0] : m[1];
		double x1 = p[k3][0] - p[k1][0];
		double y1 = p[k3][1] - p[k1][1];
		double t1 = Math.atan2(y1, x1);
		x1 = p[k2][0] - p[k1][0];
		y1 = p[k2][1] - p[k1][1];
		double t2 = Math.atan2(y1, x1);
		double t3 = t2 - t1;
		if (t3 > pi)
			t3 -= 2.0 * pi;
		else {
			if (t3 < -pi)
				t3 += 2.0 * pi;
		}
		if (t3 > 0.0)
			sw = 1;
		return sw;
	}

	/************************************************/
	/* 三角形内に他の頂点が存在するか否かのチェック */
	/*      n : 点の数                              */
	/*      k : 開始点番号                          */
	/*      m : 点番号                              */
	/*      p : 点の座標                            */
	/*      return : =0 : 存在しない                */
	/*               =1 : 存在する                  */
	/************************************************/
	int check2(int n, int k, int [] m, double [][] p)
	{
		double pi = 4.0 * Math.atan2(1.0, 1.0);
		int k1 = m[k];
		int k2 = (k == n-1) ? m[0] :m[k+1];
		int k3, sw = 0;
		if (k1 < n-2)
			k3 = m[k+2];
		else
			k3 = (k == n-2) ? m[0] : m[1];
		double x1  = p[k2][0] - p[k1][0];
		double y1  = p[k2][1] - p[k1][1];
		double t10 = Math.atan2(y1, x1);
		x1  = p[k3][0] - p[k2][0];
		y1  = p[k3][1] - p[k2][1];
		double t11 = Math.atan2(y1, x1);
		x1  = p[k1][0] - p[k3][0];
		y1  = p[k1][1] - p[k3][1];
		double t12 = Math.atan2(y1, x1);
		for (int i1 = 0; i1 < n && sw == 0; i1++) {
			if (i1 != k1 && i1 != k2 && i1 != k3) {
				x1 = p[m[i1]][0] - p[k1][0];
				y1 = p[m[i1]][1] - p[k1][1];
				double t2 = Math.atan2(y1, x1);
				double t3 = t2 - t10;
				if (t3 > pi)
					t3 -= 2.0 * pi;
				else {
					if (t3 < -pi)
						t3 += 2.0 * pi;
				}
				if (t3 > 0.0) {
					x1 = p[m[i1]][0] - p[k2][0];
					y1 = p[m[i1]][1] - p[k2][1];
					t2 = Math.atan2(y1, x1);
					t3 = t2 - t11;
					if (t3 > pi)
						t3 -= 2.0 * pi;
					else {
						if (t3 < -pi)
							t3 += 2.0 * pi;
					}
					if (t3 > 0.0) {
						x1 = p[m[i1]][0] - p[k3][0];
						y1 = p[m[i1]][1] - p[k3][1];
						t2 = Math.atan2(y1, x1);
						t3 = t2 - t12;
						if (t3 > pi)
							t3 -= 2.0 * pi;
						else {
							if (t3 < -pi)
								t3 += 2.0 * pi;
						}
						if (t3 > 0.0)
							sw = 1;
					}
				}
			}
		}
		return sw;
	}
}

/**************************************/
/* mainメソッドを含むクラスTestの定義 */
/**************************************/
public class Test {
	public static void main(String args[]) throws IOException
	{
/*
	 楕円
*/
		Ellipse e1 = new Ellipse ();

		e1.input();
		e1.menseki();
		e1.print(0);

/*
	 多角形
*/
		BufferedReader in = new BufferedReader(new InputStreamReader(System.in));

		System.out.print("点の数は? ");
		int n = Integer.parseInt(in.readLine());

		Polygon p1 = new Polygon (n);

		p1.input();
		p1.menseki();
		p1.print(1);
	}
}
		

8.3 インタフェース

  Java には,C++ のように,複数のクラスを継承する多重継承の機能がありません.そこで,その機能の一部を補うのがインタフェースです.クラスの階層構造の中で,上下関係にない複数クラス間で情報を交換する(共通の変数やメソッドの利用する)ための機能であり,クラスと同様以下のようにして定義されます.
public interface インタフェース名 implements 親インタフェース名 [ , ・・・ ] {
	// 変数: public static final とみなされる
	// メソッド宣言:abstract とみなされる
}		
  クラスは,複数のインタフェースを継承可能です( implements の後ろに複数のインタフェースをカンマで区切って並べることができます).ただし,継承可能なものは,メソッドの宣言と public static final な変数(基本的に定数)だけであり,継承したクラスが抽象クラスでない限り,インタフェース内で宣言されたメソッドの内容を定義(オーバーライド)してやる必要があります.

  また,一般的には,インタフェースはクラスではありませんので,インスタンスを作ることはできません.しかし,あるクラスがあるインターフェースを継承した場合,
TestInter tm = new Test(...);   // クラス Test は,インタフェース TestInter を継承している		
のように,オブジェクト変数をインタフェース名で宣言できます.ただし,tm を通して参照できるのは,インタフェース内で定義されている変数やメソッドだけです.

(プログラム例 8.4 ) インタフェース
01	/****************************/
02	/* インタフェース           */
03	/*      coded by Y.Suganuma */
04	/****************************/
05	import java.io.*;
06	
07	/********************************/
08	/* インタフェースTabemonoの定義 */
09	/********************************/
10	interface Tabemono {
11		boolean eatable = true;
12		int shukaku ();
13	}
14	
15	/*********************/
16	/* クラスYasaiの定義 */
17	/*********************/
18	class Yasai {
19		String name = "野菜";
20	}
21	
22	/**********************/
23	/* クラスDaikonの定義 */
24	/**********************/
25	class Daikon extends Yasai implements Tabemono {
26		String name1 = "大根";
27		double dia, length;   // 大きさ(直径,長さ)
28		Daikon (double d, double l)   // コンストラクタ
29		{
30			length = l;
31			dia    = d;
32		}
33		public int shukaku () { return 12; }   // インタフェースのshukakuのオーバーライド
34	}
35	
36	/************************/
37	/* クラスKudamonoの定義 */
38	/************************/
39	class Kudamono {
40		String name = "果物";
41	}
42	
43	/*********************/
44	/* クラスRingoの定義 */
45	/*********************/
46	class Ringo extends Kudamono implements Tabemono {
47		String name1 = "リンゴ";
48		double dia;   // 大きさ
49		Ringo (double d)   // コンストラクタ
50		{
51			dia = d;
52		}
53		public int shukaku () { return 10; }   // インタフェースのshukakuのオーバーライド
54	}
55	
56	/************************************/
57	/* mainメソッドを含んだクラスの定義 */
58	/************************************/
59	public class Test {
60		public static void main (String[] args)
61		{
62	
63			Daikon x = new Daikon (10.0, 50.0);
64			Ringo y  = new Ringo (10.0);
65			Tabemono z = new Ringo (11.0);
66	
67			System.out.println(x.name + "(" + x.name1 + ")" + " 大きさ " + x.length + " " + x.dia + " 収穫 " + x.shukaku() + " 食料 " + x.eatable);
68			System.out.println(y.name + "(" + y.name1 + ")" + " 大きさ " + y.dia + " 収穫 " + y.shukaku() + " 食料 " + Tabemono.eatable);
69			System.out.println("収穫 " + z.shukaku() + " 食料 " + z.eatable);
70		}
71	}
		
10 行目~ 13 行目

  インタフェース Tabemono の定義です.変数 eatable とメソッド shukaku を定義しています.

25 行目~ 34 行目

  クラス Daikon の定義です.クラス Yasai を継承すると共に,インタフェース Tabemono を継承しています.33 行目において,インターフェースに定義してあるメソッド shukaku をオーバーライドしています.

46 行目~ 54 行目

  25 行目~ 34 行目と同様,クラス Ringo の定義です.

67 行目

  クラス Daikon のインスタンス x に対して,変数の値やメソッドを呼んだ結果を出力しています(出力結果の 1 行目).継承が正しく行われていることが分かると思います.

68 行目

  67 行目と同様,クラス Ringo のインスタンス y に対して,変数の値やメソッドを呼んだ結果を出力しています(出力結果の 2 行目).インターフェースに定義してある変数 eatable は,public static final とみなされますので,このような参照も可能です.

69 行目

  65 行目において,インタフェース Tabemono の名前で,クラス Ringo のインスタンスを生成しています.従って,この変数を通して参照できるのはインターフェースに定義してある変数 eatable とメソッド shukaku だけです(出力結果の 3 行目).67,68 行目のような記述をするとエラーになってしまいます.
  このプログラムを実行すると,以下に示すような出力が得られます.
野菜(大根) 大きさ 50.0 10.0 収穫 12 食料 true
果物(リンゴ) 大きさ 10.0 収穫 10 食料 true
収穫 10 食料 true		
  なお,抽象メソッドを 1 つだけ持つ関数型インタフェースは,ラムダ式の定義に利用可能です.詳細については,7.4 節を参照してください.

演習問題8

[問1]クラス Shape をスーパークラス,正方形,円,正三角形をそのサブクラスとして定義せよ.なお,それらの面積を表すメソッドを,抽象メソッドとしてスーパークラスで定義せよ.

[問2]整数の値を変数として持つクラス Number をスーパークラスとして定義し,その値を 8 進,16 進,及び,10 進で出力するメソッドを持つクラスをそのサブクラスとして定義せよ.なお,出力を行うメソッドを,抽象メソッドとしてスーパークラスで定義せよ.

情報学部 菅沼ホーム 全体目次 演習解答例 付録 索引