静岡理工科大学 菅沼ホーム 全体目次 演習解答例 付録 索引

第17章 その他

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

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

17.1 インタフェース(Java)

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

  また,一般的には,インタフェースはクラスではありませんので,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);
		Shukaku z  = 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);
//		System.out.println(z.name + "(" + z.name1 + ")" + " 大きさ " + y.dia);   // エラー
		System.out.println(z.shukaku() + " " + z.eatable);
	}
}
		

このプログラムを実行すると,以下に示すような結果が得られます.
野菜(大根) 大きさ 50.0 10.0 収穫 12
     true
果物(リンゴ) 大きさ 10.0 収穫 10
     true
10 true
		

17.2 パッケージ(Java)

  複数のクラスを一つのグループにまとめ,他のグループからそのグループに含まれるクラス等を隠す手段としてパッケージ定義があります.変数名や関数名の有効範囲を制限するといった意味からは,C++ における名前空間に似ています.パッケージ定義は以下のようにして行います.なお,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 には,スレッドと呼ばれる処理単位を複数同時に動作させる機能があります.詳細に関しては,プログラム例にしたがって説明します.なお,C/C++ にも,子プロセスを生成するための関数 forkspawnlspawnv などが存在します.詳細については,付録内の各関数に対する説明を参照して下さい.

(プログラム例 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						// 自分自身のオブジェクト
13			Test test = new Test();
14						// test型のオブジェクトの生成
15						// 同じオブジェクトから生成するため,異なる名前を付加(***と---)
16			Thread t1 = new Thread(test, "***");
17			Thread t2 = new Thread(test, "---");
18						// スレッドの実行開始
19			t1.start();
20			t2.start();
21						// スレッドの状況のチェック
22			int k = 0;
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			System.out.println(Thread.currentThread().getName() + " が実行中");
38			if (Thread.currentThread().getName().equals("***")) {
39				for (int i1 = 0; i1 < 10; i1++) {
40					total_1++;
41					System.out.println("total_1 " + total_1);
42				}
43			}
44			if (Thread.currentThread().getName().equals("---")) {
45				for (int i1 = 0; i1 < 10; i1++) {
46					total_2++;
47					System.out.println("total_2 " + total_2);
48				}
49			}
50		}
51	}
		
  このプログラムでは,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)
		

  次のプログラムは,上の例を多少複雑にして,スレッドの実行順序を制御した場合の例です.最初のスレッド *** がスタートしカウントを始めますが,10 カウントしたところでスレッド --- の実行が終了するまで待ちます.そして,スレッド --- の実行終了後に再びカウントを継続します.

/**********************************/
/* マルチスレッド(実行順序制御) */
/*      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
	{
					// 自分自身のオブジェクト
		Test test = new Test();
					// Threadオブジェクトの生成
		Thread t1 = new Thread(test, "***");
		t1.setPriority(6);   // プライオリティを少し高くしておきます
		Thread t2 = new Thread(test, "---");
					// スレッドの実行開始
		t1.start();
		t2.start();
					// スレッドの状況のチェック
		int k = 0;
		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()
	{
		System.out.println(Thread.currentThread().getName() + " が実行中");
		if (Thread.currentThread().getName().equals("***")) {
			for (int i1 = 0; i1 < 20; i1++) {
				total_1++;
				System.out.println("total_1 " + total_1);
				if (total_1 == 10) {
					try {
						this.wait();   // スレッドを待ち状態にする
					}
					catch (InterruptedException e) {}
				}
			}
		}
		if (Thread.currentThread().getName().equals("---")) {
			for (int i1 = 0; i1 < 20; i1++) {
				total_2++;
				System.out.println("total_2 " + total_2);
			}
			this.notify();   // 待ち状態のスレッドを元に戻す
		}
	}
}
		
  このプログラムを実行すると以下のような結果が得られます.
*** が実行中
total_1 1
k 1 1.0
total_1 2
total_1 3
k 2 1.4142135623730951
total_1 4
k 3 1.7320508075688772
total_1 5
k 4 2.0
total_1 6
k 5 2.23606797749979
total_1 7
k 6 2.449489742783178
total_1 8
k 7 2.6457513110645907
total_1 9
k 8 2.8284271247461903
total_1 10
k 9 3.0
--- が実行中
k 10 3.1622776601683795
total_2 1
k 11 3.3166247903554
total_2 2
k 12 3.4641016151377544
total_2 3
k 13 3.605551275463989
total_2 4
k 14 3.7416573867739413
total_2 5
k 15 3.872983346207417
total_2 6
k 16 4.0
total_2 7
k 17 4.123105625617661
total_2 8
k 18 4.242640687119285
total_2 9
k 19 4.358898943540674
total_2 10
k 20 4.47213595499958
total_2 11
k 21 4.58257569495584
total_2 12
k 22 4.69041575982343
total_2 13
total_2 14
k 23 4.795831523312719
total_2 15
k 24 4.898979485566356
total_2 16
k 25 5.0
total_2 17
k 26 5.0990195135927845
total_2 18
k 27 5.196152422706632
total_2 19
k 28 5.291502622129181
total_2 20
k 29 5.385164807134504
total_1 11
k 30 5.477225575051661
total_1 12
k 31 5.5677643628300215
total_1 13
k 32 5.656854249492381
total_1 14
k 33 5.744562646538029
total_1 15
total_1 16
k 34 5.830951894845301
total_1 17
k 35 5.916079783099616
total_1 18
k 36 6.0
total_1 19
k 37 6.082762530298219
total_1 20
k 38 6.164414002968976
   終了しました(total_1 20 total_2 20)
		

静岡理工科大学 菅沼ホーム 全体目次 演習解答例 付録 索引