レッスン with Java言語プログラミングレッスン終了。

さて、未了項目のそうざらえも済んで、これで「レッスン with Java言語プログラミングレッスン」は終了となります。
すごいスピード!」との評をいただきましたが、本当に無茶なペースで、凄いスピードでやってきました。あっという間に一週間、でも上下巻に分かれたテキストを通して行いJavaを習得するのにたったの一週間。そうですね、きつくて、面白い一週間でした。
さて、実は今日から、Javaで仕事を始めています。正確に言えば、ちょっと前からJavaが必要な業務に回されていて、ようやく仕事を進め始めました。これは一からコードをくみ上げるようなものではなくて、専用の業務パッケージ・フレームワークがDB接続からGUIまで大枠を面倒みてくれ、それでは手の届かない部分をいじっていくようなお仕事です。
フレームワークが良くできていて、Javaが分からなくても何とかなるかも、ということなのですが、技術屋さんとしてはそれではつまらないので、突貫での勉強でした。今でも、まだアプリケーションが書けるというレベルではないのですが、フレームワークの生成したコードはなんとなく読めるようになっています。おかげで、ちょっと、仕事が楽しくなりました。
この感謝を結城さん、この書籍に関わった(きっと多くの)方々、そして販売と版数を支えてくれた先に勉強された先達、そうした皆様に。それから、これから勉強する人に幸ありますように!
それでは、レッスン with Java言語プログラミングレッスン、これにて一巻の終わりでございます。

もう一度String.replaceのドキュメントを読んでみる。

未了だった「String.replaceのドキュメントを読んでみる。」を再考してみます。分からなかったのは、ドキュメントには、

String replace(CharSequence target, CharSequence replacement) 
    リテラルターゲットシーケンスに一致するこの文字列の部分文字列を、指定されたリテラル置換シーケンスに置き換えます。 

とあるのに、実際には次のようにこの形のコードがコンパイル・エラーになった点でした。

      while ((line = reader.readLine()) != null) {
        line.replace("\t", "  ");
        System.out.println(line);
      }
E:\study\18>javac ShowFile1.java
ShowFile1.java:15: java.lang.String の replace(char,char) は (java.lang.String,j
ava.lang.String) に適用できません。
        line.replace("\t", "  ");
            ^
エラー 1 個

実際に、Java 1.5の環境でコンパイルしてみると、ちゃんとコンパイルできます。おやおやと思って、Java 1.4.2のドキュメントを探してみました。英語ですが、「replace(CharSequence target, CharSequence replacement)」というシンボルはなくて、replaceに関しては「replace(char oldChar, char newChar)」しかない、ということぐらいは確かめられます。
Java 1.5、Java言語プログラミングレッスンで大きく取り上げられているSystem.out.printfやジェネリクスの追加だけではなく、細かいところもいろいろ変わっているのですね。これに関しても、Java 1.5では使いやすくなったものだと思います。

もう一度参照遊び。

未了だった「参照遊び。」を再考してみます。分からなかったのは、この点でした。

気になるのは、「Rectangle@17590db」という表記。「Rectangleオブジェクト」で「メモリのロケーションが17590db」とか?

結局分からないままでしたが、でも一通りレッスンを終えて、「toString未作成のところで」文字列にキャストした時に何が行われるのかは考えられるようになりました。toStringをオーバーライドしていない状態ですから、Objectから継承しているtoStringが実行されるのですね。
Object.toStringメソッドのドキュメントを読んでみることにします。

オブジェクトの文字列表現を返します。通常、toString メソッドはこのオブジェクトを「テキストで表現する」文字列を返します。この結果は、人間が読める簡潔で有益な情報であるべきです。すべてのサブクラスで、このメソッドをオーバーライドすることをお勧めします。
Object クラスの toString メソッドは、オブジェクトの派生元のクラス名、アットマーク (@)、およびオブジェクトのハッシュコードの符号なし 16 進表現から構成される文字列を返します。つまり、このメソッドは次の値と等しい文字列を返します。

getClass().getName() + '@' + Integer.toHexString(hashCode())

「メモリのロケーション」ではないのですが、「等価なオブジェクトでは等しい値」で「できるかぎり(略)異なるオブジェクトについては異なる整数値」となるハッシュコードの16進表現、近いところはついているみたいです。でもこのハッシュコード、必ずしも「2 つのオブジェクトが等価でないとされた場合は、これらのオブジェクトに対して hashCode メソッドを呼び出したときに、結果が異なる整数値にならなくてもかまわない」ということらしくて、ちょっと微妙な感じですね。

もう一度傘を持って。

未了だった「傘を持って。」を再考してみます。せっかくなので、「確率の範囲外」という例外を作ってみました。

import java.io.*;

public class Kasa4 {
  public static void main(String[] args) {
    BufferedReader reader = new BufferedReader(new InputStreamReader(System.in));
    try {
      System.out.println("降水確率を入力してください。");
      String line = reader.readLine();
      int n = Integer.parseInt(line);
      /* 妥当な確率値か確認 */
      if (n < 0 || n > 100) throw new RatioOutOfBoundsException(n);
      /* 主処理 */
      System.out.println("降水確率は" + n + "%です。");
      if (n > 50) {
        System.out.println("傘を忘れずにね。");
      } else {
        System.out.println("傘は要りません。");
      }
      System.out.println("行ってらっしゃい。");
    } catch (IOException e) {
      System.out.println(e);
    } catch (NumberFormatException e) {
      System.out.println("数字の形式が正しくありません。");
    } catch (RatioOutOfBoundsException e) {
      System.out.println(e);
    }
  }
}

class RatioOutOfBoundsException extends Exception {
  public RatioOutOfBoundsException(int r) {
    super(r + "%は確率の範囲(0〜100%)外です。");
  }
}
E:\study\04>javac Kasa4.java
E:\study\04>java Kasa4
降水確率を入力してください。
-20
RatioOutOfBoundsException: -20%は確率の範囲(0〜100%)外です。

E:\study\04>java Kasa4
降水確率を入力してください。
120
RatioOutOfBoundsException: 120%は確率の範囲(0〜100%)外です。

E:\study\04>java Kasa4
降水確率を入力してください。
100
降水確率は100%です。
傘を忘れずにね。
行ってらっしゃい。

しかし、mainを終わらせてしまっているという点はまったく変わっていませんね。解決したのか、そうでないのか、ちょっと微妙なところです。

"&"と"&&"と"and"をもう一度。

未了だった「"&"と"&&"と"and"。」を再考してみます。実はif文を覚えた直後にもちょっと書き直してみたのですが、こんなコンパイル・エラーになって、解決できずにいました。

E:\study\02>javac testAndOr.java
testAndOr.java:5: ここで 'void' 型を使用することはできません。
        if (false & System.out.print("false & print\n")) {}
            ^

「なんでfalseが'void' 型なんだろう」と悩んだのですが、違いますね。System.out.printメソッドがvoid型で、'&'でつないでいるからその前にマークが行ってしまっているわけです。
修正したコードと実行結果はこんな感じ。

public class testAndOr {
  public static void main(String[] args) {
    boolPrint("[and]\n");
    if (false & boolPrint("false & print\n")) {}
    if (false && boolPrint("false && print\n")) {}
    boolPrint("[or]\n");
    if (true | boolPrint("true | print\n")) {}
    if (true || boolPrint("true || print\n")) {}
  }
  static boolean boolPrint (String s) {
    System.out.printf(s);
    return true;
  }
}
E:\study\02>javac testAndOr.java
E:\study\02>java testAndOr
[and]
false & print
[or]
true | print

「false && 出力文」や「true || 出力文」では、予想通り、出力文は実行されていないようです。

下巻終了。

下巻の付録まで、ゆっくりゆっくり読み終わりました。「上巻終了、下巻へ。」で「昨夜で上巻の資料まで読み終え、今日から下巻、章でいえば11章以降に入ります」と書いたのが3日目の朝、今が6日目の夜ですから、上巻は2日で終え、下巻は4日かけたということですね。
下巻はいよいよJavaらしい機構が、わんさかと登場しました。クラスとインスタンススーパークラスとサブクラス、インターフェースとインターフェースの多重継承。Threadクラスの継承によるマルチスレッドとRunnableインターフェースの継承によるマルチスレッド。例外、ガーベッジコレクション、パッケージ。ファイル入出力とコレクション。
一章一章が手ごわいのですが、超特急で駆け抜ける旅でも観光バスで行く点から点への旅でもなく、「Java言語プログラミングレッスン」では各駅停車、一駅一駅と駅とその区間を楽しむゆったりした旅でした。各駅で降りて、余計なコードをかいてみたり一駅入れたりしながら、気がつけばお遍路を終えていたみたいな感じです。
そう言えば、僕がこの本で勉強しようと思ったのは、ある人が「会社で指定されたJavの本がすごく難しくて...でもこの本は分かりやすかったですよ!」と言っていたからでした。実はその「すごく難しい」本も手元にあるので、目次を紹介しましょう。

順番が全然違いますね。おそらく、Javaの(そして他のオブジェクト指向言語の)学習の難しいところは、最初から「public class xxx」などといういかにもな記述と向き合わなければいけないところだと思います。多くの書籍は、最初にここを解説しようとして、「クラスとインスタンスとは」「継承が」「カプセル化により」といった説明から入ります。
Java言語プログラミングレッスンでは、こうしたところはひとまず「こう書くものだ」と保留してしまいます。代わりに、まずコンパイルと実行方法、次に数値や文字、文字列といった変数、if文やswitch文、for文、while分といった制御構造、配列を教えます。何度も手を動かし、繰り返しレッスンし、その時点ではJavaの体系的な理解じゃないかもしれないけど、一部分でも「僕でもできる」という成功体験を積ませてくれます。
それが上巻から下巻へと続き、なんと19章に至り、振り返ってみると、「すごく難しい」本の「入出力」までの項目は、全てマスターしています。そう、「読んだ」だけでもなく、「知っている」だけでもなく、しかも「覚えている」にもとどまらない、確かにそのレベルまでのコードはもう書けるという自信がついています。「習得した」と言う段階ですね。とても良い教科書だと思います。
さあ、一通り、全ての章を終えました。あとは未了の各エントリを見直し、また上限を通して各章を見直して、グランドフィナーレといきたいと思います。