[ev3×Java]   インターフェースと実装と継承(イベント処理)

この記事では、ロボットを使ってインターフェースという機能を主に見ていきます。


#目次
0.インターフェースとは
1.コードの構造
　1-0.インターフェース内で処理を実装するサンプルコード
　1-1.クラスをつくってインターフェースを実装するサンプルコード
2.インターフェースTimerListnenerと実装クラスTimerのサンプルコード
3.leJOSにおけるモーターのクラスとインターフェースの関係


#0.インターフェースとは

インターフェースは**メソッドの仕様のみ**を定義するクラスのようなものです。
インターフェースの中にはメソッドが並べられていますが、その**処理**は書いてありません。

なのでメソッドを見たときそれが**何をするものなのか**はわかりますが、**実際にどういった処理をするものなのか**はわかりません。
メソッドの処理の内容は、**インターフェースを実装するクラス**の中を見ればわかります。

---

Point　:　**インターフェース**
メソッドの仕様（戻り値とメソッド名、引数）のみを定義する
実際には処理をしない

---

Point　:　**インターフェースを実装するクラス**
メソッドの処理の内容を定義する
処理を実際に行う

---

[参考記事][インターフェースを実装！Javaでimplementsを使う方法](https://techacademy.jp/magazine/17414)
※ここに書いてあるように、**インターフェースでメソッドの処理を実装すること**もあります。




---

#1.コードの構造

##1-0.インターフェース内で処理を実装するサンプルコード
まずは、以下のコードをみてみましょう。
モーター回転開始時と終了時に文字を表示するプログラムです。


```MotorEvent.java
import lejos.hardware.motor.EV3LargeRegulatedMotor;
import lejos.hardware.port.MotorPort;
import lejos.robotics.RegulatedMotor;
import lejos.robotics.RegulatedMotorListener;
import lejos.utility.Delay;

public class MotorEvent{
	
	//インスタンスの生成
	//RegulatedMotor:インターフェース
	//EV3LargeRegulatedMotor:インターフェースを実装するクラス
	private static RegulatedMotor l_a = new EV3LargeRegulatedMotor(MotorPort.A);
	
	public static void main(String[] args) {
		
		//インスタンスの生成
		//インターフェースRegulatedMotorListenerの中のメソッドをカスタマイズして処理を定義する
		final RegulatedMotorListener listener = new RegulatedMotorListener() {
			
			//インターフェースRegulatedMotorListenerがもつ以下の2つのメソッドをカスタマイズする
			
			//モーターが回転を開始した時に呼び出されるメソッドをカスタマイズする
			@Override
			public void rotationStarted(
					final RegulatedMotor motor,
					final int tachoCount,
					final boolean stalled,
					final long timeStamp) {
				//カスタマイズする内容
				System.out.println("Started");
				Delay.msDelay(500);
			}
			//モーターが回転を停止した時に呼び出されるメソッドをカスタマイズする
			@Override
			public void rotationStopped(
					final RegulatedMotor motor,
					final int tachoCount,
					final boolean stalled,
					final long timeStamp) {
				//カスタマイズする内容
				System.out.println("Stopped");
				Delay.msDelay(500);

			}
				
		};
		
		//addListenerメソッドはRegulatedMotorListenerのインスタンスを引数にとる
		//モータに何かしらの動きがあれば、listenerに伝えられる
		//そしてlistenerの中のメソッドが呼び出される
		l_a.addListener(listener);
		l_a.forward();
		Delay.msDelay(2000);
		l_a.stop();		
		Delay.msDelay(500);
		
	}
}
```

---

###サンプルコードの概要

1.モーターの回転開始時と終了時に文字を表示するために**addlistenerメソッドを使いたい**。
※void addListener（RegulatedMotorListener  listener）
ローテーションが開始または停止したときに通知されるリスナーオブジェクトを追加します


2.addlistenerメソッドはクラスEV3LargeRegulatedMotorで実装されているので、そこから**参照型変数l_aをつくってl_a.addlistenerという形で使用**する。

3.また、addlistenerメソッドは、引数に**インターフェースRegulatedMotorListenerのインスタンスをとる**ので、まずその**インターフェースからインスタンスをつくる必要**がある。

4.しかし、インターフェースは**メソッドの処理を定義していない**ので、インスタンスをつくったとしても**実際に処理を行うことができない**。
そこで、**インターフェースの中にあるメソッドをカスタマイズして処理を定義**し、処理を行うことができるようにする。


---

###サンプルコードの整理

◯インターフェース　:　**RegulatedMotor**
実装クラス:EV3LargeRegulatedMotor

◯インターフェース　:　**RegulatedMotorListener**
実装クラス:なし(インターフェース内で実装)

◯メソッド　:　**rotationStarted()**
RegulatedMotorListenerの中で仕様のみが定義されている
モーターの回転開始時に呼び出されるメソッド

◯メソッド　:　**rotationStopped()**
RegulatedMotorListenerの中で仕様のみが定義されている
モーターの回転終了時に呼び出されるメソッド

◯メソッド　:　**addlistener()**
void addListener(RegulatedMotorListener listener)
**インターフェースから生成されたオブジェクト**を引数にとる。インターフェースRegulatedMotorで仕様が定義され、EV3LargeRegulatedMotorクラスで実装されている。



---
##1-0.クラスをつくってインターフェースを実装するサンプルコード

◯インターフェースRegulatedMotorListenerを実装するために自分でRotateStateEventというクラスをつくります。
そのなかでRegulatedMotorListenerのなかにあるメソッドをカスタマイズして、処理を定義します。

```MotorEvent02.java
import lejos.hardware.motor.EV3LargeRegulatedMotor;
import lejos.hardware.port.MotorPort;
import lejos.robotics.RegulatedMotor;
import lejos.robotics.RegulatedMotorListener;

public class MotorEvent02 {

    private static RegulatedMotor l_a = new EV3LargeRegulatedMotor(MotorPort.A);

    public static void main(String[] args) {
        //クラスRotateStateEventのインスタンスを引数にする
        l_a.addListener(new RotateStateEvent());
        l_a.forward();
        Delay.msDelay(2000);
        l_a.stop();     
        Delay.msDelay(500);
    }
}

//インターフェースRegulatedMotorListenerを実装するクラスをつくる
class RotateStateEvent implements RegulatedMotorListener{
    @Override
    public void rotationStarted(
                    final RegulatedMotor motor,
                    final int tachoCount,
                    final boolean stalled,
                    final long timeStamp) {
                //処理を定義する
                System.out.println("Started");
                Delay.msDelay(500);
            }

    @Override
    public void rotationStopped(
                    final RegulatedMotor motor,
                    final int tachoCount,
                    final boolean stalled,
                    final long timeStamp) {
                //処理を定義する
                System.out.println("Stopped");
                Delay.msDelay(500);
                }
}


```

---

Point　:　implements

``クラス　implements インターフェース{}``という形で、インターフェースを実装するクラスとして定義することができます。

---




#2.インターフェースTimerListnenerと実装クラスTimerのサンプルコード

◯Timerクラスを扱うためにSecondCounterクラスを自分でつくります。
Timerクラスは、TimerListenerインターフェース(を実装しているクラス)のインスタンスを引数にとります。
そして、**指定した周期ごと**に、TimerListenerインターフェース(を実装しているクラス)の中にある**timedOutメソッド(=処理)を呼び出します**。

今回は1秒おきにカウントを数え、それを表示し、5秒おきに音を鳴らすプログラムにしてあります。


```.java
import lejos.utility.Timer;
import lejos.hardware.Sound;
import lejos.utility.TimerListener;

public class SecondCounter {
    private int second;

    // 1000ミリ秒ごとに割り込みを発生させる
    private Timer timer = new Timer(1000, new CountTimer());

    public void reset() {
        second = 0;
    }

    public int getSecond() {
        return second;
    }

    public void start() {
        timer.start();
    }

    public void stop() {
        timer.stop();
    }
    //インターフェースTimerListenerを実装するCountTimerクラスをつくる
    class CountTimer implements TimerListener {
        // 1000ミリ秒ごとにtimedOut()メソッドが、Timerから呼ばれる
        //インターフェースの中にあるメソッドの処理内容をカスタマイズして定義する
        @Override
        public void timedOut() {
            //1ずつ加算する
            second++;
            System.out.print(second);
            //5秒おきに音を鳴らす
            if(second % 5 == 0 ) {
            	Sound.beep();
            }
        }
    }
}
```



```.java
import lejos.hardware.Button;
import lejos.hardware.motor.EV3LargeRegulatedMotor;
import lejos.hardware.port.MotorPort;
import lejos.robotics.RegulatedMotor;
import lejos.utility.Delay;

public class SecondCounterSample {
	
    private static RegulatedMotor l_a = new EV3LargeRegulatedMotor(MotorPort.A);

    public static void main(String[] args) {
        
        ////上で定義したクラスSecondCounterのインスタンスの生成
        SecondCounter counter = new SecondCounter();
        //カウントを開始する
        counter.start();

        //20回処理を繰り返す
        for (int i = 0; i < 20; i++) {
        	
            if(i == 10) {
            	//タイマーをリセット
            	System.out.print("reset ");
            	counter.reset();
            }else if(i > 15) {
            	//徐行運転
            	System.out.print("slowdown ");
            	l_a.setSpeed(100);
            }else {
            	//加速する
            	//カウント*100にスピードを設定する
            	l_a.setSpeed(counter.getSecond() * 100);
            }
            l_a.forward();
            Delay.msDelay(1000);
        }
        l_a.stop();
        counter.stop(); 

        System.out.println("push any button");
        Button.waitForAnyPress();
    }

}
```



Point　:　**public Timer(int theDelay,TimerListener el)**
周期とTimerListenerクラス(を実装するクラス)のインスタンスを引数にとる。

Point　:　**void timedOut()**
Called every time the Timer fires.
Timerが発火する時に毎回呼ばれる

























#3.leJOSにおけるモーターのクラスとインターフェースの関係

モーターに関するクラスとインターフェースの関係性についてみていきます。

・**クラスBaseRegulatedMotor**
・**クラスEV3LargeRegulatedMotor**
・**インターフェースRegulatedMotor**
・**インターフェースBaseMotor**

モーターの一番大きな集合として、クラスBaseRegulatedMotorがあり、それをEV3LargeRegulatedMotorは継承しています。
インターフェースRegulatedMotorは、これら2つのクラスに実装されています。
また、インターフェースBaseMotorを継承しています。


---


###**クラスBaseRegulatedMotor**

◯以下のインターフェースを実装している
BaseMotor,RegulatedMotor...

◯サブクラス
EV3LargeRegulatedMotor, EV3MediumRegulatedMotor...

```BaseRegulatedMotor.java
//BaseRegulatedMotorクラスの一部を抜粋
public abstract class BaseRegulatedMotor extends Device implements RegulatedMotor{

    public void forward()
    {
        reg.newMove(speed, acceleration, +NO_LIMIT, true, false);
    }

    public void rotate(int angle, boolean immediateReturn)
    {
        rotateTo(Math.round(reg.getPosition()) + angle, immediateReturn);
    }

    public void addListener(RegulatedMotorListener listener)
    {
        reg.addListener(this, listener);
    }
    
    public RegulatedMotorListener removeListener() 
    {
        return reg.removeListener();
    }

    ...

}    
```
---

Point　:　**継承**
extendsを使ってクラスの継承を行うことができます。

[参考記事][extendsキーワード](https://java-code.jp/112)

---

Point　:　**implements**

``クラス　implements インターフェース{}``という形で、インターフェースを実装するクラスとして定義することができます。


---






###**クラスEV3LargeRegulatedMotor**

◯以下のインターフェースを実装している
インターフェース:BaseMotor, Encoder, RegulatedMotor, Tachometer

```EV3LargeRegulatedMotor.java
//BaseRegulatedMotorクラスを継承するEV3LargeRegulatedMotorクラス
public class EV3LargeRegulatedMotor extends BaseRegulatedMotor
{
    public EV3LargeRegulatedMotor(Port port)
    {
        super(port, null, EV3SensorConstants.TYPE_NEWTACHO, MOVE_P, MOVE_I, MOVE_D,
                HOLD_P, HOLD_I, HOLD_D, OFFSET, MAX_SPEED);
    }

    ...
}
```









---

###**インターフェースRegulatedMotor**

◯以下のクラスで実装されている
BaseRegulatedMotor, EV3LargeRegulatedMotor, EV3MediumRegulatedMotor...

◯インターフェースBaseMotorを継承している

```.java
//インターフェースRegulatedMotorの一部を抜粋
public interface RegulatedMotor extends BaseMotor, Tachometer, Closeable {

	public void addListener(RegulatedMotorListener listener);

    public RegulatedMotorListener removeListener();

    void rotate(int angle, boolean immediateReturn);

    public void stop(boolean immediateReturn);

    ...

```



---

###**インターフェースBaseMotor**

◯インターフェースRegulatedMotorから継承されているインターフェース

```BaseMotor.java
public interface BaseMotor {

	  void forward();

	  void backward();

	  void stop();
}
```

---


#最後に

読んでいただきありがとうございました。
間違いなど改善すべき点などございましたら教えていただけると幸いです。