第17章 その他(Java)

17.1 インタフェース
(プログラム例 17.1 ) インタフェース
17.2 パッケージ
(プログラム例 17.2 ) パッケージ
17.3 マルチスレッド
(プログラム例 17.3 ) マルチスレッド

  この章においては,Java 独自の機能について説明します.

17.1 インタフェース

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

  また,一般的には,インタフェースはクラスではありませんので( Java のドキュメントの中で,「クラス」という言葉によって,「クラス」と「インタフェース」の両方を意味する場合がある),new 演算子によってインスタンスを作ることはできません.しかし,あるクラスがあるインターフェースを継承した場合,
	TestInter tm = new Test(...);   // クラスTestは,インタフェースTestInterを継承している
		
のように,オブジェクト変数をインターフェース名で宣言できます(もちろん,クラスに所属するメソッドや変数を参照しないなど,そのようにして問題がない場合).

(プログラム例 17.1 ) インタフェース
/****************************/
/* インタフェース           */
/*      coded by Y.Suganuma */
/****************************/
import java.io.*;

/*******************************/
/* インタフェースShukakuの定義 */
/*******************************/
interface Shukaku {
	boolean eatable = true;
	int shukaku ();
}

/*********************/
/* クラスYasaiの定義 */
/*********************/
class Yasai {
	String name = "野菜";
}

/**********************/
/* クラスDaikonの定義 */
/**********************/
class Daikon extends Yasai implements Shukaku {
	String name1 = "大根";
	double dia, length;   // 大きさ(直径,長さ)
	Daikon (double d, double l)   // コンストラクタ
	{
		length = l;
		dia    = d;
	}
	public int shukaku () { return 12; }   // インタフェースのshukakuのオーバーライド
}

/************************/
/* クラスKudamonoの定義 */
/************************/
class Kudamono {
	String name = "果物";
}

/*********************/
/* クラスRingoの定義 */
/*********************/
class Ringo extends Kudamono implements Shukaku {
	String name1 = "リンゴ";
	double dia;   // 大きさ
	Ringo (double d)   // コンストラクタ
	{
		dia = d;
	}
	public int shukaku () { return 10; }   // インタフェースのshukakuのオーバーライド
}

/************************************/
/* mainメソッドを含んだクラスの定義 */
/************************************/
public class Test {
	public static void main (String[] args)
	{

		Daikon x = new Daikon (10.0, 50.0);
		Ringo y  = new Ringo (10.0);

		System.out.println(x.name + "(" + x.name1 + ")" + " 大きさ " + x.length
                         + " " + x.dia + " 収穫 " + x.shukaku());
		System.out.println("     " + x.eatable);
		System.out.println(y.name + "(" + y.name1 + ")" + " 大きさ "
                         + y.dia + " 収穫 " + y.shukaku());
		System.out.println("     " + y.eatable);
	}
}
		

17.2 パッケージ

  複数のクラスを一つのグループにまとめ,他のグループからそのグループに含まれるクラス等を隠す手段としてパッケージ定義があります.パッケージ定義は以下のようにして行います.なお,package 文は,ソースコードの先頭になければなりません.
	package パッケージ名;
	public class {
		・・・・
	}
		
  異なるパッケージからは,public 宣言されていないクラス,メソッド,変数を参照できなくなります.また,public 宣言された他のパッケージのクラスやメソッドを参照するには,「パッケージ名.クラス名 or メソッド名」という形で参照できます.ただし,import することによって,クラス名だけによる参照も可能です.

  パッケージも,継承と同様,階層化可能です.ただし,パッケージ名とそのクラスが存在するディレクトリ名は一致している必要があります.例えば,パッケージ「 test.img 」内のクラス img1.class は,ディレクトリ「 test/img 」に存在しなければなりません.

(プログラム例 17.2 ) パッケージ

  下の例では,クラス Sansho (ディレクトリ /temp に存在)から,パッケージ test にあるクラス Complex (ディレクトリ /temp/test に存在)を参照しています.
/****************************/
/* パッケージ               */
/*      coded by Y.Suganuma */
/****************************/
import java.io.*;

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

		test.Complex x = new test.Complex (1.0, 2.0);
		test.Complex y = new test.Complex ();

		System.out.print("x = " + "(" + x.real() + "," +
                                  x.imag() + ") abs " + x.v + "\n");
		System.out.print("y = " + "(" + y.real() + "," +
                                  y.imag() + ") abs " + y.v + "\n");
	}
}
--------------------------------------------
/***********************/
/* クラスComplexの定義 */
/***********************/
package test;

public class Complex   // public宣言をしないと,他のパッケージからアクセスできない
					   // (クラス内の変数,メソッドについても同様)
{
	private double r;
	private double i;
	public double v;
	public Complex (double a, double b)   // constructor
	{
		r = a;
		i = b;
		v = Math.sqrt(a * a + b * b);
	}
	public Complex ()   // constructor
	{
		r = 0;
		i = 0;
		v = 0.0;
	}
	public double real() { return r;}
	public double imag() { return i;}
}
		
  クラス参照は,import を使用して,以下のように書くことも可能です.
import java.io.*;
import test.Complex;

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

		Complex x = new Complex (1.0, 2.0);
		Complex y = new Complex ();

		System.out.print("x = " + "(" + x.real() + "," +
                                  x.imag() + ") abs " + x.v + "\n");
		System.out.print("y = " + "(" + y.real() + "," +
                                  y.imag() + ") abs " + y.v + "\n");
	}
}
		
17.3 マルチスレッド

  Java には,スレッドと呼ばれる処理単位を複数同時に動作させる機能があります.詳細に関しては,プログラム例にしたがって説明します.

(プログラム例 17.3 ) マルチスレッド

  スレッドを作成する最も簡単な方法は,Java が提供している Thread クラスを継承することです.次の例は,この方法による簡単なプログラム例です.しかし,場合によっては,この方法を採用できない場合があります.例えば,後の章で述べるアプレットを作成する場合,必ず Applet クラスを継承しなければならなず,かつ,多重継承ができないため,この方法を採用できません.その際に使用できる方法が Runnable インタフェースを利用することです.次の例の 7 行目を以下のようにすることによって可能です.
class Test implements Runnable {   // Runnableインタフェースの利用

01	/****************************/
02	/* マルチスレッド           */
03	/*      coded by Y.Suganuma */
04	/****************************/
05	import java.io.*;
06
07	class Test extends Thread {   // Threadクラスの継承
08		static int total_1 = 0, total_2 = 0;
09					// main method
10		public static void main (String[] args) throws IOException
11		{
12			int i1, k = 0;
13						// 自分自身のオブジェクト
14			Test test = new Test();
15						// test型のオブジェクトの生成
16						// 同じオブジェクトから生成するため,異なる名前を付加(***と---)
17			Thread t1 = new Thread(test, "***");
18			Thread t2 = new Thread(test, "---");
19						// スレッドの実行開始
20			t1.start();
21			t2.start();
22						// スレッドの状況のチェック
23			while (t1.isAlive() || t2.isAlive()) {
24				k++;
25				System.out.println("k " + k);
26			}
27						// スレッドの終了後の処理
28			System.out.println("   終了しました(total_1 " + total_1 + " total_2 " + total_2 + ")");
29						// スレッドの終了
30			t1 = null;
31			t2 = null;
32		}
33					// スレッドの実行内容の記述(スレッドがスタートすると,
34					// このメソッドに記述されたことが実行されます)
35		public void run()
36		{
37			int i1;
38			System.out.println(Thread.currentThread().getName() + " が実行中");
39			if (Thread.currentThread().getName().equals("***")) {
40				for (i1 = 0; i1 < 10; i1++) {
41					total_1++;
42					System.out.println("total_1 " + total_1);
43				}
44			}
45			if (Thread.currentThread().getName().equals("---")) {
46				for (i1 = 0; i1 < 10; i1++) {
47					total_2++;
48					System.out.println("total_2 " + total_2);
49				}
50			}
51		}
52	}
		
  このプログラムでは,2 つのスレッドが生成されスタートすると,各スレッドは run メソッドに記述された内容(カウンタを 10 まで数える)を実行します.スレッドが実行されている間,main では,while 文が実行されカウンタ k が増加していきます.実際,このプログラムを実行すると,例えば,以下のような結果が得られます.
	k 1
	*** が実行中
	--- が実行中
	k 2
	total_1 1
	total_2 1
	k 3
	total_1 2
	total_2 2
	k 4
	total_1 3
	total_2 3
	k 5
	total_1 4
	total_1 5
	total_1 6
	total_1 7
	total_2 4
	total_2 5
	total_2 6
	total_2 7
	total_2 8
	total_2 9
	total_2 10
	total_1 8
	total_1 9
	k 6
	k 7
	k 8
	k 9
	k 10
	k 11
	k 12
	total_1 10
	k 13
	   終了しました(total_1 10 total_2 10)
		
  結果は,常に上のようになるわけではありません.状況によって実行順序は変化します.

  次のプログラムは,上の例を多少複雑にして,スレッドの実行順序を制御した場合の例です.最初のスレッド *** がスタートしカウントを始めますが,50 カウントしたところでスレッド --- の実行が終了するまで待ちます.そして,スレッド --- の実行終了後に再びカウントを継続します.
/**********************************/
/* マルチスレッド(実行順序制御) */
/*      coded by Y.Suganuma       */
/**********************************/
import java.io.*;

class Test extends Thread {   // Threadクラスの継承
	static int total_1 = 0, total_2 = 0;
				// main method
	public static void main (String[] args) throws IOException
	{
		int i1, k = 0;
					// 自分自身のオブジェクト
		Test test = new Test();
					// Threadオブジェクトの生成
		Thread t1 = new Thread(test, "***");
		t1.setPriority(6);   // プライオリティを少し高くしておきます
		Thread t2 = new Thread(test, "---");
					// スレッドの実行開始
		t1.start();
		t2.start();
					// スレッドの状況のチェック
		while (t1.isAlive() || t2.isAlive()) {
			k++;
			System.out.println("k " + k + " " + Math.sqrt((double)k));
		}
					// スレッドの終了後の処理
		System.out.println("   終了しました(total_1 " + total_1 + " total_2 " + total_2 + ")");
	}
				// スレッドの実行内容の記述(スレッドがスタートすると,
				// このメソッドに記述されたことが実行されます)
				// スレッド間の同期をとるためにsynchronized指定が必要
				// 同期をとれば,あるスレッドの実行中に他のスレッドが割り込むことはない
	public synchronized void run()
	{
		int i1;
		System.out.println(Thread.currentThread().getName() + " が実行中");
		if (Thread.currentThread().getName().equals("***")) {
			for (i1 = 0; i1 < 100; i1++) {
				total_1++;
				System.out.println("total_1 " + total_1);
				if (total_1 == 50) {
					try {
						this.wait();   // スレッドを待ち状態にする
					}
					catch (InterruptedException e) {}
				}
			}
		}
		if (Thread.currentThread().getName().equals("---")) {
			for (i1 = 0; i1 < 100; i1++) {
				total_2++;
				System.out.println("total_2 " + total_2);
			}
			this.notify();   // 待ち状態のスレッドを元に戻す
		}
	}
}
		
  このプログラムを実行すると以下のような結果が得られます.
	k 1
	*** が実行中
	k 2
	total_1 1
	k 3
	total_1 2
	k 4
	 ・・・
	total_1 48
	k 52
	total_1 49
	k 53
	total_1 50
	k 54
	k 55
	k 56
	k 57
	k 58
	k 59
	--- が実行中
	k 60
	total_2 1
	k 61
	total_2 2
	k 62
	total_2 3
	k 63
	total_2 4
	k 64
	total_2 5
	k 65
	total_2 6
	k 66
	total_2 7
	total_2 8
	 ・・・
	total_2 81
	total_2 82
	total_2 83
	k 67
	k 68
	k 69
	k 70
	k 71
	k 72
	k 73
	k 74
	k 75
	k 76
	k 77
	k 78
	k 79
	total_2 84
	k 80
	total_2 85
	k 81
	 ・・・
	total_2 98
	k 94
	total_2 99
	k 95
	total_2 100
	k 96
	k 97
	k 98
	 ・・・
	k 133
	k 134
	total_1 51
	k 135
	k 136
	k 137
	k 138
	total_1 52
	total_1 53
	k 139
	total_1 54
	k 140
	 ・・・
	k 184
	total_1 99
	k 185
	total_1 100
	k 186
	   終了しました(total_1 100 total_2 100)
		

ホームページ 目次 演習解答例目次 付録目次 索引