■■■■■■■■■■■■■■■■■■■■■■■■■■■■■ 2009年11月15日 Java総合講座 - 初心者から達人へのパスポート vol.179 セルゲイ・ランダウ バックナンバー: http://www.flsi.co.jp/Java_text/ ■■■■■■■■■■■■■■■■■■■■■■■■■■■■■ ------------------------------------------------------- ・このメールマガジンは、画面を最大化して見てください。 小さな画面で見ていると、不適切な位置で行が切れてしまう など、問題を起すことがあります。 ・このメールマガジンに掲載されているソース・コード及び 文章は特に断らない限り、すべて筆者が著作権を所有してい ます。また、これらのソース・コードは学習用のためだけに 提供しているものです。 ------------------------------------------------------- ======================================================== ◆ 01.ログ出力のための技術 ======================================================== さて、これからStrutsを使ったWebアプリケーション開発のお話をしていきますが、 その前に準備として、ログ出力に係わる技術(technique)を一つ紹介しておき ます。 vol.159にも書いたように、ログにはメソッドを呼び出したときの引数の値や戻り値 のデータも書き出しておくべきですが、その引数や戻り値が複雑な構造を持つクラス の場合は、その中のデータをログに書き出させるための処理をプログラミングする のは面倒になります。 それを、統一した形式で簡単にプログラミングできるようにする方法がありますので、 紹介しておきましょう。 一番簡単な方法は、Apache Commons(以前はJakarta Commonsと呼ばれていた)と呼ば れるAPIをインストールして、開発する各クラスに次のようなtoString()メソッドを 追加することです。 -------------------------------------------------------- public String toString() { return ReflectionToStringBuilder.toString(this); } -------------------------------------------------------- または、 -------------------------------------------------------- public String toString() { return ReflectionToStringBuilder.toString(this, ToStringStyle.MULTI_LINE_STYLE); } -------------------------------------------------------- Apache Commons(後で詳しく説明します)のLangというコンポーネントの中に上記の ReflectionToStringBuilderというクラスが含まれているのですが、このクラスの toString()メソッドは、引数に指定されたオブジェクトの各フィールドをJavaの リフレクションという機能を使って調べ出して返してくれます。 このtoString()メソッドの第二引数には戻り値の出力のスタイルを指定できるので すが、以下のようなスタイルがあります。 [ ToStringStyle.DEFAULT_STYLE ] これはデフォルトのスタイルで、戻り値の出力が下記のような表示になります。 jp.co.flsi.human.Person@182f0db[name=吉田茂,age=51,smoker=true] ┌補足─────────────────────────┐ この表示は、 オブジェクトのクラス名@オブジェクトのハッシュコード[フィー ルド名=その値,フィールド名=その値,・・・] という形式になっています。ここで、オブジェクトのハッシュ コードというのは、オブジェクトを識別できるコードで、例えば 同じクラスの複数のインスタンスがあった場合、ハッシュコー ドによって、インスタンスを区別できます。(ただし、場合に よっては、異なるインスタンスが同じハッシュコードを持つこと もありえます。) オブジェクトのハッシュコードはObjectクラスのhashCode() メソッドによって実装されていますが、自分でプログラミング することもできます。本来オブジェクトのハッシュコードはint 型ですが、上のtoString()メソッドでは16進数表示されます。 └───────────────────────────┘ [ ToStringStyle.MULTI_LINE_STYLE ] これは複数行で表示するスタイルで下記のような感じになります。 (これは、上記のソース・コード例で提示したものです。このスタイルが 一番見やすいと思います。) jp.co.flsi.human.Person@182f0db[ name=吉田茂 age=51 smoker=true ] [ ToStringStyle.NO_FIELD_NAMES_STYLE ] フィールド名を出力しないスタイルで下記のような感じになります。 jp.co.flsi.human.Person@182f0db[吉田茂,51,true] [ ToStringStyle.SHORT_PREFIX_STYLE ] オブジェクトのハッシュコードを出力しないスタイルで下記のような感じになります。 jp.co.flsi.human.Person[name=吉田茂,age=51,smoker=true] [ ToStringStyle.SIMPLE_STYLE ] 一番簡単なスタイルで下記のような感じになります。 吉田茂,51,true なお、toString()メソッドは、すべてのクラスのスーパークラスであるObjectに備わ っているメソッドであり、上記のようなメソッドを追加することはObjectクラスの toString()メソッドをオーバーライド(上書き)することを意味します。 ObjectクラスのtoString()メソッドは、「オブジェクトのクラス名@オブジェクトの ハッシュコード」から構成される文字列だけを返すようになっており、このメソッドを そのまま呼び出しても、オブジェクトに含まれるデータを表示することはできません。 そのサブクラスでtoString()メソッドをオーバーライドすることによって、適切な値 を返すようにプログラミングする必要があります。(実際、Javaの基本的なAPIの中 に含まれる種々のサブクラスでもオーバーライドされています。) Javaでは、すべてのObjectのサブクラスで、このメソッドをオーバーライドすることを 推奨しています。 そこで、上記のソース・コードのようにtoString()メソッドをオーバーライドしてやる ことによって、Apache CommonsのReflectionToStringBuilderにオブジェクトの各フィー ルドの値をリスト・アップさせるのです。 では、実際に試してみましょう。 はじめに、Eclipse(以下特に断らない限り、前回インストールしたEclipse 3.5のことを たんにEclipseと呼びます)を起動して下さい。 ◆ ◆ ◆ ◆ ◆ ◆ ◆ ◆ ◆ ◆ ◆ ◆ ◆ ◆ ◆ ◆ ◆ ◆ ◆ まず、テスト用のプログラムを入れるプロジェクトを作成しておきましょう。 (現在、Java EEパースペクティブのままになっているものと仮定します。) (1) プロジェクト・エクスプローラーの中で右クリックし、「新規」→「プロジェクト」 を選択します。 (2) 「新規プロジェクト」ウインドウで「Java」配下の「Javaプロジェクト」を選択し、 「次へ」ボタンをクリックします。 (3) 「プロジェクト名」欄に ReflectionTest と入力し、「完了」ボタンをクリックします。 (このとき「関連づけられたパースペクティブを開きますか?」ウインドウが表示され たら、「はい」ボタンをクリックすることによって、Javaパースペクティブに切り替わ ります。) ◆ ◆ ◆ ◆ ◆ ◆ ◆ ◆ ◆ ◆ ◆ ◆ ◆ ◆ ◆ ◆ ◆ ◆ ◆ 続いて、Apache Commonsをインストールしておきましょう。 Apache Commonsは名前からわかる通り、Apacheソフトウエア・ファウンデーション のオープン・ソースです。 このソフトウェアは、Commonsという名前からわかるように、共通して皆が使える ようなクラス群から構成されており、Javaの標準のAPIを補完する目的で作られた API群です。 Apache Commonsのホームページは下記の通りです。 http://commons.apache.org/index.html Apache Commonsにはコンポーネント(Components = 構成要素)がたくさんあり ますが、そのうち、今回は最も基本的なLangだけを使用します。 ダウンロードするには、 http://commons.apache.org/downloads/index.html のWebページの中の「Downloads」の下のコンポーネントのリストの中から「Lang」 を選択(クリック)し、次のページで「2.4.zip」を選択(クリック)しましょう。 ファイル(commons-lang-2.4-bin.zip)をダウンロードしたら、解凍しましょう。 解凍してできたフォルダー(commons-lang-2.4)を適切な場所に移動しましょう。 当メールマガジンでは、C:ドライブ直下に移動し、 C:\commons-lang-2.4 というフォルダーにします。 このフォルダーの中にcommons-lang-2.4.jarというJARファイルがありますね。 これがApache CommonsのLangコンポーネントの本体です。 ◆ ◆ ◆ ◆ ◆ ◆ ◆ ◆ ◆ ◆ ◆ ◆ ◆ ◆ ◆ ◆ ◆ ◆ ◆ では、このJARファイルをEclipseで使えるようにしましょう。 つまり、下記のように作業して、Eclipseのクラスパス(ビルド・パス)に登録 しましょう。 (1) Eclipseのメニュー・バーから「ウィンドウ」→「設定」を選択します。 (2) 「設定」ウインドウの左側で「Java」配下の「ビルド・パス」配下の 「ユーザー・ライブラリー」をクリックします。 (3) 右側の「新規」ボタンをクリックします。 (4) ユーザー・ライブラリー名を apache_commons (自分の好きな名前にしてもよいが、このメールマガジンではこういう名前にしておく) と入力して「OK」ボタンを クリックします。 (5) 「apache_commons」を選択し、「JARの追加」ボタンをクリックします。 (6) 「JARの選択」ウインドウで適切なJARファイルを選択します。 つまり、 C:\commons-lang-2.4 の中の commons-lang-2.4.jar を選択し、「開く」ボタンをクリックします。 (7) 「設定」ウインドウの「OK」ボタンをクリックします。 これで、apache_commonsというユーザー・ライブラリー名でcommons-lang-2.4.jarが 利用できるようになりました。 (8) 「パッケージ・エクスプローラー」の中のReflectionTest(プロジェクト名)を 右クリックし、「ビルド・パス」→「ライブラリーの追加」を選択します。 (9) 「ライブラリーの追加」ウインドウの中の「ユーザー・ライブラリー」を選択し、 「次へ」ボタンをクリックします。 (10) 「apache_commons」(これは先ほど作成したユーザー・ライブラリー名)の左側に チェック・マークを入れ(クリックすればチェック・マークが入る)、「完了」ボタン をクリックします。 ◆ ◆ ◆ ◆ ◆ ◆ ◆ ◆ ◆ ◆ ◆ ◆ ◆ ◆ ◆ ◆ ◆ ◆ ◆ では、このReflectionTestプロジェクトの中にサンプルのプログラムを作って いきましょう。 (1) 「パッケージ・エクスプローラー」の中のReflectionTest(プロジェクト名)配下 のsrcを右クリックし、「新規」→「パッケージ」を選択します。 (2) 「新規Javaパッケージ」ウインドウにおいて、「名前」欄に jp.co.flsi.lecture.test.reflection と入力し、「完了」ボタンをクリックします。 (3) 「パッケージ・エクスプローラー」の中のjp.co.flsi.lecture.test.reflection (パッケージ名)を右クリックし、「新規」→「クラス」を選択します。 (4) 「新規Javaクラス」ウインドウにおいて、「名前」欄に Student と入力し、「完了」ボタンをクリックします。 (5) Student.javaのエディターが開いたら、下記のようなソース・コードに編集 しましょう。 -------------------------------------------------------- package jp.co.flsi.lecture.test.reflection; import org.apache.commons.lang.builder.ReflectionToStringBuilder; import org.apache.commons.lang.builder.ToStringStyle; public class Student { private String name = null; private int age = 0; public void setName(String name) { this.name = name; } public String getName() { return name; } public void setAge(int age) { this.age = age; } public int getAge() { return age; } public String toString() { return ReflectionToStringBuilder.toString(this, ToStringStyle.MULTI_LINE_STYLE); } } -------------------------------------------------------- (6) 同様にjp.co.flsi.lecture.test.reflectionパッケージ配下に Teacher というクラスを作成し、下記のようなソース・コードに編集しましょう。 -------------------------------------------------------- package jp.co.flsi.lecture.test.reflection; import org.apache.commons.lang.builder.ReflectionToStringBuilder; import org.apache.commons.lang.builder.ToStringStyle; public class Teacher { private String name = null; private String subject = null; public void setName(String name) { this.name = name; } public String getName() { return name; } public void setSubject(String subject) { this.subject = subject; } public String getSubject() { return subject; } public String toString() { return ReflectionToStringBuilder.toString(this, ToStringStyle.MULTI_LINE_STYLE); } } -------------------------------------------------------- (7) 同様にjp.co.flsi.lecture.test.reflectionパッケージ配下に School というクラスを作成し、下記のようなソース・コードに編集しましょう。 -------------------------------------------------------- package jp.co.flsi.lecture.test.reflection; import java.util.Vector; import org.apache.commons.lang.builder.ReflectionToStringBuilder; import org.apache.commons.lang.builder.ToStringStyle; public class School { private Vector<Student> students = new Vector<Student>(); private Teacher teacher = null; public void setStudents(Vector<Student> students) { this.students = students; } public Vector<Student> getStudents() { return students; } public void addStudent(Student student) { this.students.add(student); } public void setTeacher(Teacher teacher) { this.teacher = teacher; } public Teacher getTeacher() { return teacher; } public String toString() { return ReflectionToStringBuilder.toString(this, ToStringStyle.MULTI_LINE_STYLE); } } -------------------------------------------------------- (8) 同様にjp.co.flsi.lecture.test.reflectionパッケージ配下に ReflectionToStringBuilderTest というクラスを作成し、下記のようなソース・コードに編集しましょう。 -------------------------------------------------------- package jp.co.flsi.lecture.test.reflection; public class ReflectionToStringBuilderTest { /** * @param args */ public static void main(String[] args) { School school = new School(); Teacher teacher = new Teacher(); teacher.setName("佐々貴子"); teacher.setSubject("英語"); school.setTeacher(teacher); Student student1 = new Student(); student1.setName("聖都一郎"); student1.setAge(11); school.addStudent(student1); Student student2 = new Student(); student2.setName("聖都二郎"); student2.setAge(12); school.addStudent(student2); System.out.println("[ログ出力]\n" + school); } } -------------------------------------------------------- 上のSystem.out.println()の引数の中ではschoolとだけ書いて、toString()は指定 していませんが、実際にはこの呼び出し方でschool.toString()が呼び出されること は以前にもお話した通りです。 以上のソース・コードは説明しなくても理解できることと思いますが、 もし、分からないところがありましたら、質問をお寄せ下さい。 編集が終わったら、すべて保管して、ReflectionToStringBuilderTestを実行 してみて下さい。 下記のような結果が出力されますね。 -------------------------------------------------------- [ログ出力] jp.co.flsi.lecture.test.reflection.School@276af2[ students=[jp.co.flsi.lecture.test.reflection.Student@186db54[ name=聖都一郎 age=11 ], jp.co.flsi.lecture.test.reflection.Student@a97b0b[ name=聖都二郎 age=12 ]] teacher=jp.co.flsi.lecture.test.reflection.Teacher@56a499[ name=佐々貴子 subject=英語 ] ] -------------------------------------------------------- Vectorの要素までちゃんと展開されて出力されていることが分かりますね。 もちろん配列など、他の複合形式のオブジェクトでもちゃんとデータが展開されて 出力されます。 このようにすれば、複雑な構造をしたオブジェクトのデータのログ出力が楽になる ことは分かりますが、面倒なことは、各クラスを作成する人が必ず -------------------------------------------------------- public String toString() { return ReflectionToStringBuilder.toString(this); } -------------------------------------------------------- のようなtoString()メソッドを追加するというルールを守らなければならない という点です。 そこで、こういった決まりきったメソッドの追加は、人がやるのではなく、 自動化するようにすればいいのです。自動化のやり方は、後に上級コースで お話します。 ただし、フィールドは普通、private指定になっているものであり、勝手に公開して はならないものです。getメソッドなどを提供して公開しているものは良いとして も、公開するメソッドを一切持っていないフィールドの値をtoString()に出力させ ることにはセキュリティー上問題があります。 上記のReflectionToStringBuilderの使用においては、その点も注意する必要が あります。 (それに、もう一つ大きな問題がありますので、後ほどお話しします。) ◆ ◆ ◆ ◆ ◆ ◆ ◆ ◆ ◆ ◆ ◆ ◆ ◆ ◆ ◆ ◆ ◆ ◆ ◆ このReflectionToStringBuilderというクラスは、内部でJavaのリフレクション という機能を呼び出して、各クラスのフィールドを調べ上げ、その値をリスト・ アップするようになっています。 このリフレクションという機能を直接使うプログラムを書けば、上のように ReflectionToStringBuilderを使わなくても、目的を達成できます。しかも、 各クラスの開発者がtoString()メソッドを追加(オーバーライド)する必要も なく、また、privateのフィールドのデータを勝手にログ出力してしまうという セキュリティー上の問題も起さずに済みます。 そういうプログラムをこれから作成することにします。 リフレクションは元々、Beanをビジュアルに組み合わせてプログラムを開発するため のツール(ビルダー・ツール)など、アプリケーション開発を支援するツールが Beanやオブジェクトを自動的に調べられるようにするための仕組みでした。 リフレクションは、クラス(Bean)の中にどのようなフィールドやメソッド(あるいは コンストラクター)があるのかを調べ出すことができるので、未知のクラスについても そのフィールドやメソッドをリスト・アップしてそれらを片っ端から呼び出すといった ことが可能になります。 したがって、リフレクションを直接使えば、ReflectionToStringBuilderを使わなく ても、未知のクラスのオブジェクトのフィールドを洗い出して、そのデータをログ出力 することができます。 そうすれば、各クラスにtoString()メソッドを追加(オーバーライド)しなくても 済みますし、しかも、getメソッドで公開されているフィールドのみを出力するように すれば、さきほどの、セキュリティー上の注意も必要ありません。 では、実際にリフレクションを直接使って、そのようなログ出力を支援するプログラム を作ってみましょう。 ReflectionToStringBuilderのまねをしてReflectionToStringMakerというクラス名に してみます。 ◆ ◆ ◆ ◆ ◆ ◆ ◆ ◆ ◆ ◆ ◆ ◆ ◆ ◆ ◆ ◆ ◆ ◆ ◆ (次回に続く) では、今日はここまでにします。 ======================================================== ◆ 02.編集後記 ======================================================== vol.163でも書いたことですが、当メールマガジンを書き始めてから、 あっと言う間に3年も経ってしまいました。 3年も経つと、以前記載していたURLが陳腐化して使えなくなるなど、 ITの世界はどんどんと変化してしまいます。 そのため、これからプログラミング技術を学ぼうという初心者の方々が 当メールマガジンのバックナンバーで学習することは困難になってきて います。(その旨、読者から苦情が来ていました。) そこで、今週から(2009年11月開講コースと称して)別途、初心者向けの コースも開始することにしました。 今までの記事の続編は、これまでと同様に毎週日曜の深夜に発行していく 予定ですが、2009年11月開講コース(初心者向けコース)のほうは毎週 水曜の深夜に発行する予定です。 これによって、初心者の方々も、Javaを最新の環境で最初から学んでいく ことができますので、ご期待ください。 ┏━━━━━━━━━━━━━━━━━━━━━━━━━━━┓ ★ホームページ: http://www.flsi.co.jp/Java_text/ ★このメールマガジンは 「まぐまぐ(http://www.mag2.com)」 を利用して発行しています。 ★バックナンバーは http://www.flsi.co.jp/Java_text/ にあります。 ★このメールマガジンの登録/解除は下記Webページでできます。 http://www.mag2.com/m/0000193915.html ★このメールマガジンへの質問は下記Webページにて受け付けて います。わからない所がありましたら、どしどしと質問をお寄 せください。 http://www.flsi.co.jp/Java_text/ ┗━━━━━━━━━━━━━━━━━━━━━━━━━━━┛ Copyright (C) 2009 Future Lifestyle Inc. 不許無断複製 |