Loading [MathJax]/jax/output/HTML-CSS/jax.js

最終課題に向けて

ここに示した内容はこれまでに提出された課題を見て,説明が必要であろうと思われる部分を抜粋しました.

Visual Studio Code で表示されるラベルについて

Visual Studio Codeなどの昨今の IDE (Integrated Development Environment; 統合開発環境) では, EoD (Ease of Development) のため実引数に仮引数の名前を表示する機能があります. 以下に例を図示します.

Visual Studio Codeスクリーンショット

この図中の5, 7行目と12行目にそれぞれ,prefixformat という文字列が見えます. これは IDE の機能により表示されているラベルです. 実際にプログラム中にこのラベルを書くとコンパイルエラーになるため,注意してください.

インデントを揃える.

インデントをしっかりと揃える必要がありますが,手作業でインデントを揃えないようにしましょう. 手作業でインデントすると漏れや間違いが起こる可能性があるためです. 利用しているエディタの一括インデントを行ってください.

Visual Studio Code の場合,Option+Shift+F で一括インデントが行えます.

コンパイル&実行結果を確認する.

提出された練習問題を見てみるとごく単純なコンパイルエラーが残っているケースが見られます. どんなに面倒でも一度コンパイルしてください. そして,コンパイルできたら実行して,結果を確認してください. その際,どのような入力により,どのような結果が期待されるのかを確認してから実行すると良いでしょう.

以下のような場合は,速やかにTA,教員に相談してください. 授業中に挙手する他に,Teams でのチャットでも質問対応しております. 相談により成績が下がることはありませんが,相談せずに未完成のまま提出することは 結果的に成績の低下に繋がります.

スコープについて

変数には有効範囲があります.この有効範囲のことを スコープ(scope) と呼びます. プログラム中の {} で囲まれた範囲を ブロック(block) と呼びます. スコープは変数が宣言された後,宣言されたブロックの中でのみ有効です.

メソッドの呼び出しについて

メソッドを呼び出すには,何らかの実体に対して呼び出す必要があります. この実体のことを レシーバ(receiver) と呼びます.

例えば,以下のフィボナッチ数列のn番目の値を求めるプログラムFibonacciで考えてみましょう.

public class Fibonacci {
    void run(String[] args) {
        Integer index = 10;
        if(args.length != 0) 
            index = Integer.parseInt(args[0]);
        Integer result = fibonacci(index);
    }
    Integer fibonacci(Integer index) {
        if(index < 2)
            return 1;
        return fibonacci(index - 1) + fibonacci(index - 2);
    }
    public static void main(String[] args) {
        HelloWorld app = new HelloWorld();
        app.run(args);
    }
}

mainメソッド内で,app.run(args) というメソッド呼び出しを行なっています. この呼び出しのappがレシーバ,argsが実引数(arguments),runがメソッド名です.

メソッド呼び出し部分の各名称

runメソッド内やfibonacciメソッド内の fibonacciメソッドの呼び出しには レシーバがないように見えます. これは実はthisというレシーバが隠されており,自分自身を表しています.

次に,8行目のfibonacciメソッドの宣言に注目してください. この行の最初の Integer は返り値の型であり,このメソッドの最後にこの型の値を return する必要があります. fibonacciはメソッド名,括弧内のindexは仮引数(parameter)と呼びます. 波括弧で囲まれた部分はメソッドボディやメソッドの中身と呼ばれ,そのメソッドが行う処理が書かれています.

メソッド定義部分の各名称

メソッド分割について

メソッドは細かく分割しましょう. メソッドを分割するのは,処理に名前をつけるために行います. 処理に名前が適切に付けられていると,処理内容を理解しなくても何が行われるのかを理解できます. つまり,読みやすくなるのです.

適切な名前のためには,命名規則に従うことも重要でしょう. Javaの場合,メソッド名は動詞から始まり,キャメルケースで命名することが推奨されています. このことを意識して適切なメソッド名をつけてみましょう.

最初は適切な名前をつけるのは難しいかもしれません. そのような場合,日本語(ローマ字)で付けるのも良いでしょう.

一方でメソッドを分けるときに,次のようなメソッドの中で他のメソッドを呼び出しているだけのメソッドには分割の意味はありません.

public class SomeClass {
    void run(String[] args) {
        perform(args);
    }
    void perform(String[] args) {
        // 何らかの処理
    }
    // mainメソッドは省略.
}

エラーについて

コンパイルエラーや実行時エラーの場合,エラーメッセージと発生した場所をしっかりと確認しましょう.

コンパイルエラーについて

コンパイルエラーは次のようにエラーの内容とエラーの場所を示してくれます. この例の場合,Fibonacci.java の 18 行目で,「シンボルを見つけられない」というコンパイルエラーです. シンボルを見つけられないというエラーは,典型的には綴りを間違えています. その変数の名前をしっかりと確認しましょう.

Fibonacci.java:18: エラー: シンボルを見つけられません
                prev2 = reslut; // result を代入して,次の i に備える.
                        ^
  シンボル:   変数 reslut
  場所: クラス Fibonacci
エラー1個

また,典型的なコンパイルエラーとその原因は次の通りです. 指摘された箇所を失火路と確認して修正していきましょう.

以下の内容も併せてご覧ください.

実行時エラーについて

実行時に何らかのエラーが起こった場合,次のようなメッセージが出力されます. このようなメッセージのことをスタックトレース(stack trace)と呼びます.

Exception in thread "main" java.lang.ArrayIndexOutOfBoundsException: Index 0 out of bounds for length 0
	at FizzBuzz.run(FizzBuzz.java:5)
	at FizzBuzz.main(FizzBuzz.java:25)

1行目にどのようなエラーであるか,2行目以降にプログラムのどこで発生したかが書かれています. どのようなエラーなのかは,thread "main" の後ろを見てみましょう. ここには,エラーの原因となった例外のクラス名が書かれています. 上の例の場合,ArrayIndexOutOfBoundsException,すなわち,array index out of bounds exception です. これは,配列の範囲を超えて要素にアクセスしようとした場合に発生する例外です. 英語の意味がわからない場合,Google 翻訳や DeepL 翻訳にかけてみましょう.

2行目以降は,クラス名.メソッド名(ソースファイル名:行番号) のように出力されています. 表示された場所と例外クラスの名前を手掛かりに実行時エラーが起こらないよう修正しましょう.

スタックトレースとは,実行時に例外が発生したときに表示されるエラーメッセージです. 上で示したように,どこでどのような例外が発生したのかを把握する手掛かりとなる重要なメッセージです.

そして,例外が発生してスタックトレースを出力するときになると, 例外が発生した箇所はどのような経緯で呼び出されたのかを辿ってmainメソッドまで到達しようとします. メソッドの呼び出しはスタックで管理されていますので,スタックを辿っていくわけです. このことからスタックトレースと呼ばれています.

スタックトレースは Java に限ったものではなく,例外機構を導入しているプログラム言語であれば 似たような出力が行われます.

例外の責任転嫁について

ファイルの入出力時に発生する可能性のある IOException などの検査例外 と呼ばれる例外は, 例外が発生したときの対応をプログラム中に明示しておかなければコンパイルできないようになっています. 対応方法は以下の2種類です.

この講義では,呼び出し元に責任を転嫁します. 責任を転嫁するには,例外が発生する可能性のあるメソッドのシグネチャに throws 節を追加します.

public class Cat {
    void run(String[] args) throws IOException {
        for(String arg: args) {
            performEach(arg);
        }
    }
    void performEach(String arg) throws IOException {
        BufferedReader in = new BufferedReader(new FileReader(arg));
        String line;
        while((line = in.readLine()) != null)
            System.out.println(line);
        in.close();
    }
    public static void main(String[] args) throws IOException {
        SomeClass app = new SomeClass();
        app.run(args);
    }
}

こうすることで,performEach 内で IOExceptionが発生した場合,呼び出し元である run メソッドに対応を任せます. しかし,run メソッドも同様に throws 節が宣言されており,呼び出し元である main メソッドに対応を任せています. 同様に main メソッドも throws 節があるため,呼び出し元に責任転嫁しています. mainメソッドの呼び出し元とは,javaコマンドです. もし,例外が発生した場合,javaコマンドまで例外が伝播し,そこでスタックトレースが出力されてプログラムが終了することになります.