問題2-17
自動車を表すCarクラスと、ガス欠を表すGasolineException例外クラスを作成してください。
また、Carクラスを使用するOriginalExceptionPracticeクラスのmainメソッドはすでに完成しています。
(変更は禁止です)
【GasolineException例外クラス】
このクラスは検査例外なので、Exceptionクラスを継承します。
String型の引数を1つ持つコンストラクタを定義します。
スーパークラスであるExceptionクラスにも同じシグネチャのコンストラクタが存在するので、引数で受け取ったデータをスーパークラスのコンストラクタに渡します。
【Carクラス】
Carクラスには3つのインスタンスフィールドがあります。
・ナンバーを表すnumberインスタンスフィールド(int型)
・車種を表すtypeインスタンスフィールド(String型)
・ガソリン量を表すgasインスタンスフィールド(double型)引数を3つ受け取るコンストラクタを定義します。
その受け取った3つの値は、そのまま3つのインスタンスフィールドに渡します。
(mainメソッドの記述を参考にしてください)車の走行を表すrunインスタンスメソッド(引数・戻り値なし)を定義します。
runメソッドはガス欠を表現するGasolineException例外をスローする可能性があるので、throwsキーワードを用いて指定してください。
メソッドの最初の処理として、ガソリン量をチェックします。
ガソリン量が0.1リットル未満の場合は、ガス欠例外クラスをインスタンス化し、スローします。
インスタンス化するとき、エラーメッセージをコンストラクタの引数として渡します。
(エラーメッセージは下記実行結果を参考)
ガソリン量が0.1リットル以上の場合は走行メッセージを表示し、ガソリン量を0.1リットル分減らします。
(走行メッセージは下記実行結果を参考)
【実行結果】
ナンバー123のロミオは走行しています。 ナンバー123のロミオは走行しています。 ナンバー123のロミオは走行しています。 ナンバー123のロミオは走行しています。 ナンバー123のロミオは走行しています。 ナンバー123のロミオは走行しています。 ナンバー123のロミオは走行しています。 ナンバー123のロミオは走行しています。 ナンバー123のロミオはガソリン不足のため走行できません。
【OriginalExceptionPractice.java】
public class OriginalExceptionPractice { public static void main(String[] args) { // 自動車オブジェクトの生成 Car mycar = new Car(123, "ロミオ", 0.8); // ガソリンがなくなるまで走行する try { while(true) { // 走行する mycar.run(); } } catch(GasolineException e) { System.out.println(e.getMessage()); } } } // ここに車クラスを作成してください // ここにガス欠例外クラスを作成してください
解答例
【OriginalExceptionPractice.java】
public class OriginalExceptionPractice { public static void main(String[] args) { // 自動車オブジェクトの生成 Car mycar = new Car(123, "ロミオ", 0.8); // ガソリンがなくなるまで走行する try { while(true) { // 走行する mycar.run(); } } catch(GasolineException e) { System.out.println(e.getMessage()); } } } // 車クラス class Car { private int number; // ナンバー private String type; // 車種 private double gas; // ガソリン量 // コンストラクタ public Car(int number, String type, double gas) { this.number = number; this.type = type; this.gas = gas; } // 走行させる public void run() throws GasolineException { // ガソリン量チェック if(gas < 0.1) { // ガス欠例外のスロー throw new GasolineException( "ナンバー" + number + "の" + type + "はガソリン不足のため走行できません。"); } else { // 走行メッセージの表示 System.out.println("ナンバー" + number + "の" + type + "は走行しています。"); // ガソリンを0.1リットル減らす gas -= 0.1; } } } // ガス欠例外クラス class GasolineException extends Exception { public GasolineException(String message) { super(message); } }
解説
Javaではいろんな種類の例外が、すでにクラスとして定義されています。
そして、例外クラスを継承することで、自由に例外クラスを定義することも可能です。
さらに、例外クラスを自由に作れるということは、好きなタイミングで例外をスローすることもできるわけです。
今回の問題は、それをテーマにした問題です。
Javaの例外には、例外処理が必須の「検査例外」と、例外処理が任意の「非検査例外」の2種類あります。
もし自分で作りたい例外が「検査例外」の場合は「Exception」クラスを、「非検査例外」の場合は「RuntimeException」クラスを継承してください。
今回の問題では、車のガス欠というトラブルを表現する例外クラスを「検査例外」として作成しました。
// ガス欠例外クラス class GasolineException extends Exception { public GasolineException(String message) { super(message); } }
Exceptionクラスにはコンストラクタが5つオーバーロードされているのですが、String型の引数を1つだけ持つコンストラクタを用いて詳細メッセージを格納することが可能です。
もちろんこの詳細メッセージは、Exceptionクラスの持つgetMessageメソッドを用いて後から取り出すことが可能です。
例外クラスをインスタンス化してそのあとスローするわけですが、それにはキーワード「throw」を使用します。
// ガス欠例外のスロー throw new GasolineException( "ナンバー" + number + "の" + type + "はガソリン不足のため走行できません。");
検査例外をスローする記述をした瞬間、例外処理が義務付けられます。
でも、わざわざ例外をスローしておいて、自分でtry-catchするのは変ですね。
ですので、そのメソッド定義の後ろにキーワード「throws」を付けて、例外処理をメソッドを呼び出した側に委ねます。
キーワード「thow」と「throws」は全く違うので注意してください。
// 走行させる public void run() throws GasolineException { .... }
この「throws」の記述がないと、コンパイルエラーになります。
また、「throws」の後には複数の例外クラスをカンマ区切りで記述することも可能です。
その他、例外とは直接関係ないのですが、車の走行を表現するrunメソッドを呼び出すたびにガソリンをきちんと0.1リットル減らすのを忘れないようにしてください。
もしガソリンが減らない夢の車(?)を作ってしまうと、ガス欠例外をスローしないのでmainメソッドのwhile文はいつまでたっても終了しないので無限ループになります。
ステート(状態)が変化するタイプのオブジェクトは、バグが混入しやすいので注意が必要です。
参考図書
LINE公式アカウント
仕事が辛くてたまらない人生が、仕事が楽しくてたまらない人生に変わります。
【登録いただいた人全員に、無料キャリア相談プレゼント中!】