アニメーションは基本的にパラパラ漫画と同じ原理で行います.
EZ Graphics では,EZ.addCircle
やEZ.addLine
などで
追加して返される実体の位置を変更し,EZ.refreshScreen()
を呼び出すと画面を更新します.
また,Javaで一定時間スリープするのは,Thread.sleep
メソッドを利用します. sleep
メソッドに
スリープする時間(ミリ秒)を渡します.
次の例では,100ミリ秒(0.1秒)スリープしています.
次の例で確認してみましょう.
import java.awt.Color;
public class RoundTrip{
void run(){
EZ.initialize(400, 400);
EZCircle circle = EZ.addCircle(
100, 100, 5, 5, Color.BLUE, true);
this.roundTrip(circle);
}
void roundTrip(EZCircle circle){
Integer deltaX = 10;
while(true){ // 無限ループ
Integer newX = circle.getXCenter() + deltaX;
circle.translateTo(newX, circle.getYCenter());
if(newX >= 400 || newX <= 0){
deltaX = deltaX * -1;
}
EZ.refreshScreen();
Thread.sleep(100);
}
}
}
これでコンパイルすると,次のようなコンパイルエラーが発生します.
prog/spring06/RoundTrip.java:17: エラー: 例外InterruptedExceptionは報告されません。スローするには、捕捉または宣言する必要があります
Thread.sleep(100);
^
エラー1個
例外(Exception)は,近年のプログラミング言語で採用されている実行時エラーの通知機構です.
従来のプログラミング言語,例えば,C言語の場合,fopen
で開くファイルが見つからなかった場合,
返り値をNULL
にすることでエラーを通知していました. この場合,プログラマが責任を持って,
返り値の値を確認して,正常か異常かを判断しなければいけませんでした.
一方の例外機構は,異常処理を行うための別の処理経路を作るものです. もし,プログラムの実行途中で何らかの異常が発生した時,それまでに行なっていた処理を中断し, 別の処理を行うようにする機構です.
C言語のようなエラー処理は上記のフローチャートのように,
プログラマ自身がfopen
の返り値を元に分岐処理によって,正常処理,異常処理を振り分ける必要があります.
対して,例外機構がサポートされている言語の場合,プログラマが異常,正常の分岐を明示的に書く必要はありません. 異常が起こった場合の処理の経路が決まっており,その経路に処理を書いておくことが異常処理を行うことになります. 正常処理の場合は,それまでの処理の続きにそのまま処理を書いていきます. これにより正常処理の見通しがよくなります.
そして,例外が投げられれば,何らかの異常が発生したと判断できるようになります. 逆に,例外が投げられなければ,データが変であろうが,正常な処理であると判断できます (もちろん,開発途中で変な場合はバグの可能性はありますが).
プログラマが明示的に投げる例外も存在しますが,多くの場合,システムが異常を検知し例外を投げます. 例えば,ファイルが見つからない場合,スリープ中に割り込みが入った場合などです. そのような例外が発生した時に,どのような対応をするのかをあらかじめ決めておく必要があります.
次のプログラムが,例外処理のイメージです.
void exceptionalMethod(){
// 例外が発生する可能性のある処理
someMethodCall();
// 例外が起こらなかった場合の処理
process();
}
例外が起こった場合は,exceptionalMethod
の呼び出し元に通知されます.
Javaの例外は,検査例外と非検査例外の2つに分類できます. 違いは次に挙げる通りです.
例外が発生した時の処理をプログラム中に明示的に書いておかなければコンパイルエラーになる例外. 完全に防ぐことが不可能な例外(実行時の状態によって発生しうる例外).
例えば,実行時エラーは,プログラムでどれほど十分にチェックしたとしても,完全に回避することはできません.
IOException
InterruptedException
例外が発生した時の処理は書かなくてもコンパイルが通る例外.
NullPointerException
ArrayIndexOutOfBoundsException
NumberFormatException
非検査例外は,事前にチェックすることで,例外の発生を抑えられます. 逆に言えば,実行時に非検査例外が投げられた場合は,事前のチェックが不十分であるとも言えます. 例えば,配列の範囲を超えてアクセスすることはプログラム中で確認することで避けられます.
さて,例外が発生した時の対処法をプログラム中に書いておく必要があります. 取れる対処法は2つです.
どちらがふさわしいかは場合により異なります. ここでは,呼び出し元に対応を任せましょう. そのために,呼び出し元に責任を丸投げするようにプログラム中に明示しておきましょう.
こうすることで,例外が発生した時はそのメソッドの呼び出し元に責任を転嫁し,正常処理のみに集中して処理を書くことができます.
本講義では,例外に対応する処理については省略します.
詳細を知りたい場合は,try-catch
について調べてみてください.
さて,コンパイルエラーで,InterruptedException
が投げられる可能性があると述べられています.
この例外は,スリープ中に割り込みが発生した時に発生する例外です.
ここでは,呼び出し元に責任を転嫁しましょう.
以下のような対応になります.
Thread.sleep
が例外を発生させた時,Thread.sleep
の呼び出し元であるroundTrip
に対応が任されます.これが例外が投げられた,ということです.roundTrip
の呼び出し元であるrun
に対応を任せましょう.run
でも,呼び出し元であるmain
に処理を任せる事にします.main
も呼び出し元に対応を任せましょう.呼び出し元に対応を任せるには,メソッドのシグネチャに throws
節を追加します.
throws
節は以下のように指定します.
以下のように書くことで,例外クラス
が発生した時,methodName
の呼び出し元に責任を転嫁することができるようになります.
public class ClassName{
void methodName() throws 例外クラス {
// 検査例外が発生する可能性のある処理
}
}
なお,複数の例外が発生する可能性のある場合,throws
節に例外クラスの名前をコンマ区切りで指定できます.
すなわち,メソッドの宣言部分を以下のように変更してください.
public class RoundTrip{
void run() throws InterruptedException{
// ... 省略
// roundTripで IntrruptedException が発生する可能性がある.
this.roundTrip(circle);
}
void roundTrip(EZCircle circle)
throws InterruptedException{
// .... 省略
// Thead.sleepの呼び出しで
// IntrruptedExceptionが発生する可能性がある.
Thread.sleep(100);
}
public static void main(String[] args) throws InterruptedException{
RoundTrip trip = new RoundTrip();
// runでIntrruptedExceptionが発生する可能性がある.
trip.run();
}
}
このようにプログラムを変更し,コンパイルしてみましょう.今度はコンパイルできたはずです. 次のような実行結果となるはずです.
このように,アニメーションを行うには,スリープが必要です. 一方,スリープを行うには,例外への対応が必要になります. 今後,ファイルの入出力を扱う時にも例外機構は必要になってきますので, どのような機構であるのか,しっかりと押さえておいてください.
スリープを行わないと,環境によっては目にも留まらぬ速さでアニメーションが繰り広げられます.
以下の実行結果になるよう,鉛直投げ上げ運動のアニメーションを作成してみましょう.
(アニメーションが途中で終わっているので変な挙動のように見えますが,バウンドし続ける動きになっています)
クラス名は Bound
としてください.
$y$方向の0が一番上,下方向がプラスになっていますので,通常の投げ上げとは方向が逆になっています(上方向に重力がかかっていると思ってください). 必要な式は次の通りです.