22. Runnableインタフェース

既に1つのクラスを継承しているときに使います。

(1) 文字が流れるアプレットの作り方

 ホームページで文字が右から左に流れていくアプレットを見たことはありますか?今回はそんなプログラムを作ってみます。アプレットプログラムなので java.applet.Applet クラスのサブクラスにします。

 文字は右から左に毎回10ずつずらして書いていくことでスクロールしているように見せます。文字列の左端が -100 より小さくなったら、また(300,0)の座標に戻して左にスクロールさせます。

  Scroller.java(未完成)

import java.awt.*;
import java.applet.Applet;

public class Scroller extends Applet {
    // 文字列表示位置の初期値
    private int strX = 300;

    public void start() {
        while(true) {
            // 文字列の描画
            repaint();
            // 描画位置を左にずらす
            strX = strX - 10;
            // 文字列が画面から消えたとき
      if (strX<-100) strX = 300;
        }
    }

    public void paint(Graphics g) {
        g.drawString("Javaプログラミング",strX,30);
    }
}

 これでうまく文字が流れるでしょうか?次の HTML ファイルを作って実行してみます。

  Scroller.html

<APPLET CODE="Scroller.class" width="300" height="50">
</APPLET>


 実行例



 しかし、画面には何も表示されません。これは、start( ) 内に無限ループがあるため、描画処理を行うスレッドに実行の機会が与えられないからです。

 今回のようにアニメーションのプログラムを作るには新規にスレッドを作って実行しなければいけません。

 スレッドで動くプログラムをつくるには Thread クラスのサブクラスにするという方法を見てきましたが、今回は既に Applet クラスのサブクラスになっているので、これ以上 extends 節にクラスを指定することができません。Java ではスーパークラスは1つしか持てないからです。

 このように、既にあるクラスのサブクラスになっている場合、スレッドのプログラムにするには Runnable というインタフェースを使って書きます。


(2) Runnable インタフェース

 Runnable インタフェースにはメソッドが1つだけ定義されています。そのメソッドは
public void run( )

 です。これはどこかで見たメソッドですね。そうです、Thread クラスのメソッドと同じ run( ) メソッドです。既に1つのクラスを継承している場合、この Runnable インタフェースを実装したクラスとし、Thread のサブクラスを作った時と同じように run( ) メソッド内に処理を書いていきます。

 今回は start( ) の処理内容をスレッドで行うので public void start( ) を public void run( ) に置き換えます。

 置き換えたあと、run( ) メソッド内に Thread.sleep( ) を入れておきます。sleep( ) を入れると他のスレッドに実行の機会を与えることができるので、無限ループ内でも描画用のスレッドに実行件を譲ることができます。

  Scroller.java (未完成)

import java.awt.*;
import java.applet.Applet;

public class Scroller extends Applet implements Runnable {
    // 文字列表示位置の初期値
    private int strX = 300;

    public void run() {
        while(true) {
            // 文字列の描画
            repaint();
            // 描画位置を左にずらす
            strX = strX - 10;
            // 文字列が画面から消えたとき
            if (strX<-100) strX = 300;
            
            try {
                Thread.sleep(200);
            } catch (Exception e) { }
        }
    }

    public void paint(Graphics g) {
        g.drawString("Javaプログラミング",strX,30);
    }
}


(3) Runnable インタフェースを使った場合のスレッド実行

 Runnalbe インタフェースを使った場合、スレッドの実行を開始するには次の Thread クラスのコンストラクタを使い、Thread オブジェクトを作ります。
Thread(Runnable obj)

 引数に Runnable 型をとるコンストラクタです。このコンストラクタの意味は「Runnable インタフェースを実装しているクラスなら引数にとることができますよ」という意味なので、今回は引数に this を指定します。スレッドの実行開始は Thread クラスの start( ) メソッドです。

 つまり、

Thread kick = new Thread(this);
kick.start( );

 と書くと、kick.start( ) を実行したとき、Thread クラスのコンストラクタの引数で指定されたオブジェクトが持っている run( ) メソッドを実行することになります。今回は Scroller クラス自身が run( ) メソッドを持っている(Runnable インタフェースを実装している)ので、コンストラクタの引数に this を書いたというわけです。

Scroller.java (完成版)

import java.awt.*;
import java.applet.Applet;

public class Scroller extends Applet implements Runnable {
    // 文字列表示位置の初期値
    private int strX = 300;

    public void start() {
        Thread kick = new Thread(this);
        kick.start();
    }

    public void run() {
        while(true) {
            // 文字列の描画
            repaint();
            // 描画位置を左にずらす
            strX = strX - 10;
            // 文字列が画面から消えたとき
            if (strX<-100) strX = 300;

            try {
                Thread.sleep(200);
            } catch (Exception e) { }
        }
    }

    public void paint(Graphics g) {
        g.drawString("Javaプログラミング",strX,30);
    }
}


  Scroller.html

<APPLET CODE="Scroller.class" width="300" height="50">
</APPLET>


 実行例 (画像をクリックすると実際のアプレットが起動します)




(4) 改良点

 これで文字が流れるプログラムが出来ましたが、もう少しなおしておく部分があります。今のままではアプレットが表示されるとスレッドの実行が始まりますが、他のページに行ってもスレッドの実行を行ったままになってしまいます。

 これを避けるために stop( ) メソッドを追加し、違うページに移ったときはスレッドの実行を止めておきます。

  Scroller.java (改良版)

import java.awt.*;
import java.applet.Applet;

public class Scroller extends Applet implements Runnable {
    // スレッドオブジェクト
    Thread kick;

    // 文字列表示位置の初期値
    private int strX = 300;

    public void start() {
        // スレッドオブジェクトが生成されていないとき
        // だけスレッドオブジェクトを作る
        if (kick==null) {
            kick = new Thread(this);
            kick.start();
        }
    }

    public void stop() {
        // スレッドがあれば終了させておく
        if (kick!=null) {
            kick = null;
        }  
    }

    public void run() {
        while(true) {
            // 文字列の描画
            repaint();
            // 描画位置を左にずらす
            strX = strX - 10;
            // 文字列が画面から消えたとき
            if (strX<-100) strX = 300;

            try {
                Thread.sleep(200);
            } catch (Exception e) { }
        }
    }

    public void paint(Graphics g) {
        g.drawString("Javaプログラミング",strX,30);
    }
}


前の章(21.Threadクラス)   次の章(23.synchronized修飾子)