Comments
Description
Transcript
第14章 クラスとオブジェクト
165 第 14 章 クラスとオブジェクト ここまで Java でプログラムを作成するときの、基本的なテクニックについて説明をしてきました。ここまでの 内容は、ほとんどが、Java 以外のプログラミング言語にも共通するものです。しかし、Java は、1.5 節でも説明 したとおり、オブジェクト指向のプログラミング言語という、大変に大きな特徴があります。そこで本章では、こ の「オブジェクト指向」について、説明をしてきます。 14.1 オブジェクト指向とは 1.5 節でも説明したとおり、オブジェクト指向とは、現実世界の「もの」に着目した、ソフトウェア開発の考え 方です。この「もの」のことをオブジェクトと呼びます。 例えば、図書館での本の貸し出し・返却などを行う図書館システムや学校での授業や成績などの管理をする教務 システムのソフトウェアを例として考えると、図 14.1 や図 14.2 のように、現実世界に存在するもの(ソフトウェ ア開発の世界では「もの」と呼びますが、人間を「もの」と呼ぶには違和感があれば「実物」と考えても良いで しょう)を、ソフトウェア内で扱います。 図 14.1: 図書館システムでのオブジェクト 図 14.2: 教務システムでのオブジェクト こういったオブジェクトとオブジェクトを互いに連携させる、つまりオブジェクト同士の関係を考えることで、 ソフトウェア全体を構築していきます。例えば、図 14.3 や図 14.4 のように、オブジェクト同士には、何らかの関 係があります。オブジェクト指向でのソフトウェア開発では、こういったオブジェクト同士の関係を見つけていき ます。 図 14.3: 図書館システムでのオブジェクト同士の関係 図 14.4: 教務システムでのオブジェクト同士の関係 第 14 章 166 クラスとオブジェクト オブジェクト同士の関係が見つかれば、その関係から、それぞれのオブジェクトに対し、オブジェクトが持つ役 割を割り当てます。この役割とは、プログラムの動作全体の中で、それぞれのオブジェクトが担当する処理、と言 うことができます。そして、図 14.5 や図 14.6 のように、必要なときに、あるオブジェクトから別のオブジェクト に対して依頼をする、つまり処理を呼び出す、という形で必要な処理が行われます。依頼をされたオブジェクト自 身が、必要な処理をする、つまりオブジェクトが自分で動く、という形で、ソフトウェアが成り立っていきます。 図 14.5: オブジェクトからオブジェクトへの依頼(図書 図 14.6: オブジェクトからオブジェクトへの依頼(教務 館システム) システム) このように、オブジェクト指向では、ソフトウェア内で扱うオブジェクト同士の処理の依頼を組み合わせるこ とで、ソフトウェアを構築していきます。 <ミニテクニック> 慣れるまでは「オブジェクト」とは現実に触れるものと考える プログラムを作成するとき、その作りたいプログラムにおいて「オブジェクトをどう見つけるか?」「何がオブ ジェクトになるのか?」を考えることは、慣れるまでは戸惑い、難しく感じるものです。 「オブジェクト」とは、現 実世界の「もの」に対応し、プログラム内ではデータとして扱われます。そこで、まず最初は「オブジェクト」と は「現実の世界で、手で触れる実物」と考えると、オブジェクトのイメージがつかみやすくなります。実際には、 例えば、教務システムで扱う「授業」など、必ずしも手で触れる実物ではないオブジェクトも多くあります。しか し、手で触れる実物は、多くの場合、オブジェクトとして扱われるものです。そこで、オブジェクト指向の考え 方に慣れるまでは、作成したいプログラムで扱うものの中で、現実の世界で手で触れるものを探し、それをオブ ジェクトと考えていくと良いでしょう。 14.2 「クラス」と「オブジェクト」の関係 14.2.1 概念 これまで説明したきたとおり、オブジェクト指向とは「もの」を中心としてソフトウェアを構築していく考え方 です。この「もの」のことをオブジェクトと呼びます。オブジェクトは、1 つ 1 つの具体的な実物を表します。つ まり、図書館であれば、所蔵されている 1 冊 1 冊の本、教務システムであれば、1 人 1 人の生徒や先生を、それぞ れオブジェクトと呼びます。 しかし、こういったオブジェクトを基本にしてソフトウェアのプログラムを作成していくのではきりがありませ ん。例えば、図書館には何千冊、何万冊という本が所蔵されています。オブジェクトを基本にしてプログラムを作 成すると、この大量の本 1 冊 1 冊について、その関係を考え、1 冊 1 冊が果たす役割、つまり 1 冊 1 冊の本が担当 する処理を割り当てていく、ということになります。これは、とてもではありませんが、現実的ではありません。 そこで、実際には、扱い方の同じオブジェクトをまとめてクラスという単位で、プログラムを作成していきます。 クラスとは、オブジェクトを分類したカテゴリのようなもの、オブジェクトの総称のような概念です。例えば、 図 14.7 や図 14.8 のように、1 つのクラスに対し、類似する性質を持つ複数のオブジェクトが所属します。 14.2. 「クラス」と「オブジェクト」の関係 図 14.7: 図書館システムでのクラスとオブジェクト 167 図 14.8: 教務システムでのクラスとオブジェクト まとめると、クラスとオブジェクトは、以下のようなものです。 クラス: オブジェクトの分類(一般名詞のようなもの) • 例 1 - 図書館の本: 図書館に「本」はたくさんある • 例 2 - ある高校の生徒: その高校に「生徒」はたくさんいる オブジェクト 具体的な実物(固有名詞のようなもの) • 例 1 - 図書館蔵書 ID 0001, 毎日一郎著, 現代プログラミング事情, 2008 年出版 図書館にこの本は 1 冊 だけ • 例 2 - 1 年 A 組, 出席番号 1 番, 相川太郎: その高校にこの生徒は 1 人だけ クラスについて、もう少し厳密な表現をすると「同一のフィールド(属性)とメソッド(操作・振る舞い)を持 つオブジェクトの集合」ということができます。フィールド(属性)」とは、オブジェクトが持つデータであり、 メソッド(操作・振る舞い)とは、オブジェクトが担当する処理のことです。例えばデータについては「本」クラ スのオブジェクトで言うと「タイトル」や「著者」などのデータを持ちます。1 冊 1 冊の本が持つタイトルや著者 の情報は、それぞれ違いますが「タイトル」や「著者」といったデータの分類は同一です。このデータの分類を フィールド(属性)と呼びます。またメソッド(操作・振る舞い)は、12 章で説明したメソッドのことです。つ まり、全く同じフィールドとメソッドを持つオブジェクトの分類を「クラス」と呼びます。 14.2.2 作り方 クラスの作り方 クラスとは、これまでも、Java プログラムが動作するときの基本単位であり、あらかじめ用意されているもの もたくさんある、と説明しました。そして、意識はしていなかったかもしれませんが、これまでにもクラスを作っ てきています。図 14.9 が、クラスを作成するための書式です。 図 14.9: クラス作成の書式 これまで作ってきた Java プログラムとの違いは、main メソッドがないことです。12 章でメソッドについて説 明しましたが、main メソッドも、少々特殊とはいえ、メソッドの一種です。そして、メソッドは、クラス内にお 第 14 章 168 クラスとオブジェクト いて定義するものです。従って、メソッドの定義をすべて取り除いてしまえば、クラスの最も基本的な定義の書式 になります。 そして、このクラスの中に、様々なデータや処理の定義をしていきます。 オブジェクトの作り方 クラスの定義をしても、クラスだけでは、プログラムを動作させることはできません。例えば図書館システム であれば「利用者が本を借りる」というときには、図 14.10 のように、すべての利用者が図書館のすべての本を借 りるのではありません。ある特定の利用者が、特定の数冊の本を借りるのです。また、図 14.11 のように、すべて の生徒がすべての授業を受講するのではありません。ある特定の生徒が、特定の授業を受講するのです。 図 14.10: 図書館システムの動作 図 14.11: 教務システムの動作 つまり、プログラム記述時に、同様の性質を持つオブジェクトをまとめてクラスとして定義しますが、プログラ ム実行時に、実際に様々なデータを持ち、様々な処理を行うのは、オブジェクトです。そこで、クラスを作成した ら、次にオブジェクトに対する具体的なデータの設定や、オブジェクト同士での処理の呼び出しの定義をしてい きます。 オブジェクトに対してデータを設定したり、オブジェクト同士での処理の呼び出しの定義をするには、まず、プ ログラム内で、オブジェクトを作成する、という処理を行う必要があります。このオブジェクトの作成とは、図 14.12 のように、現実世界の 1 つ 1 つの実物と対応する枠組みを、プログラム内に作成する、というイメージで良 いでしょう。この枠組みに対し、具体的なデータを設定し、オブジェクト同士での処理の呼び出しを定義すること で、現実世界の実物を、プログラム内で表現できることになります。 14.2. 「クラス」と「オブジェクト」の関係 169 図 14.12: オブジェクト作成のイメージ オブジェクトは、他のオブジェクトと連携して動作をします。そのため、オブジェクトの作成も、多くの場合、 別のオブジェクトの処理の中で行われます。そして、オブジェクトの処理とは、クラスに対して定義するメソッド です。従って、オブジェクトの作成処理は、多くの場合、図 14.13 のように、別のクラス内に定義されたメソッド 内に書きます。 図 14.13: 別のクラスにおけるオブジェクト作成 プログラムでの、オブジェクトの作成処理は、 ¶ ³ new クラス名 () µ ´ と書きます。一度この new クラス名 ()」を実行すると、オブジェクトが 1 つ作成されます。また、二度、三度と、 複数回実行すると、それぞれオブジェクトが作成されますが、作成されたオブジェクトは、すべて、異なるオブ ジェクトとして扱われます。 ただし、これだけでは、オブジェクトは作成できますが、その後の様々な処理に作成したオブジェクトを利用 することができません。一方で、クラスは、一種のデータ型として扱うこともできます。つまり、クラス名をデー タ型として変数の宣言ができます。そこで、 ¶ ³ クラス名 変数名 = new クラス名 () µ ´ と、クラス名をデータ型とする変数を宣言し、その変数にオブジェクトを代入します。この変数をオブジェクトと して利用することで、作成したオブジェクトを様々な処理に利用することができます。具体的には、図 14.14 や図 14.15 のように書きます。 第 14 章 170 図 14.14: 「本」クラスのオブジェクトの作成 クラスとオブジェクト 図 14.15: 「生徒」クラスのオブジェクトの作成 もちろん、クラス名は一種のデータ型なので、以下のように配列としても利用することができます。配列として オブジェクトの変数を宣言したときには、図 14.16 のように、配列の要素 1 つ 1 つに対し、オブジェクトを作成す る必要があります。 図 14.16: 配列でのオブジェクト作成 なお、このように、オブジェクト同士を連携させるプログラムでは、多くの場合、Java プログラムのファイル を複数作成します。複数のファイルをコンパイルするには、2.2 節(2-b)を参照してください。 null という値 上記で説明したとおり、オブジェクトは、クラス名のように扱って「クラス名 オブジェクトの変数名」とい形 で、変数を宣言します。そして「オブジェクトの変数名 = new クラス名 ();」として、オブジェクトを作成して オブジェクトの変数に代入します。 このとき、オブジェクトの変数名を宣言しただけでは、その変数には何も入っていません。この「何も入って いない」という状態を、プログラムでは、null という値が入っている状態と考えます。null で「何もない」とい う意味になります。 <注意> オブジェクトの作成と配列の作成の違い オブジェクトの作成と、8 章で説明した配列の宣言はよく似ています。上記でも説明したとおり、オブジェクト の作成は ¶ クラス名 変数名 = new クラス名 (); µ のように行います。一方、配列の宣言は ¶ データ型 [] 変数名 = new データ型 [要素数]; µ ³ ´ ³ ´ のように行います。new という同じキーワードを使っていますが、意味は違います。特に、配列でクラスのオブ ジェクトを宣言したときには、よく混同しがちです。「データ型 [] 変数名= new データ型 [要素数];」で宣言す るのは、あくまでオブジェクトの個数を宣言しただけであって、オブジェクトを実際に作成したわけではありま せん。この配列の宣言後、図 14.16 のように、1 つ 1 つの配列の要素に対して、オブジェクトを作成する必要があ 14.3. フィールド 171 ります。オブジェクトを作成する new と、配列の要素を宣言する new を混同しないよう、注意しましょう。 <コラム> オブジェクトとインスタンスの違いオブジェクトと非常によく似た意味で使われる用語として、イ ンスタンスというものがあります。本書では、用語としてオブジェクトを使っていますが、オブジェクト指向の解 説書によっては「インスタンス」の方を用語として使っているものもよくあります。厳密に言えば違いはあります が、オブジェクト指向に慣れるまでは、ほとんど同じ意味の言葉と考えてかまわないでしょう。 違いを考えると、インスタンスは、実体とも呼ばれます。プログラムを動作させて、実際に作成したオブジェク ト、ということができます。作成され、コンピュータのメモリ内に格納されたオブジェクト、というようなイメー ジです。 14.3 フィールド ここまで、クラスやオブジェクトの概念と、それらの作成方法について説明してきました。しかし、ただ、ク ラスやオブジェクトを作成しただけでは何もできできません。実際のプログラムの様々な処理を行うには、1 つ 1 つのオブジェクトに対して具体的なデータを設定し、オブジェクトが担当する処理を定義する必要があります。こ こでは、オブジェクトに対して、具体的なデータを設定する方法について説明します。なお、フィールド変数に対 してデータを設定したり、フィールド変数の値を参照することを、フィールド変数にアクセスすると言います。 プログラムで扱う 1 つ 1 つのオブジェクトは、それぞれ図 14.17 のように、具体的なデータを持ちます。これら の「著者」や「タイトル」 「出席番号」 「氏名」といったデータの分類が、14.2.1 節で説明したフィールドです。そ して、このフィールドに対して設定された値が、個々のオブジェクトが持つデータです。 図 14.17: オブジェクトが持つ具体的なデータ オブジェクトに対する、フィールドの値を設定は、 1. オブジェクトのクラスに対して、フィールドを設定する 2. フィールドを設定したクラスのオブジェクトを作成する 3. 作成したオブジェクトに対して、具体的な値を設定する という順序で行います。 (1) オブジェクトのクラスに対して、フィールドを設定する フィールドは、図 14.18 のように、クラス内で変数(フィールド変数)として宣言します。 フィールド変数は、これまでの変数の宣言方法と全く同じです。int 型や double 型、String 型といった様々 なデータ型を使い「データ型 変数名 1, 変数名 2, 変数名 3, ...;」という形で宣言します。ただし、フィールド変数 第 14 章 172 クラスとオブジェクト 図 14.18: フィールド変数の宣言 は、メソッドの定義の中に入れてはいけません。メソッドの定義の中で宣言した変数は、そのメソッドの変数に なってしまい、フィールド変数にはなりません。 図 14.19 と図 14.20 は、それぞれ、図書館システムの「本」クラスでのフィールド変数の宣言と、教務システム の「生徒」クラスでのフィールド変数の宣言の例です。 図 14.19: 「本」クラスのフィールド宣言 図 14.20: 「生徒」クラスのフィールド宣言 <ミニテクニック> フィールド変数の宣言は、クラス宣言のすぐ後に書く フィールド変数は、図 14.21 のように、クラス内であり、かつメソッドに含まれていなければ、どこでも宣言す ることができます。しかし、あちこちに分散してフィールド変数を宣言すると、どのフィールド変数をどこで宣 言しているか、わかりにくくなってしまいます。そこで、図 14.22 のように、クラス宣言のすぐ後に、すべての フィールド変数をまとめて宣言しておくと、宣言したフィールド変数の一覧を確認しやすくなります。フィールド 変数の宣言は、1 箇所にまとめるようにしましょう。 図 14.21: フィールド変数の宣言が可能な場所 図 14.22: フィールド変数の宣言が望ましい場所 <コラム> フィールド変数とローカル変数 クラス内で、どのメソッドにも含まれない場所で宣言される変数がフィールド変数です。フィールド変数は、ク ラスのオブジェクトが持つデータを表し、オブジェクトが持つ性質の一部を表現したもの、と言えます。 これに対して、メソッド内で宣言される変数をローカル変数と呼びます。ローカル変数は、メソッドにおける 様々な処理のために利用される変数であり、クラスのオブジェクトが持つデータや、オブジェクトが持つ性質とを 表すものではありません。 また、13.1 節で変数のスコープで、ブロックが異なっても、外側と内側、という関係になっているブロックで、 同じ名前の変数を宣言してはならない、ということを説明しました。しかし、フィールド変数とローカル変数は例 外です。つまり、フィールド変数と同じ名前のローカル変数、ローカル変数と同じ名前のフィールド変数を宣言す ることはできます。フィールド変数同士、ローカル変数同士では、同じ名前の変数を宣言することはできません。 14.3. フィールド 173 (2) フィールドを設定したクラスのオブジェクトを作成する クラス内でフィールド変数の宣言をしたら、次にそのクラスのオブジェクトを作成します。オブジェクトの作 り方は、14.2.2 節で説明したとおりです。つまり、そのオブジェクトと連携をする別のオブジェクトのクラス内で new クラス名 (); として、オブジェクトを作成します。 (3) 作成したオブジェクトに対して、具体的な値を設定する オブジェクトを作成するということは、現実世界の 1 つ 1 つの実物と対応する枠組みを、プログラム内に作成 する、というイメージであると 14.2.2 節で説明しました。それは、図 14.23 のように、フィールド変数に対して 何も値の設定されていない枠組みを作る、ということとも言えます。そして、空欄のフィールド変数に対し、それ ぞれのオブジェクトの具体的な値設定していきます。 図 14.23: フィールド変数に値が設定されていないオブジェクト オブジェクトのフィールド変数への値の設定は、オブジェクトを作成した場所で行います。つまり、そのオブ ジェクトのクラスとは別のクラスで、値の設定処理を書きます。フィールド変数は、オブジェクトの変数名. フィー ルド変数名で、通常の変数と全く同じように扱うことができます。そこで、この「オブジェクトの変数名. フィー ルド変数名」という形の変数に、具体的な値を代入することで、オブジェクトへのフィールド変数の値を設定す ることになります。 図 14.24 は、図書館システムにおいて「本」クラスのオブジェクトへのフィールドの値の設定の例です。 「本」ク ラスの Java ファイルは「Book.java」であり、この「本」クラスのオブジェクトに対し「LibrarySystem.java」と いう Java ファイルの中で、オブジェクトの作成と値の設定を行っています。entry は「LibrarySystem.java」で の「本」クラスのオブジェクトの変数名です。 そして、図 14.25 のように、変数 entry の後に. で続けて指定する変数名は「Book.java」で定義された変数で ある必要があります。 174 第 14 章 クラスとオブジェクト 図 14.24: 「本」クラスのオブジェクトへのフィールド変 数の値の設定 図 14.25: 「本」クラスのフィールド変数の対応関係 図 14.26 は、教務システムにおいて「生徒」クラスのオブジェクトへのフィールド変数の値の設定の例です。 「生徒」 クラスの Java ファイルは「Student.java」であり、この「生徒」クラスのオブジェクトに対し「SchoolSystem.java」 という Java ファイルの中で、オブジェクトの作成と値の設定を行っています。member は「SchoolSystem.java」で の「生徒」クラスのオブジェクトの変数名であり、配列です。オブジェクトの変数名が配列の場合には、図 14.26 のように「配列変数名 [添え字]. フィールド変数名」という形で、オブジェクトのフィールド変数を扱います。 そして、図 14.27 のように、配列の要素 member の後に. で続けて指定する変数名は「Student.java」で定義さ れた変数である必要があります。 図 14.26: 「生徒」クラスのオブジェクトへのフィールド 変数の値の設定 図 14.27: 「生徒」クラスのフィールド変数の対応関係 例外 null PointerException 14.2.2 節で説明したとおり、クラスのオブジェクトの変数名を宣言しただけで、オブジェクトの作成と変数への 代入をしていなければ、そのオブジェクトの変数には null という値が入っています。この null という値が入っ ている状態では、オブジェクトのフィールド変数への値の設定や、フィールド変数の値の参照をすることができ ません(14.7.1 節で説明するインスタンス変数に対してはできません。14.7.2 節で説明するクラス変数に対しては できます)。 null という値が入っている状態のオブジェクトに対し、フィールド変数への値の設定や、フィールド変数の値 の参照をしようとすると、nullPointerException という例外が発生します。この例外を回避するために、以下 のように条件分岐を使うことがよくあります。 ¶ ³ Book entry; ... 略 ... if (entry != {\tt null}) { entry オブジェクトへのフィールド変数への値の設定や、フィールドの値変数の参照の処理 } µ ´ 14.4. メソッド 14.4 175 メソッド オブジェクトは、フィールド以外にも、14.2.1 節で説明したように、メソッド(操作・振る舞い)を持ちます。 この「メソッド(操作・振る舞い)」とは、12 章で説明したメソッドのことです。メソッドの定義のしかたも、12 章と全く同じです。作成したメソッドを、別のクラスのオブジェクトと連携させる方法については、15 章で説明 します。なお、メソッドを呼び出すことを、メソッドにアクセスすると言います。 また、13.1 節で説明した、変数のスコープについては、クラスのフィールド変数についても有効です。従って、 クラス内にメソッドを定義するときには、メソッドの処理に、フィールド変数を利用することができます。 14.5 コンストラクタ クラスのオブジェクトは「オブジェクトの変数 = new クラス名 ();」という形式で生成する、ということを 14.2.2 節で説明しました。この new というキーワードで指定する「クラス名 ()」の部分のことをコンストラクタ と呼びます。 コンストラクタは、かなり特殊なものではありますが、メソッドの一種です。通常のメソッドの違いは、以下の とおりです。 1. コンストラクタの返り値のデータ型が、自身のクラスである 2. コンストラクタの名前は必ずクラス名と同じである 3. コンストラクタの定義時には、返り値のデータ型を宣言しない 4. 引数がなく、オブジェクトを作成するだけのコンストラクタは、クラスを作成した時点で、自動的に用意さ れている1 オブジェクトの作成時に「new クラス名 ()」とするということは、つまりコンストラクタというメソッドを呼 び出している、ということです。しかし、これまでの説明でも「コンストラクタ」という一種のメソッドを、クラ ス内に定義する、ということは説明していません。コンストラクタを作成していないにも関わらず、オブジェクト 作成時に、コンストラクタを呼び出すことができているのは、上記の 4. のためです。 そして、上記 4. のとおり、コンストラクタは自動的に用意されてはいますが、自分で定義することもできます。 14.5.1 コンストラクタの定義 コンストラクタを定義するときの書式は、図 14.28 のようなものです。この書式について、図 14.29 のようにい くつかの部分に分けて説明をします。 図 14.28: コンストラクタの定義の書式 図 14.29: コンストラクタの定義の書式(説明用) 1 クラスには「抽象クラス」と「具象クラス」という分類があります。本書で説明しているものは具象クラスです。抽象クラスは、オブジェ クトを作成することができないため、コンストラクタもありません。 第 14 章 176 クラスとオブジェクト (1) コンストラクタの名前 上記の「通常のメソッドとコンストラクタとの違い」で説明したとおり、コンストラクタの名前は、必ず、その コンストラクタを持つクラスの名前を同じです。従って、コンストラクタを自分で定義するときも、コンストラク タの名前は、クラスの名前と同じにする必要があります。 また、返り値についても、上記で説明したとおり、コンストラクタの返り値のデータ型は、自身のクラスです。 コンストラクタを定義するときに、返り値のデータ型は Java のルール上、決められているため、通常のメソッド とは違い、返り値のデータ型の定義はしません。 (2) 引数 クラスを作成したときに自動的に用意されているコンストラクタは、引数がないコンストラクタです。自分で コンストラクタを定義するときには、引数をつけて定義することができます。また、引数のないコンストラクタ も、その処理内容を自分で定義しなおすことができます。 引数は、通常のメソッドと同様に定義します。引数の定義のしかたは、12.2.1 節 (4) を参照してください。 (3) 処理内容の始まりと終わり コンストラクタの処理内容は、図 14.29(3) の { から } の間に書いていきます。これも、メソッドと全く同じです。 (4) 処理内容 図 14.29(4) の部分には、コンストラクタの処理内容を記述します。これも、メソッドと全く同じです。条件分 岐やループ文、例外処理、他クラスのオブジェクト作成とフィールド変数への値の代入や参照など、どのような 内容を記述してもかまいません。 また、コンストラクタの処理では、コンストラクタが定義されているクラスのオブジェクトが 1 つ、自動的に 作成されます。このオブジェクトが、コンストラクタの返り値と言えます。このオブジェクトは、コンストラクタ の処理の一番最初に作成されます。そして、自動的に作成されるものであるため、作成処理の記述はしません。 さらに、メソッドとは違い、return 文を書く必要はありません。コンストラクタの返り値は、クラスのオブジェ クトです。返り値になるものが自動的に決まっているため、明示的に return 文で返り値のデータを指定しません。 コンストラクタの作成例 図 14.30 は、図書館システムでの Book クラスに作成したコンストラクタ、図 14.31 は教務システムでの Student クラスに作成したコンストラクタの例です。 図 14.30: コンストラクタの例(図書館システム) 図 14.31: コンストラクタの例(教務システム) 図 14.30 の例では、コンストラクタに引数を定義し、コンストラクタの引数の値を、Book クラスのフィールド 変数に代入しています。このように、コンストラクタの引数の値を、フィールド変数に代入する、という使い方は よく行われます。 図 14.31 の例では、フィールド変数の値に初期値を代入しています。コンストラクタは「呼び出される = オブ ジェクトが作成される」ということになります。つまり、コンストラクタが呼び出されて以降、作成されたオブ 14.5. コンストラクタ 177 ジェクトが様々な処理に利用される、ということになります。そこで、コンストラクタ内の処理で、フィールド 変数を初期化しておく、という使い方もよくされます。こうすることで、初期化が必要なフィールド変数は、オ ブジェクトが作成されると同時に初期化されることになります。コンストラクタとは別に、初期化の処理を記述 するということでは、初期化の処理を書き忘れることもあるため、コンストラクタで初期化しておく方が良いで しょう。 また、特に、データ型がクラスである変数は、初期化(オブジェクトを作成して代入しておく)していないま ま、その変数を利用しようとすると、14.3 節で説明した nullPointerException という例外が発生してしまいま す。この例外を防ぐために、あたりさわりのない値で、コンストラクタで変数を初期化しておく、ということもよ く行われます。 14.5.2 コンストラクタの利用 コンストラクタを利用するということは、オブジェクトを作成する、ということです。14.3 節で説明したとお り「オブジェクトの変数名 = クラス名 ();」という形で利用します。 図 14.32 は、図書館システムでの Book クラスに作成したコンストラクタを LibrarySystem クラスで利用する 例です。 図 14.32: コンストラクタの利用例(図書館システム) 図 14.32 は、コンストラクタの引数で、オブジェクトのフィールド変数の値を設定しています。従って、14.3 節 で説明したように「オブジェクトの変数名. フィールド変数名 = 値;」のようにして、値を代入しなくても、フィー ルド変数に値が代入されています。従って、LibrarySystem クラスに書かれてある標準出力では、コンストラク タの引数で設定した値が出力されます。 図 14.33 は教務システムでの Student クラスに作成したコンストラクタを SchoolSystem で利用する例です。 図 14.33: コンストラクタの例(教務システム) 図 14.33 は、Student クラスのコンストラクタで、フィールド変数に初期値を代入しています。従って、SchoolSystem クラスで Student クラスのオブジェクトを作成した後、フィールド変数に値を代入しなくても name と classCode の 2 つのフィールド変数には、値が代入されています。従って、SchoolSystem クラスに書かれてある標準出力で は、Student クラスで定義したコンストラクタで代入される初期値が出力されます。 第 14 章 178 14.6 クラスとオブジェクト キーワード this メソッド(コンストラクタではない)は、オブジェクトが担当する処理です。そして、メソッドの処理におい て、そのメソッドの所有者であるオブジェクトを利用したいことがあります。また、コンストラクタは、オブジェ クトを作成します。このコンストラクタの処理においても、自分自身で作成したばかりのオブジェクト(コンスト ラクタを呼び出すことで、自動的に作成されるオブジェクト)を利用したいことがあります。 こういった、メソッド内やコンストラクタ内で、メソッドの所有者オブジェクト・コンストラクタで自動的に作 成されたオブジェクトを、処理に利用するために、this というキーワードを利用します。この this とは「自分自 身」という意味です。 14.3 節で、フィールド変数は「オブジェクトの変数名. フィールド変数名」という形で利用できると説明しまし た。この this というキーワードは、メソッド内やコンストラクタ内において、メソッドの所有者オブジェクト・ コンストラクタで自動的に作成されたオブジェクトの名前を表します。従って「this. フィールド変数名」という 形で、フィールド変数を使うことができます。 図 14.34 は、この this キーワードを使った例です。 図 14.34: キーワード this の利用例 この例のように、フィールド変数名とメソッドやコンストラクタの引数名が同じであり、両者を区別して使う場 合に、フィールド変数名を「this. フィールド変数名」を書くことがよくあります。フィールド変数名と引数名 (ローカル変数名)が同じ場合には、そのメソッド・コンストラクタ内では、ローカル変数名が優先されます。つ まり、図 14.34 の例では、this キーワードのついていない変数 idtitleauthoryear はすべて、ローカル変数と して扱われます。従って、これらの値をフィールド変数に代入したいときは、this キーワードをつけて、フィー ルド変数を使うことを明示的に示す必要があります。 フィールド変数名とローカル変数名が異なる場合には、この this キーワードはつけなくてもかまいません。 フィールド変数名をそのまま、メソッド内やコンストラクタ内での処理に利用することができます。 しかし、フィールド変数名をそのまま使っていると、メソッド内やコンストラクタ内の処理を見ただけでは、そ の変数名がフィールド変数であるかローカル変数であるかが区別がつきません。そこで、ローカル変数名と名前 の重なりがなくても、この this キーワードをつけてフィールド変数を利用することで、その変数がフィールド変 数であることが一目瞭然になります。そうすると、プログラムの見やすさが向上する、ということで、フィールド 変数には常に this キーワードをつける、というプログラムの書き方もよくあります。 14.7 フィールド変数とメソッドの分類 クラス内で定義するフィールド変数には、インスタンス変数(インスタンスメンバ)とクラス変数(クラスメ ンバ)という分類があります。メソッドには、インスタンスメソッドとクラスメソッドという分類があります。こ こでは、これらの分類と、プログラムでの記述について説明します。 14.7.1 インスタンス変数・メソッド フィールド変数の値は、通常、そのクラスのオブジェクトごとに異なります。図 14.23 のように、例えば「本」 クラスのオブジェクトであれば、オブジェクト 1 のタイトルや著者と、オブジェクト 2 のタイトルや著者は、値 が異なります。また、メソッドも、オブジェクトに関連付けて呼び出します。こういった、オブジェクトによって 値の違うフィールド変数やメソッドのことを、インスタンス変数(インスタンスメンバ)と呼びます。そして、オ 14.7. フィールド変数とメソッドの分類 179 ブジェクトに関連付けて呼び出すメソッドのことをインスタンスメソッドと呼びます。なお、オブジェクトに関連 付けたメソッドの呼び出しについては、15.1 節で詳しく説明します。 フィールド変数については、これまでと同様に「データ型 変数名;」という形で宣言を記述すると、インスタン ス変数の宣言になります。インスタンス変数を利用するには、14.3 で説明したとおり、クラスのオブジェクトを作 成し「オブジェクトの変数名. フィールド変数名」という形で利用します。インスタンスメソッドについては「返 り値 メソッド名 (引数) {」という形でメソッドの宣言を記述すると、インスタンスメソッドの宣言になります。12 節でつけていた static というキーワードはつけません。 図 14.35 は、図書館システムでのインスタンス変数とインスタンスメソッドの例です。左側のクラス(Book)で は、タイトルや著者、本を配置している本棚、出版年のフィールド変数をインスタンス変数として宣言しています。 右側のクラス(LibrarySystem)では giveShelfCode という、本クラスのオブジェクトに対して、その本を配 置している書架のデータを設定するインスタンスメソッドを宣言しています。 図 14.35: インスタンス変数とインスタンスメソッド(図書館システム) 図 14.36 は、教務システムでのインスタンス変数とインスタンスメソッドの例です。左側のクラス(Student) では、名前や学級、学年、出席番号のフィールド変数をインスタンス変数として宣言しています。 右側のクラス(SchoolSystem)では printStudentNameList という、受講生の氏名を出力するインスタンスメ ソッドを宣言しています。 図 14.36: インスタンス変数とインスタンスメソッド(教務システム) 12 章では static というキーワードを先頭につけて、メソッドを宣言していました。この static というキー ワードをつけるかつけないかは、定義するメソッドの役割に応じて決定します。オブジェクトに関連付けて呼び出 すインスタンスメソッドを定義するときには、static キーワードはつけません。static キーワードをつける場 合は、14.7.2 節で説明します。 14.7.2 クラス変数・クラスメソッド どのオブジェクトであっても、同じ値を持っている、というフィールド変数を宣言することができます。また、オ ブジェクトに関連付けることなく、呼び出すことのできるメソッドを定義することもできます。こういったフィー ルド変数やメソッドを、それぞれクラス変数(クラスメンバ)、クラスメソッドと呼びます。 「クラス変数」や「ク ラスメソッド」は、オブジェクトとは関係なく、クラスそのものが持つデータや、クラスに関連付けて呼び出すメ ソッドと考えることができます。このクラス変数やクラスメソッドを宣言するために、static というキーワード 第 14 章 180 クラスとオブジェクト を利用します。このキーワードを、フィールド変数やメソッドの宣言部分の先頭につけると、そのフィールド変数 やメソッドは、クラス変数・クラスメソッドになります。 なお、クラスやコンストラクタに、この static をつけて宣言することはできません2 例えば、図 14.37 のように、どのオブジェクトにも共通する値を入れる変数を、クラス変数として宣言します。 図 14.37: クラス変数の利用例 また、インスタンス変数の場合、オブジェクト作成して「オブジェクトの変数名. フィールド変数名」という形 で利用してきました。しかし、この static キーワードをつけたフィールド変数は、クラスに所属するすべてのオ ブジェクトに共通データであるため、オブジェクトを作成せず「クラス名. 変数名」という形で利用できます。 図 14.38 は、図書館システムでのクラス変数とクラスメソッドの例です。左側のクラス(Book)では、本の所 有者を表すフィールド変数をクラス変数として宣言しています。 右側のクラス(LibrarySystem)では createBookEntry という、本クラスのオブジェクトに対して、タイトル や著者などのデータを設定するクラスメソッドを宣言しています。 そして、右側のクラスの main メソッドにおいて、左側のクラスで宣言したクラス変数を参照しています。 図 14.38: クラス変数とクラスメソッド(図書館システム) 図 14.39 は、図書館システムでのクラス変数とクラスメソッドの例です。左側のクラス(Student)では、生徒 の教務システム上での身分を表すフィールド変数をクラス変数として宣言しています。 右側のクラス(SchoolSystem)では printStudentInfo という、生徒の情報を出力するクラスメソッドを宣言 しています。 そして、右側のクラスの printStudentInfo メソッドにおいて、左側のクラスで宣言したクラス変数を参照し ています。 2 2.1.1 のコラムで説明した内部クラスにはつけることができます。トップレベルクラスや、内部クラスであってもコンストラクタにはつけ ることができません。 14.8. アクセス修飾子 181 図 14.39: クラス変数とクラスメソッド(教務システム) また、クラス変数・クラスメソッドと、インスタンス変数・インスタンスメソッドを、同一クラス内で利用する ときの利用方法には、以下のルールがあります。 1. クラスメソッド内で、インスタンス変数を利用(インスタンス変数にアクセス)することはできない 2. クラスメソッド内で、インスタンスメソッドを呼び出すことはできない なお、インスタンスメソッド内で、クラス変数やクラスメソッドを利用することはできます。また、クラスメ ソッド内で、クラス変数や別のクラスメソッドの利用もできます。 12 章で、static キーワードをつけてメソッドを宣言していたのは、12 章の段階では、main メソッドから、定 義したメソッドを呼び出す必要があったためです。main メソッドは、static キーワードをつけて宣言しているた め、クラスメソッドと言えます。従って、上記 2. の理由のため、定義するメソッドを、クラスメソッドとする必 要があったのです。main メソッドからではなく、他のインスタンスメソッドから呼び出す場合には、メソッドの 宣言において static キーワードはつけません。 14.8 アクセス修飾子 クラス宣言やメソッド宣言、フィールド変数の宣言には public や private などのキーワードをつけて宣言す ることがよくあります。これらのキーワードを修飾子と呼びます。14.7.2 節で説明した static キーワードも修飾 子です。他のクラスのオブジェクトから、そのクラスやメソッド、フィールド変数が利用されるときの、利用可能 な範囲や可能な利用のされ方を表すものです。 修飾子の中でも、public と protected、private という 3 つを、アクセス修飾子と呼びます。アクセス修飾子 は、それをつけて宣言されたクラスやメソッド、変数について、どのような範囲からアクセスできるか、というこ とを表します。「範囲」とは、そのクラスの中のみ、同じパッケージに所属しているクラス同士のみ、そしてどこ のクラスからでも、というものです。 「パッケージ」とは、2.1.1 節で説明した、クラスの分類です。多くのクラスを作って、それらを連携させる場 合には、パッケージを自分で作って、クラスを分類することもできます。 14.8.1 public public は、どこからでもアクセスできる、というアクセス修飾子です。自クラス内はもちろん、同じパッケージ に所属するクラスのオブジェクトから、異なるパッケージに所属するクラスのオブジェクトからでも、この public というアクセス修飾子をつけたクラスやメソッド、フィールド変数にはアクセスできます。 図 14.40 は、図書館システムでのクラス・メソッド・フィールド変数に対するアクセスの例です。 この例では、以下のように、クラス・メソッド・フィールド変数へのアクセスが行われています。これらのアク セスはすべて可能です。 第 14 章 182 クラスとオブジェクト 図 14.40: public 修飾子でのアクセス(図書館システム) • LibrarySystem クラスから Book クラスのフィールド変数 title へのアクセス: 異なるパッケージ内でのア クセス • BookShelf クラスの giveShelfCode メソッドからの Book クラスのフィールド変数 shelf へのアクセス: 同 一パッケージ内の異なるクラスからのアクセス • LibrarySystem クラスの main メソッドから setBookData メソッドへのアクセス: 同一クラス内からのア クセス 図 14.41 は、教務システムでのクラス・メソッド・フィールド変数に対するアクセスの例です。 図 14.41: public 修飾子でのアクセス(教務システム) この例では、以下のように、クラス・メソッド・フィールド変数へのアクセスが行われています。これらのアク セスはすべて可能です。 • SchoolSystem クラスの main メソッドから Student クラスのフィールド変数 classCode へのアクセス: 異 なるパッケージ内でのアクセス • Course クラスの printStudentName メソッドからの Student クラスのフィールド変数 name へのアクセス: 同一パッケージ内の異なるクラスからのアクセス 14.8. アクセス修飾子 183 • Student クラスの setStudentNumber メソッドからフィールド変数 studentNumber へのアクセス: 同一ク ラス内からのアクセス 14.8.2 protected protected は、同一パッケージのクラス同士、または同一クラス内のみからアクセスできる、というアクセス 修飾子です。異なるパッケージのクラスのオブジェクトからは、アクセスができません。ただし、この protected 修飾子は、クラスに対してはつけることができません3 図 14.42 は、図書館システムでのクラス・メソッド・フィールド変数に対するアクセスの例です。 図 14.42: protected 修飾子でのアクセス(図書館システム) この例では、以下のように、クラス・メソッド・フィールド変数へのアクセスが行われています。アクセスの 可・不可も、以下のとおりです。 • LibrarySystem クラスから Book クラスのフィールド変数 title へのアクセス: 異なるパッケージ内でのア クセス(アクセス不可) • BookShelf クラスの giveShelfCode メソッドからの Book クラスのフィールド変数 shelf へのアクセス: 同 一パッケージ内の異なるクラスからのアクセス(アクセス可) • LibrarySystem クラスの main メソッドから setBookData メソッドへのアクセス: 同一クラス内からのア クセス(アクセス可) 図 14.43 は、教務システムでのクラス・メソッド・フィールド変数に対するアクセスの例です。アクセスの可・ 不可も、以下のとおりです。 3 2.1.1 のコラムで説明した内部クラスにはつけることができます。トップレベルクラスにはつけることができません。 第 14 章 184 クラスとオブジェクト 図 14.43: protected 修飾子でのアクセス(教務システム) この例では、以下のように、クラス・メソッド・フィールド変数へのアクセスが行われています。これらのアク セスはすべて可能です。 • SchoolSystem クラスから Student クラスへのアクセス: 異なるパッケージ内でのアクセス(アクセス不可) • Course クラスからの Student クラスのフィールド変数 name へのアクセス: 同一パッケージ内の異なるクラ スからのアクセス(アクセス可) • Student クラスの setStudentNumber メソッドからフィールド変数 studentNumber へのアクセス: 同一ク ラス内からのアクセス(アクセス可) 14.8.3 private private は、同一クラス内のみからアクセスできる、というアクセス修飾子です。パッケージが同じであっても 異なっても、異なるクラスからは、アクセスができません。ただし、この private 修飾子は、クラスに対しては つけることができません4 図 14.44 は、図書館システムでのクラス・メソッド・フィールド変数に対するアクセスの例です。 4 protected と同様に、2.1.1 で説明した内部クラスにはつけることができます。トップレベルクラスにはつけることができません。 14.8. アクセス修飾子 185 図 14.44: private 修飾子でのアクセス(図書館システム) この例では、以下のように、クラス・メソッド・フィールド変数へのアクセスが行われています。アクセスの 可・不可も、以下のとおりです。 • LibrarySystem クラスから Book クラスのフィールド変数 title へのアクセス: 異なるパッケージ内でのア クセス(アクセス不可) • BookShelf クラスの giveShelfCode メソッドからの Book クラスのフィールド変数 shelf へのアクセス: 同 一パッケージ内の異なるクラスからのアクセス(アクセス不可) • LibrarySystem クラスの main メソッドから setBookData メソッドへのアクセス: 同一クラス内からのア クセス(アクセス可) 図 14.45 は、教務システムでのクラス・メソッド・フィールド変数に対するアクセスの例です。アクセスの可・ 不可も、以下のとおりです。 図 14.45: private 修飾子でのアクセス(教務システム) • SchoolSystem クラスから Student クラスへのアクセス: 異なるパッケージ内でのアクセス(アクセス不可) • Course クラスからの Student クラスのフィールド変数 name へのアクセス: 同一パッケージ内の異なるクラ スからのアクセス(アクセス不可) 第 14 章 186 クラスとオブジェクト • Student クラスの setStudentNumber メソッドからフィールド変数 studentNumber へのアクセス: 同一ク ラス内からのアクセス(アクセス可) <コラム> 何もつけていないものは、protected クラス宣言やメソッド宣言、フィールド変数の宣言のときに、public も protected も private もつけずに宣 言することもできます。このとき、宣言されたクラスやメソッド、フィールド変数は、自動的に protected とし て扱われます。 14.9 final 修飾子 final 修飾子をつけてクラスやメソッド、フィールド変数を宣言すると、クラスは、15.2 節で説明する継承がで きなくなります。メソッドは、15.3 節で説明するオーバーライドができなくなります。そして、フィールド変数 は、一度代入した値を変更できなくなります。 特にフィールド変数に final 修飾子をつけたものを、定数と呼びます。定数は、宣言すると同時に初期化をす る必要があります。そして初期化後、値を変更することはできません。 図 14.46 は、図書館システムでの定数とメソッドの例です。左側のクラス(Book)では、本の所有者を表すフィー ルド変数を定数として宣言しています。また、9.3 節で説明したとおり、Java の命名規則として、定数はすべて大 文字で名前をつける、となっているため、ここでも、大文字で名前をつけています。 右側のクラス(LibrarySystem)では createBookEntry という、本クラスのオブジェクトに対して、タイトル や著者などのデータを設定するメソッドに final 修飾子をつけて宣言しています。 図 14.46: final 修飾子をつけたフィールド変数とメソッド(図書館システム) 図 14.47 は、教務システムでの定数とメソッドの例です。左側のクラス(Student)、生徒の教務システム上で の身分を表すフィールド変数を定数として宣言しています。また、9.3 節で説明したとおり、Java の命名規則とし て、定数はすべて大文字で名前をつける、となっているため、ここでも、大文字で名前をつけています。 右側のクラス(SchoolSystem)では printSchoolName という、学校名を出力するメソッドに final 修飾子を つけて宣言しています。 図 14.47: final 修飾子をつけたフィールド変数とメソッド(教務システム) <注意> 変数で、修飾子をつけるのはフィールド変数だけ 修飾子をつけて宣言をすることができるのは、フィールド変数のみです。フィールド変数は、他のオブジェクト によって値を代入・参照(アクセス)されることもよくあります。しかし、メソッド内で宣言される変数(ローカ 14.10. プログラム例 187 ル変数)は、他のオブジェクトからアクセスされることはありません。そのため、ローカル変数には、修飾子を つけません。 <注意> 修飾子をつける場所 修飾子は、以下のように、クラスの宣言であれば class キーワードの前、メソッドの宣言であれば「返り値のデー タ型」の前、フィールド変数の宣言であればデータ型の前につけます。アクセス修飾子と static 修飾子、final 修飾子の順序は好きな順序でかまいませんが(多くの場合、アクセス修飾子を先頭に書きます)、class キーワー ドやデータ型との順序を間違えないよう、注意しましょう。 ¶ ³ public class クラス名 { µ ¶ ´ ³ protected void メソッド名 { µ ¶ ´ ³ private{\tt static}int 変数名; µ ´ 14.10 プログラム例 ここでは、これまでの内容をまとめて、図書館システムと教務システムのプログラム例を説明します。 図 14.48 と図 14.49 は、図書館システムの処理の一部を記述したプログラム例です。この例では、Book クラスに おいて、ID やタイトル、著者などのフィールド変数を宣言し、LibrarySystem クラスでそれを利用しています。 LibrarySystem クラスでは、まず、Book クラスのオブジェクトを作成して、ID やタイトル、著者などの具体 的な値を設定ます。本の ID やタイトル、著者などの具体的な値は「BookData.csv」というファイルに保存されて いるものを読み込みます。この「BookData.csv」には「本の ID, 本のタイトル, 本の著者, 本の出版年」という形 式で、データが保存されています。例えば、以下のようなものです。 ¶ ³ 0001, 現代プログラミング事情, 毎日一郎,2008 0002,Java 三昧, 鈴木花子,2009 µ ´ LibrarySystem クラスでは、これらのデータに設定した後、それを標準出力で出力する、という処理をしてい ます。 第 14 章 188 図 14.48: 図書館システムのクラス(Book) クラスとオブジェクト 図 14.49: 図書館システムのクラス(LibrarySystem) 図 14.50 と図 14.51 は、教務システムの処理の一部を記述したプログラム例です。この例では、Student クラス において、学年や氏名、5 教科の得点などのフィールド変数を宣言し、SchoolSystem クラスでそれを利用してい ます。 SchoolSystem クラスでは、まず、Student クラスのオブジェクトを作成して、学年や氏名、5 教科の得点など の具体的な値を設定ます。学年や氏名、5 教科の得点などの具体的な値は「StudentData.csv」というファイルに 保存されているものを読み込みます。この「StudentData.csv」には「学年, 学級, 出席番号, 氏名, 数学, 国語, 英 語, 理科, 社会」という形式で、データが保存されています(教科のデータは得点です)。例えば、以下のようなも のです。 ¶ ³ 1,A 組,1, 相川太郎,85,70,98,52,79 1,A 組,2, 相沢花子,68,97,63,55,87 µ ´ SchoolSystem クラスでは、これらのデータに設定した後、printStudentInfo メソッドで、5 教科の平均点を 計算し、出力する、という処理をしています。 14.11. エラーメッセージの意味と対策 図 14.50: 教務システムのクラス(Student) 14.11 189 図 14.51: 教務システムのクラス(SchoolSystem) エラーメッセージの意味と対策 ここでは、本章までの内容で、表示される可能性のあるコンパイルエラー・例外のメッセージについて説明を します。図 14.52 と図 14.53 のプログラムを例として考えていきます。なお、この 2 つのクラスは、お互いに連携 して動作することを想定して作成されたものです。 第 14 章 190 クラスとオブジェクト 図 14.52: エラーが出るプログラム例(SampleA 1.java)図 14.53: エラーが出るプログラム例(SampleA 2.java) この図のプログラムを保存しているファイル名は、図 14.52 を「SampleA 1.java」、図 14.53 を「SampleA 2.java」、 プログラムの左側に書かれている数字は、プログラムの行数とします。 14.11.1 コンパイルエラー 図 14.52 と図 14.53 の Java ファイルをコンパイルしたときに表示されるエラーメッセージを説明します。なお、 2.3.1 節でも説明したとおり、エラーの内容によっては、あるエラーを修正し、再度コンパイルしたときに表示さ れるエラーメッセージもありますが、ここでは、図 14.52 と図 14.53 のプログラムに含まれるエラーすべてについ て、1 行目から順に説明をしていきます。エラーメッセージの基本的な読み方については、2.3 節を参照してくだ さい。 SampleA 1.java のコンパイルエラー ¶ 1 行目「変数∼は初期化されていない可能性があります」 ³ SampleA_1.java:1: 変数{\tt static}は初期化されていない可能性があります。 public class SampleA_1 { ^ µ ´ このエラーは「変数∼は初期化されていない可能性があります」というメッセージです。これまでにも説明した エラーですが、ここではまた違った理由で出てきているため、説明しておきます。 14.9 節で説明したとおり、フィールド変数に final 修飾子をつけると、そのフィールド変数は定数となります。 定数は、宣言と同時に初期化する必要があります。しかし、図 14.52 の例では、5 行目の定数の宣言のところで、宣 言をするだけで、値を代入していません。そのため、このエラーが出ています。 「public static final String OWNER = "毎日図書館";」のように、宣言と同時に値を代入するように修正すると、このエラーは解消されます。 このエラーが表示されたときは、定数に対し、宣言と同時に値を代入しているか、確認しましょう。 ¶ 5 行目「<identifier> がありません」 ³ SampleA_1.java:5: <identifier> がありません。 public String{\tt static}final OWNER; ^ µ ´ このエラーは「<identifier> がありません」というメッセージです。これまでにも説明したエラーですが、ここ ではまた違った理由で出てきているため、説明しておきます。 14.11. エラーメッセージの意味と対策 191 14.9 節で説明したとおり public や static などの修飾子をつけるときは、クラスの宣言であれば class キー ワードの前、メソッドの宣言であれば「返り値のデータ型」の前、フィールド変数の宣言であればデータ型の前 につける必要があります。 この例は、定数の宣言部分について public String static final OWNER; となっています。つまり static final という修飾子がデータ型の後になっています。そのため、このエラーが出ています。この例では public static final String OWNER; のように、すべての修飾子をデータ型の前に移動すると、このエラーは解消され ます。 このエラーが出たときは、修飾子が書かれている順序について確認しましょう。 ¶ 7 行目「メソッドの宣言が不正です。戻り値の型が必要です」 ³ SampleA_1.java:7: メソッドの宣言が不正です。戻り値の型が必要です。 public Constructor(String id, String title, String author, int year) { ^ µ ´ このエラーは「メソッドの宣言が不正です。戻り値の型が必要です」というメッセージです。これは、メソッド の宣言において、メソッド名の前に返り値のデータ型が書かれていない場合に出るエラーです。 この例では、7 行目のメソッドの宣言の部分は、コンストラクタの宣言を想定しています。コンストラクタは、 14.5.1 節で説明したとおり、名前をクラス名と同じにしなければなりません。しかしこの図 14.52 の例では、コ ンストラクタの名前が Constructor になっていて、クラス名とは違っています。このコンストラクタの名前を 「SampleA 1」と、クラス名と同じに修正すると、このエラーは解消されます。 このエラーが出たときは、エラーが出たメソッドの宣言が、コンストラクタの宣言であるかメソッドの宣言で あるかを確認し、コンストラクタの宣言であれば、コンストラクタの名前が、クラス名と同じになっているか確 認しましょう。 SampleA 2.java のコンパイルエラー ¶ 1 行目「修飾子 private をここで使うことはできません」 ³ private class SampleA_2 { ^ µ ´ このエラーは「private をここで使うことはできません」というメッセージです。14.8.2 節や 14.8.3 節で説明し たとおり protected や private というアクセス修飾子は、クラスにつけることはできません。 図 14.53 のプログラムでは、クラスに private アクセス修飾子をつけているため、このエラーが出ています。こ の private を public アクセス修飾子に修正すると、このエラーが解消されます。 このエラーが表示されたときは、クラスに protected または private のアクセス修飾子をつけていないか、確 認しましょう。 ¶ 7 行目「∼は... で— アクセスされます」 ³ SampleA_2.java:7: title は SampleA_1 で private アクセスされます。 µ entry[0].title = "現代プログラミング事情"; ^ ´ このエラーは「∼は... で— アクセスされます」というメッセージです。このメッセージは、8 行目や 25 行目、 26 行目、27 行目でも同じものが表示されます。 14.8.2 節や 14.8.3 節で説明したとおり、フィールド変数やメソッドに対しては、宣言時につけたアクセス修飾 子によって、アクセスできる範囲が限定されます。つまり public アクセス修飾子をつけると、そのフィールド変 数やメソッドには、どのクラスのオブジェクトからでもアクセスできます。しかし protected アクセス修飾子を つけると、そのフィールド変数やメソッドには、同じパッケージ内のクラスのオブジェクトからしかアクセスで きません。private アクセス修飾子をつけると、そのフィールド変数やメソッドには、同じクラス内からしかア クセスできません。 第 14 章 192 クラスとオブジェクト このエラーは protected や private アクセス修飾子をつけて宣言しているフィールド変数やメソッドに対し、 許可される範囲外からのアクセスをしようとしているときに表示されます。つまり protected アクセス修飾子を つけている場合には、異なるパッケージのクラスのオブジェクトからアクセスしようとしているときに表示され ます。private アクセス修飾子をつけている場合には、異なるクラスのオブジェクトからアクセスしようとして いるときに表示されます。 この例では、図 14.52 の id と titleauthor という変数について private アクセス修飾子がついています。つ まり、これらの変数には「SampleA 1」クラスからのみアクセスできます。しかし、図 14.53 の「SampleA 2」ク ラスからアクセスしようとしているため、このエラーが出ています。この例では、図 14.52 の id と titleauthor という変数宣言のアクセス修飾子を public に変更すると、このエラーは解消されます。 このエラーが表示されたときは、以下について確認しましょう。 • エラーが出た変数やメソッドにつけているアクセス修飾子の種類は適切か • 変数やメソッドへのアクセスの記述について、適切な変数やメソッドを指定しているか ¶ 9 行目「static でない変数∼を static コンテキストから参照することはできません」 ³ SampleA_2.java:9:{\tt static}でない 変数{\tt static}を{\tt static}コンテキストから参照するこ とはできません。 SampleA_1.publicationYear = 2008; ^ µ ´ このエラーは「static でない変数∼を static コンテキストから参照することはできません」というメッセー ジです。14.3 節や 14.7.1 節で説明したとおり、インスタンス変数は、クラスのオブジェクトを作成し「オブジェ クトの変数名. フィールド変数名」という形で利用します。 この例では、図 14.53 のクラスにおいて SampleA 1.publicationYear = 2008;、つまり「クラス名. フィール ド変数名」という形でインスタンス変数 publicationYear を利用しています。この利用方法は、14.7.2 節で説明 したクラス変数の利用方法です。そのため、このエラーが出ています。この例では entry[0].publicationYear = 2008; と、オブジェクトの変数名から参照するように修正すると、このエラーは解消されます。 このエラーが出たときは、利用しようとしているフィールド変数名が、インスタンス変数であるかクラス変数 であるかを確認しましょう。 ¶ ³ 15 行目「static でない変数∼を static コンテキストから参照することはできません」 SampleA_2.java:15:{\tt static}でない 変数{\tt systemName}を{\tt static}コンテキストから参照 することはできません。 systemName = "毎日図書館蔵書管理システム"; ^ µ ´ このエラーは「static でない変数∼を static コンテキストから参照することはできません」というメッセージ です。このエラーは、上記でも説明したものですが、また違った理由で出ているため、説明しておきます。14.7.2 節で説明したとおり、インスタンス変数(static 修飾子をつけて宣言していないフィールド変数)を、同一クラス 内のクラスメソッド(static 修飾子をつけて宣言しているメソッド)で利用することはできません。このエラー は、インスタンス変数を、同一クラス内のクラスメソッドで利用しようとしたときに表示されます。 この例では、変数 systemName は、static 修飾子をつけていないため、インスタンス変数です。この変数に対 し、main メソッド内で値を代入しようとしています。main メソッドは、一種のクラスメソッドです。そのため、 このエラーが表示されています。この例では systemName 変数に static 修飾子をつけて宣言するように修正する と、このエラーは解消されます。 このエラーが出たときは、以下について確認しましょう。 • エラーが出た変数は、インスタンス変数で良いか • エラーが出た変数にアクセスしているメソッドは、クラスメソッドで良いか 14.11. エラーメッセージの意味と対策 193 ¶ 16 行目「変数 final∼に値を代入することはできません」 ³ SampleA_2.java:16: final 変数{\tt static}に値を代入することはできません。 µ SampleA_1.OWNER = "毎日図書館"; ^ ´ このエラーは「変数 final∼に値を代入することはできません」というメッセージです。14.9 節で説明したとお り、フィールド変数に final 修飾子をつけると、その変数には、宣言と同時に値を代入する必要があります。そ して、一度代入した値は変更ができません。つまり、別の値を代入しなおすことができません。 この例は、図 14.52 のクラスの OWNER 変数が final 修飾子がついて宣言されています。そして図 14.53 のクラ スで、この OWNER 変数に値が代入されています。この値の代入は、宣言時の代入ではないため、代入のしなおし とみなされます。そのため、このエラーが出ています。OWNER 変数に値を代入しているこの 16 行目の処理を消す と、このエラーは解消されます。 このエラーが出たときは、final 修飾子のついた変数に値を代入しようとしていないかを確認しましょう。 ¶ 18 行目「static でないメソッド∼を static コンテキストから参照することはできません」 ³ SampleA_2.java:18:{\tt static}でない{\tt createBookEntry()}メソッドを{\tt static}コンテキス トから参照することはできません。 SampleA_1[] bookEntry = createBookEntry(); ^ µ ´ このエラーは「static でないメソッド∼を static コンテキストから参照することはできません」というメッ セージです。14.7.2 節で説明したとおり、インスタンスメソッド(static 修飾子をつけて宣言していないメソッ ド)を、同一クラス内のクラスメソッド(static 修飾子をつけて宣言しているメソッド)で利用することはでき ません。このエラーは、インスタンスメソッドを、同一クラス内のクラスメソッドで利用しようとしたときに表 示されます。 この例では createBookEntry メソッドは、static 修飾子をつけていないため、インスタンスメソッドです。こ のメソッドを、main メソッド内利用しています。main メソッドは、一種のクラスメソッドです。そのため、この エラーが表示されています。この例では createBookEntry メソッドに static 修飾子をつけて宣言するように修 正すると、このエラーは解消されます。 このエラーが出たときは、以下について確認しましょう。 • エラーが出たメソッドは、インスタンスメソッドで良いか • エラーが出たメソッドを利用しているメソッドは、クラスメソッドで良いか 14.11.2 例外 図 14.52 と図 14.53 のプログラムのコンパイルエラーをすべて修正してコンパイルができ、プログラムを実行し たときに表示される例外を説明します。 ¶ 7 行目 nullPointerException ³ Exception in thread "main" java.lang.nullPointerException at Sec14ErrorSample1_2.createBookEntry(Sec14ErrorSample1_2.java:7) at Sec14ErrorSample1_2.main(Sec14ErrorSample1_2.java:18) µ これは nullPointerException という例外です。14.2.2 節と 14.3 節で説明したとおり、オブジェクトの変数を 作成し、オブジェクトの作成と代入をしなければ、その変数には null という値が入っています。この null とい う値が入っている状態では、そのオブジェクトのインスタンス変数に対するアクセスはできません。そして null という値が入っている状態で、インスタンス変数にアクセスしようとすると、この nullPointerException とい う例外が発生します。 ´ 194 第 14 章 クラスとオブジェクト この例では、図 14.53 のクラスで entry という配列で図 14.52 の SampleA 1 クラスのオブジェクトの変数を宣 言しています。そして、図 14.53 の 7 行目から、このオブジェクトのインスタンス変数に値を代入していますが、 その前に、オブジェクトの作成と、変数への代入をしていません。 つまり、この例では entry[0] という変数には、null が入っている状態で、インスタンス変数に値を代入してい るため、このエラーが出ています。この例では、6 行目のあたりに entry[0] = new SampleA 1(); と SampleA 1 クラスのオブジェクトの作成と代入の処理を追加すると、このエラーは解消されます。 このエラーが出たときは、アクセスしようとしているオブジェクトの変数に、null ではない値が入っているか どうか確認しましょう。 ¶ 6 行目 NoSuchMethodError ³ java.lang.NoSuchMethodError: main Exception in thread "main" µ ´ これは NoSuchMethodError という例外です。これまでにも説明した例外ですが、ここではまた違った理由で出 ているので、説明しておきます。 これは、プログラム実行時に java コマンドで指定したクラスに、main メソッドが見つからなかったときに出 るエラーです。2.2 節で説明したとおり java コマンドに対して指定するクラスは、main メソッドを持っているク ラスである必要があります。 この例では、main メソッドが書かれているのは図 14.53 の SampleA 2 クラスです。しかし java コマンドに対 して、図 14.52 の SampleA 1 クラスを指定してしまったときに、このエラーが出ます。 このエラーが出たときは、プログラム実行時に java コマンドに対して、main メソッドを持っているクラスを 指定しているか、確認しましょう。 195 第 15 章 クラス同士の関係と連携 オブジェクト指向でのプログラミングにおいては、現実世界の「もの」(オブジェクト)に注目し、オブジェク ト同士を互いに連携させることで、プログラムを動作させていくことを、14 章で説明しました。14 章では、クラ スやオブジェクトといった、オブジェクト指向での一番の基本部分と、オブジェクトが持つデータを中心に説明 をしました。本章では、クラスやオブジェクト同士の関係や連携方法、オブジェクト指向の特徴について説明を していきます。 15.1 クラス・オブジェクト同士でのメッセージやりとり クラスやオブジェクトの連携では、例えば図 15.1 や、図 15.2 のように、あるオブジェクト(クラス)に定義さ れているメソッドを、別のオブジェクト(クラス)が呼び出す、ということを行います。そして、こういった、メ ソッドを呼び出しあうオブジェクトのクラス同士の関係を関連と呼びます。オブジェクト指向でのクラス同士の主 な関係の 1 つです。 図 15.1: メソッドの呼び出し(図書館システム) 図 15.2: メソッドの呼び出し(教務システム) メソッドの呼び出しでは、呼び出す側が、相手に対して「これをして!」とお願いをします。そして、相手側が、 お願いされた処理を行う、というイメージです。そして、この処理は、お願いをされた側が担当する処理です。つ まり、図 15.1 では「自分のタイトルを見せる」という処理は「本」が行う処理(本クラスに定義されている処理) です。図 15.2 では「学級を設定する」という処理は「生徒」が行う処理(生徒クラスに定義されている処理)です。 このように、オブジェクト同士が行うやりとりをメッセージと呼びます。メッセージとは、つまり、相手側への 処理の依頼(メソッドを呼び出すこと)です。このメッセージを、様々なオブジェクト同士でやりとりすること で、プログラムが動作していきます。 関連には、メッセージを送る側と、メッセージを受け取る側がいます。プログラムでは、メッセージを送る側の クラス内で「受け取る側. メソッド」という形で、記述をすると、メッセージを送ったことになります。そしてこ れにより、受け取る側のメソッドを呼び出すことができます。 15.1.1 インスタンスメソッド インスタンスメソッドは、14.7.1 節で説明したとおり、オブジェクトに関連付けられたメソッドです。従って、 オブジェクトの変数名. メソッドの形式でメソッドを呼び出します。この「オブジェクトの変数名」で指定される オブジェクトが、メソッドの処理を担当するオブジェクトです。つまり、このオブジェクトのクラスに、メソッド が定義されています。 インスタンスメソッドの呼び出しは、オブジェクトごとでのメソッドの呼び出しとなります。従って、同じクラ スの同じ名前・引数・返り値のデータ型のメソッドを呼び出したとしても、メソッドの処理を行うオブジェクト が異なれば、処理の結果が異なります。 第 15 章 クラス同士の関係と連携 196 図 15.3 は、図書館システムでのインスタンスメソッドの呼び出しのイメージです。図書館システムが、2 つの オブジェクトに対して、それぞれのオブジェクトのタイトルを見せるよう、メッセージを送っています。どちら も「自分のタイトルを見せる」という同じメソッドを呼び出しています。しかし、そのメソッドの処理を行うオブ ジェクトが異なり、それぞれ異なるタイトルを持っているため、返してくる返り値も異なっています。 図 15.3: インスタンスメソッドの呼び出しイメージ(図書館システム) 図 15.4 は、図 15.3 のイメージをプログラムにしたものです。 図 15.4: インスタンスメソッドの呼び出し例(図書館システム) この例では、まず、LibrarySystem クラスで、Book オブジェクトの配列変数 entry に対し、entry[0] と entry[1] に、図書館蔵書 ID やタイトルなどのフィールド変数の値を設定しています。そして、オブジェクト entry[0] のインスタンスメソッドの呼び出しをしています。呼び出したインスタンスメソッドを使って、フィー ルド変数の値を取得し、標準出力で出力しています。 この例では、配列である entry において、entry[0] と entry[1] に対して、フィールド変数の値が設定されて いますが、インスタンスメソッドの呼び出しは、entry[0] に対してのみ行われています。インスタンスメソッド は、メソッドを処理するオブジェクトによって、処理結果が異なってきます。従って、標準出力で出力される値 は、entry[0] に対して設定されている、フィールド変数の値です。 図 15.5 は、教務システムでのインスタンスメソッドの呼び出しのイメージです。教務システムが、2 つのオブ ジェクトに対して、それぞれの学級を設定するよう、メッセージを送っています。どちらも「自分の学級を設定す る」という同じメソッドを呼び出しています。しかし、そのメソッドの処理を行うオブジェクトが異なり、それぞ れのオブジェクトに対して別の学級を送っているため、それぞれのオブジェクトに設定される学級の値も異なり ます。 図 15.6 は、図 15.5 のイメージをプログラムにしたものです。 15.1. クラス・オブジェクト同士でのメッセージやりとり 197 図 15.5: インスタンスメソッドの呼び出しイメージ(教務システム) 図 15.6: インスタンスメソッドの呼び出し例(教務システム) この例では、フィールド変数の値を取得して、それを標準出力で出力する部分は、図 15.4 の図書館システムと 同様です。 この図 15.6 の例では、まず、SchoolSystem クラス内で、Student オブジェクトの配列変数 member において、 member[0] と member[1] に対して、メソッド使って学級を設定しています。オブジェクト member[0] に対しては 「A 組」、member[1] に対しては「B 組」という引数が与えられています。 インスタンスメソッドは、メソッドを処理するオブジェクトによって、処理結果が異なってきます。そのため、 member[0] のフィールド変数 classCode には「A 組」、member[1] には「B 組」と設定されることになります。 <コラム> String クラスや BufferedReader クラスの変数に対して使ったメソッドはインスタンスメソッド これまで、文字列を扱うときに、String クラスの indexOf や substring など、また入力のときには BuferedReader クラスの readLine といったメソッドを使ってきました。String や BufferedReader は、Java であらかじめ用 意されているクラスです。そしてこれらのメソッドは、クラスの変数を宣言し「変数名. メソッド」という形式で 使ってきました。 つまり、これらのメソッドは、インスタンスメソッドとして、String クラスや BufferedReader クラスに定義され ているということです。そして、これらのクラスをデータ型として宣言していた変数が、String や BufferedReader などのオブジェクトになります。 第 15 章 クラス同士の関係と連携 198 <コラム> System.out.println と System.out.print は、クラス変数+インスタンスメソッド これまで、標準出力をするために System.out.println や System.out.print を使ってきました。このしくみ としては、まず System という、Java であらかじめ用意されているクラスがあります。このクラス内に out という クラス変数が定義されています。この out は、PrintStream というクラスの変数です。そして、この PrintStream クラスのインスタンスメソッドとして println と print が定義されています。 このように、System.out.println と System.out.print は System というクラス名の out というクラス変数 を呼び出し、このクラス変数 out をもとにして println や print というインスタンスメソッドを呼び出す、とい う構造になっています。 15.1.2 クラスメソッド クラスメソッドは、14.7.2 節で説明したとおり、クラスに関連付けて呼び出すメソッドです。従って、呼び出す オブジェクトによって、処理の結果が異なるということはありません。また、インスタンスメソッドと同様に「オ ブジェクトの変数名. メソッド」の形式でも呼び出すことはできますが、クラス名. メソッドの形式で呼び出す方 が一般的です。 図 15.7 は、図書館システムでのクラスメソッドの呼び出しのイメージです。クラスメソッドは、14.7.2 で説明 したとおり、インスタンス変数にアクセスことはできません。クラスに関連付けられたクラス変数にはアクセス できます。従って、このイメージでも、クラス変数である「所有者」にアクセスをしています。 図 15.7: クラスメソッドの呼び出しイメージ(図書館システム) 図 15.8 は、図 15.7 のイメージを図にしたものです。 15.1. クラス・オブジェクト同士でのメッセージやりとり 199 図 15.8: クラスメソッドの呼び出し例(図書館システム) この例では、LibrarySystem クラスにおいて、Book クラスの owner というフィールド変数の値をメソッドで 取得し、標準出力で出力しています。クラスメソッドは、クラスに関連付けられたメソッドで、オブジェクトに よって処理結果が異なることはありません。そのため Book.getOwner() という「クラス名. メソッド」の形式で、 クラス名をもとに呼び出しています。 図 15.9 は、教務システムでのクラスメソッドの呼び出しのイメージです。このイメージでは、クラスメソッド を使って、クラス変数である「教務システム上の身分」にアクセスをしています。 図 15.9: クラスメソッドの呼び出しイメージ(教務システム) 図 15.10 は、図 15.9 のイメージを図にしたものです。 第 15 章 クラス同士の関係と連携 200 図 15.10: クラスメソッドの呼び出し例(教務システム) この例では、SchoolSystem クラスにおいて、Student クラスの position というフィールド変数の値をメソッ ドで設定しています。クラスメソッドである setPosition を Student.setPosition(引数) という「クラス名. メ ソッドの」形式で呼び出し、Student クラスのクラス変数に値を設定しています。 <コラム> 文字列を数値に変換するメソッドはクラスメソッド これまで、入力された文字列を数値に変換するときに Integer.parseInt や Float.parseFloat、Double.parseDouble という形式でメソッドを使ってきました。Integer や FloatDouble は、Java であらかじめ用意されているクラス です。つまり parseInt や parseFloatparseDouble はクラスメソッドとして定義されています。 クラスメソッドとして定義されているため、これらのメソッドは「変数名. メソッド」という形式ではなく「ク ラス名. メソッド」という形式で利用してきたのです。 15.2 継承 プログラムを作成していると、例えば、あるクラスの性質を持たせたまま、アレンジしたクラスを作りたい、と か、同じような性質を持つクラスの共通部分をまとめたい、ということはよくあります。オブジェクト指向では、 それを行うしくみが用意されています。そのしくみを継承と呼びます。 15.2.1 継承とは? 継承は、オブジェクト指向でのクラス同士の主な関係の 1 つです。クラスの親子関係と言えるものです。 例えば、図書館システムにおいて、14 章では「本」クラスを考えてきました。これに加えて「コミックス」ク ラスも考えてみましょう。また、教務システムにおいては「生徒」クラスを考えてきました。これに加えて「ス ポーツコース生」クラスも考えてみましょう。そしてこれらのクラスについて、類似性と違いを見てみましょう。 表 15.1 は、これらのクラスのフィールドとメソッドの一覧です1 。 1 フィールドとメソッドは、本書で扱う範囲のものだけ掲載しています 15.2. 継承 201 表 15.1: クラスのフィールドとメソッド一覧 図書館システム 教務システム クラス 本 コミックス 生徒 スポーツコース生 フィールド 著者 著者 氏名 氏名 タイトル タイトル 学級 学級 出版年 出版年 出席番号 出席番号 ジャンル 専門種目 巻数 メソッド 自分のタイトルを見せる 自分のタイトルを見せる 自分の氏名を見せる 自分の氏名を見せる この表を見ると、本クラスとコミックスクラス、生徒クラスとスポーツコース生クラスは、ほとんどのフィー ルドとメソッドが共通していることがわかります。図書館システムにおいては、コミックスクラスのフィールド 「ジャンル」が「本クラス」にはなく、共通ではありません。教務システムにおいては、スポーツコース生クラス のフィールド「専門種目」が生徒クラスにはなく、共通ではありません。 逆に言えば、コミックスクラスは、本クラスが持つフィールドとメソッドを、すべて持っています。スポーツ コース生クラスは、生徒クラスが持つフィールドとメソッドを、すべて持っています。 また、このように、プログラムでのフィールドやメソッドではなく、現実世界での概念に照らし合わせて考え てみます。図 15.11 は、本とコミックスの関係です。この図のように、コミックスは、すべて本の一部です。つま り、本とは、コミックスをすべて含む大きな概念であり、コミックスは本を詳細化した分類です。すべての本をコ ミックスと呼ぶわけにはいきませんが、すべてのコミックスは、本と読んでも差し支えありません。 図 15.11: 本とコミックスの包含関係 図 15.12 のは、生徒とスポーツコース生の関係です。生徒とスポーツコース生も、本とコミックスの関係と同様 です。つまり、スポーツコース生は、すべて生徒の一部です。生徒とは、スポーツコース生を含む大きな概念であ り、スポーツコース生は、生徒を詳細化した分類です。すべての生徒をスポーツコース生として扱うわけにはいき ませんが、すべてのスポーツコース生は、生徒として扱っても差し支えありません。 第 15 章 クラス同士の関係と連携 202 図 15.12: 生徒とスポーツコース生の包含関係 このように、プログラムで扱うクラスには、包含関係になるクラス同士も存在します。クラス同士の包含関係 は、含まれる側(小さい側)が含む側(大きい側)のフィールドとメソッドをすべてそのまま持っている、という 関係です。このような関係が、クラス同士の親子関係です。そして、含まれる側のクラスを含む側のクラスを継承 している、と呼びます。つまり、コミックスクラスは本クラスを継承していて(図 15.13)、スポーツコース生ク ラスは生徒クラスを継承しています(図 15.14)。なお、この 3 段の箱では、1 段目にクラスの名前、2 段目にクラ スに定義されているフィールドの一覧、3 段目にクラスに定義されているメソッドの一覧を書いています2 。 図 15.13: コミックスクラスが本クラスを継承 図 15.14: スポーツコース生クラスが生徒クラスを継承 つまり継承とは、あるクラスが、別のクラスのフィールドとメソッドをそのまま受け継いでいるという関係で す。受け継がせる側を親クラス(スーパークラス)、受け継ぐ側を子クラス(サブクラス)と呼びます。従って、 本クラスはコミックスクラスの親クラスです。コミックスクラスは本クラスの子クラスです。生徒クラスはスポー ツコース生クラスの親クラスです。スポーツコース生クラスは生徒クラスの子クラスです。 親クラスは、子クラスを完全に含むものでなくてはなりません。つまり、子クラスが持たないフィールドやメ ソッドを、親クラスが持っていることはありません。また、1 つの親クラスに対し、子クラスは複数あってかまい ません。従って、例えば本クラスであれば、コミックスクラス以外にも旅行書クラス、料理書クラスなど、様々な 子クラスが考えられます。生徒クラスであれば、スポーツコース生クラス以外にも、特進コース生クラスや音楽 コースなどの子クラスが考えられます。 また、Java では、1 つの親クラスは複数の子クラスを持ますが、1 つの子クラスが持つことのできる親クラスは 1 つだけです。従って、コミックスクラスの親クラスは、本クラスだけです。スポーツコース生クラスの親クラス は、生徒クラスだけになります。 2 ソフトウェア開発では、クラス同士の関係などを図で表現して設計をし、プログラムを作成します。この図には、多くの場合、 UML (Unified Modeling Language)という表記法を使います。UML では、クラスをこのように 3 段の箱で表現します。 15.2. 継承 15.2.2 203 継承の定義 プログラムで、継承を表現するには、子クラスのクラス宣言において、図 15.15 のように、クラス名の後に extends というキーワードで親クラスの名前を指定します。親クラスに対しては何もする必要はありません。 図 15.15: 継承の定義方法 図 15.16 は、図書館システムでのコミックス(Comics)クラスのプログラム例です。本(Book)クラスを継承し ています。図 15.16 は教務システムでのスポーツコース生(SportCourseStudent)クラスのプログラム例です。 生徒(Student)クラスを継承しています。 図 15.16: コミックスクラスが本クラスを継承 15.2.3 図 15.17: スポーツコース生クラスが生徒クラスを継承 継承したクラスの利用 15.2.2 節で説明したとおり extends キーワードをつけることで、あるクラスの性質をそっくりそのまま受け継 いだクラスを作成することができます。「性質をそっくりそのまま受け継ぐ」とは、親クラスが持つフィールド変 数やメソッドを、そのまま子クラスが受け継ぐ、ということです。つまり、親クラスが持つフィールド変数やメ ソッドを、自動的に子クラスも持つ、ということです。子クラスの方で、親クラスが持つフィールド変数やメソッ ドを定義する必要はありません。 図 15.18 は、図書館システムにおいて、Book クラスを継承した Comics クラスのフィールド変数やメソッドへ、 LibrarySystem クラスがアクセスするプログラムです。 この図 15.18 では、Comics クラスが Book クラスを親クラスとして、フィールド変数やメソッドを受け継いで います。Comics クラスに書かれている、Book クラスから継承したフィールド変数やメソッドは、実際のプログラ ムでは書きません。Comics クラスが Book クラスを継承することで、Comics クラス内に定義することなく、自動 的に持つフィールドやメソッドです。 そして、この Comics クラスのオブジェクトを LibrarySystem クラスで作成し、フィールド変数に値を代入し たり、メソッドを利用したりしています。category というフィールド変数と getCategory というメソッドは、 Comics クラスで定義されているものです。従って、LibrarySystem クラスからアクセスしても問題ありません。 その他の id や title などのフィールド変数と getID や getTitle などのメソッドは、Comics クラス内では定 義していないものです。普通に考えれば、定義していないフィールド変数やメソッドを利用している、ということ で、コンパイル時にエラーが出るように思えるでしょう。 しかし、Comics クラスは、Book クラスを継承しています。従って、実際に Comics クラス内では定義していな くても、Book クラスが持つフィールド変数やメソッドは、自動的に Comics クラスに対して定義されています。そ のため、図 15.18 のように id や title などのフィールド変数と getID や getTitle などのメソッドを利用して も、コンパイル時にエラーにはなりません。 第 15 章 クラス同士の関係と連携 204 図 15.18: 図書館システムでの子クラスへのアクセス 図 15.19 は、教務システムにおいて、Student クラスを継承した SportCourseStudent クラスのフィールド変 数やメソッドへ、SchoolSystem クラスがアクセスするプログラムです。 図 15.19: 教務システムでの子クラスへのアクセス この図 15.19 では、SportCourseStudent クラスが Student クラスを親クラスとして、フィールド変数やメソッ ドを受け継いでいます。図書館システムでの Comics クラスと同様に、SportCourseStudent クラス内で定義する ことなく、Student クラスから、フィールド変数やメソッドを受け継いでいます。 そして、この SportCourseStudent クラスのオブジェクトを SchoolSystem クラスで作成し、フィールド変数 に値を代入したり、メソッドを利用したりしています。sport というフィールド変数と getSport というメソッド は、SportCourseStudent クラスで定義されているものであるため、アクセスしても問題ありません。 その他の name や classCode などのフィールド変数と getName や getClassCode などのメソッドは、Student クラスから受け継いだものです。これらも、図書館システムの Comics クラスと同様に、Student クラスが持つ フィールド変数やメソッドは、自動的に SportCourseStudent クラスに対して定義されています。そのため、図 15.19 のように name や classCode などのフィールド変数と getName や getClassCode などのメソッドを利用し ても問題ありません。 15.2. 継承 205 このように、クラスの継承を行うと、子クラスは親クラスに定義されたフィールド変数やメソッドをすべて受 け継ぎます。そしてそれらを、あたかも自分のクラス内で定義されているフィールド変数やメソッドであるかのよ うに、利用することができます。 クラスの継承を利用すると、あるクラスを基準にして、様々なバリエーションを持ったクラスを作成したり、様々 なクラスが共通して持つ性質をまとめて定義することができます。 15.2.4 子クラスのオブジェクトを親クラスのオブジェクトとして利用 継承を使って子クラスを作ると、子クラスのオブジェクトは、親クラスのオブジェクトとして利用できます。 15.2.5 節や 15.2.6 節で説明しますが、子クラスには、親クラスとは違う独自のフィールドやメソッドを定義でき ます。しかし、15.2.1 節で説明したとおり、親クラスは、子クラスのより大きな概念です。つまり、子クラスのオ ブジェクトは、すべて、親クラスのオブジェクトであるということもできるのです。 15.2.5 節や 15.2.6 節で説明するように、子クラスは独自のフィールドやメソッドを持つことができます。従っ て、子クラスのオブジェクトを使って、それぞれ親クラスのオブジェクトは異なる処理を行うことも可能です。 しかし、常に親クラスのオブジェクトと子クラスのオブジェクトは、異なる処理に使われるとは限りません。例 えば図書館システムであれば、利用者に本の貸し出しをします。この貸し出しのときに行われる処理は、コミック スであっても、コミックス以外の本であっても、全く同じ処理をすることが考えられます。また、教務システムで あれば、生徒の試験の得点をもとに、平均点を計算したり、順位付けをするという処理を行うときに、スポーツ コース生と、それ以外の生徒で、違う処理はしないでしょう。 このような、親クラスのオブジェクトと子クラスのオブジェクトを使った処理で、全く同じ処理をしたい場合 に、これらを別々に扱わなければならないとすれば、それは大変不便です。そこで、子クラスのオブジェクトは、 親クラスのオブジェクトとして利用できるようになっているのです。言い換えれば、子クラスのオブジェクトは、 親クラスのオブジェクトでもある、ということができます。 図 15.20 は、図書館システムの LibrarySystem クラスにおいて、Comics クラスのオブジェクトに対してフィー ルド変数に値を設定し、設定した情報を標準出力で出力するプログラムです。 図 15.20: 子クラスのオブジェクトを親クラスのものとして利用(図書館システム) 図 15.20 の例では、LibrarySystem クラスにおいて createComicsEntry というメソッドで、Comics クラスの オブジェクトに対して、フィールド変数の値を設定しています。この createComicsEntry メソッドは、Comics ク ラスのオブジェクトの配列を返り値としています。そして、この createComicsEntry メソッドの返り値は、Book クラスをデータ型とする配列に代入され、オブジェクトのフィールド変数の値が標準出力で出力されています。 ここで、createComicsEntry メソッドの返り値は Comics 型であり、今回、この返り値を代入している変数は Book 型です。つまり、返り値のデータ型と、返り値を代入している変数のデータ型が違います。しかし、Comics クラスは Book クラスの子クラスであるため、Comics クラスのオブジェクトは、Book クラスのオブジェクトであ るとも言えます。そのため、このように、Comics クラスのオブジェクトを Book クラスの変数に代入してもかま いません。 第 15 章 クラス同士の関係と連携 206 図 15.21 は、教務システムの SchoolSystem クラスにおいて、SportCourseStudent クラスのオブジェクトに 対してフィールド変数に値を設定し、5 教科の得点の平均点を計算し、他の情報情報とともに標準出力で出力する プログラムです。 図 15.21: 子クラスのオブジェクトを親クラスのものとして利用(教務システム) 図 15.21 の例では、SchoolSystem クラスにおいて、main メソッドで、SportCourseStudent クラスのオブジェ クトに対して、フィールド変数の値を設定しています。SportCourseStudent クラスのオブジェクトは配列です。 そしてこの配列を、printStudentInfo メソッドの引数として与えています。printStudentInfo メソッドでは、 与えられたオブジェクトのフィールド変数の値から、5 教科の得点の平均点を計算し、他のフィールド変数の値と ともに標準出力で出力しています。 この printStudentInfo メソッドは、Student クラスのオブジェクトの配列を引数としています。しかし、今回、 この printStudentInfo メソッドに与えられている引数は、SportCourseStudent クラスのオブジェクトです。つ まり、引数として定義されているデータ型と、実際に引数として与えられている変数のデータ型が違います。しか し、SportCourseStudent クラスは Student クラスの子クラスであるため、SportCourseStudent クラスのオブ ジェクトは、Student クラスのオブジェクトであるとも言えます。そのため、このように、SportCourseStudent クラスのオブジェクトを Student クラスのオブジェクトとして扱ってもかまいません。 <コラム> instanceof 演算子 子クラスのオブジェクトは、親クラスのオブジェクトとして扱うことができることを説明しました。このよう にすると、親クラスのオブジェクトと子クラスのオブジェクトが、配列の中などで入り混じってしまい、どれが 親クラスのオブジェクトで、どれが子クラスのオブジェクト化、区別がつかなくなることもあります。しかし、子 クラスには子クラスの独自のフィールド変数やメソッドを定義しているため子クラスのオブジェクトを、親クラ スのオブジェクトと区別して扱いたい場合もあります。 そのような場合には、instanceof 演算子を使うことができます。この演算子は ¶ ³ オブジェクトの変数名 instanceof クラス名 µ ´ という形式で使うと、そのオブジェクトが、厳密には instanceof で指定されているクラスのオブジェクトだった 場合には true、そうでなかった場合には false と、boolean 型の結果を返します(図 15.22)。そこで、この演 算子を使って条件分岐などをすると、親クラスのオブジェクトと子クラスのオブジェクトを区別することができ ます。 15.2. 継承 207 図 15.22: instanceof 演算子の使い方 15.2.5 拡張 例えば、あるクラス A を親クラスとして、子クラス B と C を作成することを考えます。このとき、クラス A が 持つフィールドやメソッドは、すべてクラス B と C に受け継がれます。 これだけでは、クラス B と C はクラス A のコピーでしかないので、あまり意味がありません。そこで、クラス B と C には、それぞれ独自のフィールドとメソッドを持たせることができます。 例えば、図 15.23 は、図書館システムでのコミックスクラスと旅行書クラスが本クラスを継承している例です。 図 15.24 は、そのプログラム例です。 図 15.24: 図書館システムでの子クラスの拡張(プログ 図 15.23: 図書館システムでの子クラスの拡張 ラム) この例では、Comics(コミックス)クラスと TripGuide(旅行書)クラスは、title(タイトル)や author(著 者)などのフィールド変数と getTitle(自分のタイトルを見せる)や getAuthor メソッドを Book(本)クラス から受け継いでいます。そして、Comics クラスは「category(ジャンル)」や「volume(巻数) などのフィールド 変数と getCategory(自分のジャンルを見せる)や setVolume(巻数を設定する)というメソッドを独自で定義 しています。また、TripGuide クラスは area(地域)というフィールドを独自で定義しています。 図 15.25 は、教務システムでのスポーツコース生クラスと特進コース生クラスが生徒クラスを継承している例で す。図 15.26 は、そのプログラム例です。 第 15 章 クラス同士の関係と連携 208 図 15.25: 教務システムでの子クラスの拡張 図 15.26: 教務システムでの子クラスの拡張(プログラム) この例では、SportCourseStudent(スポーツコース生)クラスと SpecialCourseStudent(特進コース生)は、 name(名前)や classCode(学級)などのフィールド変数と getName(自分の氏名を見せる)や getClassCode メソッドを Student(生徒)クラスから受け継いでいます。そして、SportCourseStudent クラスは sport(専 門種目)のフィールド変数と getSport(自分の専門種目を見せる)というメソッドを独自で定義しています。 SpecialCourseStudent クラスは setTarget(目標校を設定する)というメソッドを独自で定義しています。 図 15.24 や図 15.26 のプログラムを見てもわかるとおり、子クラスにおいては、親クラスから受け継いだフィー ルド変数やメソッドを定義していません。言い換えれば、共通のフィールド変数やメソッドは、すべて親クラス でまとめて定義されています。そして、子クラスには、子クラス独自のフィールド変数やメソッドのみが定義され ています。 このようにすることで、各クラスに共通のフィールド変数・メソッドと、各クラスで独自のフィールド変数・メ ソッドを、明確に分離できます。これはつまり、各クラスの役割分担が明確になるということです。そうすると、 一旦作成したクラスを修正することになったとき、どの部分を修正すれば良いかがわかりやすくなります。 また、子クラスのフィールド変数やメソッドを、すべて親クラスに定義しておくこともできます。しかしそうす ると、ある子クラス独自のフィールド変数やメソッドは、別の子クラスでは不要なものです。子クラスによって は不要なものを、すべて親クラスに定義しておくと、親クラス内に記述されている内容が多くなってしまします。 そうすると、修正のときに、クラスの内容の見通しが悪くなり、修正すべき箇所がわかりにくくなります。 15.2.6 抽象化 15.2.5 節では、あるクラスから子クラスを作ることについて説明しました。逆に、あるクラスをもとに、親クラ スを作る場合も考えられます。 例えば、クラス B と C が作成されていて、それを見直したとき、フィールドやメソッドが共通していることが わかったとします。このとき、クラス B と C の共通のフィールドやメソッドをまとめて、クラス A を作成する、 ということができます。 例えば、図 15.27 は、図書館システムでの Comics クラスと TripGuide クラスのプログラムの例です。今回は、 この 2 つのクラスが別々に作成されたとします(これまで扱ってきた Book クラスは、今回はまだ作成されていな いとします)。 15.2. 継承 209 図 15.27: 図書館システムでのクラス同士の共通点 図 15.27 の例を見ると、2 つのクラスは異なるクラスですが、多くの共通点があります。まず、id や title な どのフィールド変数がいくつか共通しています。そして、getID や getTitle などのメソッドも、いくつか共通し ています。 そこで、この共通点をまとめることを考えます。図 15.28 は、Comics クラスと TripGuide クラスの共通点をま とめた例です。 図 15.28: 図書館システムでのクラスの抽象化 Comics クラスと TripGuide クラスを含むより大きな概念として、Book クラスが考えられます。そこで、Book クラスを作成し、Comics クラスと TripGuide クラスは、この Book クラスを継承するように修正します。 次に、Book クラスに、Comics クラスと TripGuide クラスの共通点をまとめます。共通するフィールド変数と メソッドを、Book クラス内に移動します。これで、Comics クラスと TripGuide クラスの共通点が Book クラス にまとまり、Comics クラスと TripGuide クラスには、それぞれの独自のフィールドやメソッドのみが残ることに なります。 また、図 15.29 の例は、教務システムでの例です。この例でも、SportCourseStudent クラスと SpecialCourseStudent クラスで、いくつかのフィールドや属性が共通しています。 第 15 章 クラス同士の関係と連携 210 図 15.29: 教務システムでのクラス同士の共通点 図 15.30 は、SportCourseStudent クラスと SpecialCourseStudent クラスの共通点をまとめた例です。 図 15.30: 教務システムでの子クラスの拡張(プログラム) この例では、SportCourseStudent クラスと SpecialCourseStudent クラスの共通点をまとめて、この 2 つの クラスを含むより大きな概念である Student クラスを作成しています。そして、SportCourseStudent クラスと SpecialCourseStudent クラスは、Student クラスを継承するように修正します。 次に、Student クラスに、SportCourseStudent クラスと SpecialCourseStudent クラスの共通点、つまり共 通するフィールド変数とメソッドを、Student クラス内に移動します。これで、SportCourseStudent クラスと SpecialCourseStudent クラスの共通点が Studnet クラスにまとまり、子クラスとなった 2 つのクラスには、そ れぞれの独自のフィールドやメソッドのみが残ることになります。 共通するフィールド変数やメソッドをそのままにしておくと、修正が必要となったときに面倒です。共通のフィー ルド変数やメソッドを、複数のクラスで定義していると、その共通の部分の修正が必要になったときに、それぞ れのクラスで同じ内容の修正をしなければなりません。 そこで、共通するフィールド変数やメソッドを、親クラスとしてまとめておくと、その共通部分は、1 つのクラ ス内のみに書かれていることになります。そうすると、共通部分の修正が必要になったときには、その親クラスの みの修正ですむことになります。 15.3 オーバーライド 15.2.2 節で説明したとおり、あるクラスを親クラスとして、それを継承して子クラスを作ると、その子クラスに は、親クラスに定義されているフィールド変数やメソッドが、そのまま受け継がれます。 しかし、親クラスから受け継がれたメソッドを、子クラスでは、違う内容にしたい場合もあります。この場合、 メソッドを子クラスで定義しなおすことができます。このメソッドの定義のしなおしをオーバーライドと呼びま す。オーバーライドをするには、以下の条件があります。 15.3. オーバーライド 211 • メソッド宣言において、以下の部分は、親クラスで定義されているメソッドとすべて同じでなければななら ない – メソッドの名前 – 引数のデータ型(引数の変数名は異なっても良い) – 引数の個数 – 引数の順序 – 返り値のデータ型 • 親クラスで定義されているものより、アクセスできる範囲の狭いアクセス修飾子で、メソッドをオーバーラ イドすることはできない(表 15.2: オーバーライドするとき、親クラスのメソッドでつけられているアクセ ス修飾子と、オーバーライド時に子クラスでつけることが可能なアクセス修飾子の対応) 表 15.2: オーバーライド時のアクセス修飾子の対応 親クラスでつけられているアクセス修飾子 子クラスでつけることが可能なアクセス修飾子 public public のみ protected または無修飾 public, protected, 無修飾 private public, protected, private, 無修飾 • 親クラスで、static 修飾子なしで定義されているメソッドは、オーバーライドするときも static 修飾子を つけてはならない • 親クラスで、static 修飾子をつけて定義されているメソッドは、オーバーライドするときも static 修飾子 をつけなければならない • 親クラスで、final 修飾子をつけて定義されているメソッドを、オーバーライドすることはできない 図 15.31 は、図書館システムでのメソッドのオーバーライドの例です。 図 15.31: 図書館システムでのオーバーライド 図 15.31 の例では、本の ID を設定する setID というメソッドを考えます。親クラスの Book では、引数として 与えられた値を、そのままフィールド変数 id の値として設定します。しかし、Book クラスの子 Comics クラスの オブジェクトの ID には、先頭に「c」とつけたいとします。そのため、引数として与えられた値が「c」で始まっ ていなければ「c」をつけ、値が「c」で始まっていれば、そのままで、フィールド変数 id の値として設定するよ う setID メソッドをオーバーライドしています。 図 15.32 は、教務システムでのメソッドのオーバーライドの例です。 第 15 章 クラス同士の関係と連携 212 図 15.32: 教務システムでのオーバーライド 図 15.32 の例では、試験の成績の平均点を返り値とする calcAverage というメソッドを考えます。親クラスの Student では、数学・国語・英語・理科・社会の 5 教科の得点の平均を計算し、返り値としています。しかし、 Student クラスの子 SportCourseStudent クラスのオブジェクトでは、5 教科に加えて体育の得点も入れて、平 均点を計算したいとします。そのため、5 教科に体育を入れた 6 教科で平均点を計算するよう calcAverage メソッ ドをオーバーライドしています。 このように、子クラスでメソッドを定義しなおすことで、子クラスに様々な特色を持たせることができます。 <ミニテクニック> 親クラスで定義しているフィールド変数やメソッドへのアクセスは super を使う 例えば、親クラスで定義しているフィールド変数やメソッド使って、新しいメソッドを作ったり、メソッドの オーバーライドをしたいとします。そのとき、super というキーワードを使うと、親クラスで定義しているもの を、子クラス内部で使うことができます。super とは、親クラス、という意味のキーワードです。図 15.33 のよう に「super. フィールド変数」や「super. メソッド」として利用します。 図 15.33: super キーワードの利用 特にこの例のように、子クラスでオーバーライドするメソッドの処理に、親クラスのオーバーライド対象のメ ソッドを処理に使いたい場合に、この super は有効です。super.setID の部分で、親クラスの serID メソッドが 呼び出されています。 15.4 ポリモーフィズム 15.2.4 節で、子クラスのオブジェクトを親クラスのオブジェクトとしても利用できる、ということを説明しまし た。また、同じ名前で引数や返り値の異なるメソッドを定義できる、ということを説明し、さらに、15.3 節では、 オーバーライドについても説明しました。これらの特性により、メソッドの呼び出し側は、呼び出すメソッドの 形式や、そのメソッドに関連付けられたオブジェクトのクラスが、実際に何であるかを意識することなく、メソッ ドの呼び出しを行うことができます。 オーバーライドやオーバーロードで、同じ名前のメソッドを、プログラム中の複数箇所で定義する、ということ はよくあります。そして、プログラムの呼び出し処理の記述では、どの箇所に定義されたメソッドを呼び出すかは 15.4. ポリモーフィズム 213 気にしなくて良いのです。プログラムの実行時に、どの箇所に記述されたメソッドを呼び出すかが決定されます。 これをポリモーフィズム(多相性・多態性)と呼びます。 図 15.34 は、図書館システムでのポリモーフィズムのイメージです。 図 15.34: 図書館システムでのポリモーフィズム この例では、子クラスであるコミックスクラスが、親クラスである本クラスの「ID を設定する」というメソッ ドをオーバーライドしています。そして、この「ID を設定する」メソッドの呼び出しを記述している呼び出し側 は、コミックスクラスのことは意識せず、本クラスのオブジェクトの「ID を設定する」メソッドを呼び出すよう に、プログラムに記述します。 そうすると、プログラム実行時に「ID を設定する」メソッドに関連付けられているオブジェクトが、本クラス のオブジェクトであれば、本クラスの「ID を設定する」メソッドが呼び出されます。実際にはコミックスクラス のオブジェクトだった場合には、コミックスクラスでオーバーライドされたメソッドが呼び出されます。 図 15.35 は、図 15.34 のイメージをプログラムにしたものです。 図 15.35: 図書館システムでのポリモーフィズム(プログラム) このプログラム中では、LibrarySystem クラスにおいて、main メソッド内で「ID を設定する」メソッドに相 当する setID メソッドが呼ばれています。setID メソッドに関連付けられているのは entry という配列変数のオ ブジェクトです。この entry は、Book クラスのオブジェクトとして宣言されています。つまり、プログラム記述 時には、setID メソッドは、Book クラスのオブジェクトに関連付けて呼び出されるように記述されています。 第 15 章 クラス同士の関係と連携 214 この entry という配列には、createBookEntry というメソッドの返り値が代入されています。この createBookEntry というメソッドでは、配列の 0 番目に対して Book クラスのオブジェクトを代入し、配列の 1 番目に対して Comics クラスのオブジェクトを代入しています。つまり、main メソッドにおいて、entry[0] は Book クラスのオブジェ クト、entry[1] は Comics クラスのオブジェクトになります。 そうすると、プログラム実行時には entry[0].setID("0001") は、Book クラスで定義されているメソッドの 呼び出しになります。このメソッドは、フィールド変数 id に、setID メソッドの引数をそのまま代入します。 entry[1].setID("1000") は、Comics クラスでオーバーライドされているメソッドの呼び出しになります。こ のメソッドは、フィールド変数 id に、setID メソッドの引数を代入しますが、この引数の先頭に「c」がついて いなければ「c」をつけたものを代入します。従って、この場合、フィールド変数 id には c1000 が代入されるこ とになります。 そのため、main メソッドの最後の標準出力では entry[0].id の出力は 0001entry[1].id の出力は c1000 とな ります。 図 15.36 は、教務システムでのポリモーフィズムのイメージです。 図 15.36: 教務システムでのポリモーフィズム この例では、子クラスであるスポーツコース生クラスが、親クラスである生徒クラスの「試験の得点の平均を計 算する」というメソッドをオーバーライドしています。そして、図書館システムと同様に、呼び出し側は、生徒ク ラスのオブジェクトの「試験の得点の平均を計算する」メソッドを呼び出すように、プログラムには記述します。 そうすると、プログラム実行時に「試験の得点の平均を計算する」メソッドに関連付けられているオブジェク トが、生徒クラスのオブジェクトであれば、生徒クラスの「試験の得点の平均を計算する」メソッドが呼び出さ れます。実際にはスポーツコース生クラスのオブジェクトだった場合には、スポーツコース生クラスでオーバーラ イドされたメソッドが呼び出されます。 図 15.37 は、図 15.36 のイメージをプログラムにしたものです。 このプログラム中では、SchoolSystem クラスにおいて、main メソッド内で、Student クラスと SportsCourseStudent クラスのオブジェクトが作成され、それぞれ配列の 0 番目と 1 番目に代入されています。そしてその配列を、 printStudentInfo メソッドの引数として与えています。 printStudentInfo メソッドの中では、Student クラスのオブジェクトの calcAverage メソッドが呼び出され ています。このとき、配列 student は、main メソッド内で作成され、フィールド変数に値を代入されたものです。 従って、student[0] は Student クラスのオブジェクト、studnet[1] は SportCourseStudent クラスのオブジェ クトということになります。 そうすると、プログラム実行時には student[0].calcAverage() は、Student クラスで定義されているメソッ ドの呼び出しになります。このメソッドは、5 教科の得点の平均点を計算して、返り値とします。 student[1].calcAverage() は、SportCourseStudent クラスでオーバーライドされているメソッドの呼び出 しになります。このメソッドは、5 教科に体育を加えた 6 教科の得点の平均点を計算して、返り値とします。 15.5. カプセル化 215 図 15.37: 教務システムでのポリモーフィズム(プログラム) そのため、この printStudentInfo メソッドの標準出力では student[0].calcAverage() の出力は、5 教科 の得点の平均(76.8)student[1].calcAverage() の出力は、6 教科の得点の平均(74.83333333333333)となり ます。 このように、ポリモーフィズムでは、実際に呼び出すメソッドの処理内容が違ったとしても、そのメソッド の呼び出しの処理を記述するときには、意識する必要がありません。ポリモーフィズムがなければ、それぞれ、 Book クラスの setID メソッドと Comics クラスの setID メソッド、Student クラスの calcAverage メソッドと SportStudentCourse クラスの calcAverage メソッドで、条件分岐をして記述する必要があります。ポリモーフィ ズムによって、そういう条件分岐を記述する必要がなくなり、プログラムの処理の記述を簡素にすることができ ます。 15.5 カプセル化 1.5 節において、オブジェクト指向においては、オブジェクトが持つデータに対し、他のオブジェクトから直接 アクセスをさせなくすることで、オブジェクト同士が互いに依存しなくなる、という説明をしました。オブジェク トが互いに依存しなくなれば、修正や再利用が容易になります。この、オブジェクトのデータ(フィールド)に、 他のオブジェクトから直接アクセスさせないことをカプセル化と呼びます。 15.5.1 フィールド変数への直接アクセスの問題点 図 15.38 や図 15.39 は、これまでに書いてきたプログラムの例です。これはカプセル化になっているでしょうか? 答えは「なっていない」です。 第 15 章 クラス同士の関係と連携 216 図 15.38: フィールド変数への直接アクセス(図書館シス 図 15.39: フィールド変数への直接アクセス(教務シス テム) テム) これらのプログラムでは「オブジェクトの変数名. フィールド変数名」という形で、フィールド変数に直接値を 代入したり、直接値を読み取ったり、つまり直接アクセスをしています。これは、カプセル化の定義である「直接 アクセスさせない」に反しています。 では、フィールド変数に直接アクセスすると、何がいけないのでしょうか? 図 15.40 と図 15.41 を見てみましょう。 図 15.40: フィールド変数への直接アクセスの問題点(図 図 15.41: フィールド変数への直接アクセスの問題点(教 書館システム) 務システム) 図 15.40 と図 15.41 では、どちらも、一度作ったクラスの内容を変更しています。図 15.40 では、Book クラス のフィールド変数 author(氏名)を 2 つ(姓と名)に分割しています。図 15.41 では、Student クラスのフィー ルド変数 grade のデータ型を int 型から String 型に変更しています。 そして、これらのフィールド変数には、別のクラス(LibrarySystem クラスや SchoolSystem クラス)からア クセスされています。このアクセスは、変更前であれば問題なくできています。しかし、変更後は、フィールド変 数の名前やデータ型が違ってしまったため、変更できなくなってしまいます。ということは、Book クラスの内容 を変更したために、LibrarySystem クラスを、Student クラスの内容を変更したために、SchoolSystem クラス を変更しなければならなくなってしまいます。 このように、フィールド変数に直接アクセスしていると、そのフィールド変数を変更したとき、それに伴って、 別のクラスの修正が必要になります。プログラムの修正は、修正箇所を探して修正することも大変ですが、修正 をミスすることもあり、そうするとまた修正、というような悪循環にもなりかねません。ですので、修正箇所はで きるだけ少ない方が望ましいものです。 一度作ったクラスを絶対に変更しないのであれば、フィールド変数へ直接アクセスしても良いのかもしれませ ん。しかし、一度作って、その後絶対に変更しない、ということはほとんどありません。多くの場合、何らかの 修正をします。従って、修正箇所は少ない方が良いのです。 15.5. カプセル化 15.5.2 217 カプセル化とは? カプセル化は、上記でも説明したとおり、フィールド変数に対して直接アクセスをさせないことです。しかし、 プログラムを動作させるには、様々な値を様々なオブジェクトに受け渡して処理していくことも必要です。では、 フィールド変数の値を、どのようにしてオブジェクトに受け渡すのでしょうか? それには、メソッドを使います。 図 15.42 と図 15.43 は、メソッドを使ってフィールド変数にアクセスするときのイメージです。 図 15.42: カプセル化のイメージ(図書館システム) 図 15.43: カプセル化のイメージ(教務システム) これらの例のように、他のオブジェクトから、フィールド変数へは直接アクセスをさせなくします。アクセスは 必ずメソッドを介して行うようにします。 そして、フィールド変数を変更したときには、そのフィールド変数を扱っているメソッドの処理内容を変更しま す。変更前と変更後で、返り値の形式は違わないようにします。もちろん、メソッドの名前や引数にも全く違いは ありません。従って、変更前にメソッドを使ってフィールド変数にアクセスしていたオブジェクトは、変更後も、 何の影響もなく、何の変更をする必要もなく、アクセスし続けることができます。 このようにして、フィールド変数を他のオブジェクトからアクセスさせなくする、つまり他のオブジェクトか ら隠す(隠蔽)してしまいます。 15.5.3 カプセル化をする方法 カプセル化を実際のプログラムで行うには、以下のようにします。 1. フィールド変数にアクセスするためのメソッドを用意する • フィールド変数の値を取得(見る)ためのメソッド(setter) • フィールド変数に値を設定するためのメソッド(getter) 2. フィールド変数には、アクセス修飾子 private を設定する 3. 他のオブジェクトからのフィールド変数のアクセスを、1. で用意したメソッドを使って行う フィールド変数にアクセスするためのメソッドは、通常、2 種類です。フィールド変数の値を取得(見る)ため のメソッド(setter)と、値を設定するためのメソッド(getter)です。これらのメソッドは、他のオブジェクト からアクセスをさせるためのものであるため、private 以外のアクセス修飾子をつけます。 値を取得するためのメソッドは、get + フィールド変数名という形式で名前をつけるのが一般的です。例えば、 フィールド変数名が title だとすると、getTitle という名前をつけます。 第 15 章 クラス同士の関係と連携 218 値を設定するためのメソッドは、set + フィールド変数名という形式で名前をつけるのが一般的です。例えば、 フィールド変数名が title だとすると、setTitle という名前をつけます。 アクセス修飾子は、14.8 節で説明したとおり private をつけると、他のクラスのオブジェクトからはアクセスで きなくなります。フィールド変数には原則的に private アクセス修飾子をつけるのが一般的です。これで、フィー ルド変数に、直接アクセスさせない、という体制が整います。そして、get や set で始まるメソッドを使って、 フィールド変数にアクセスします。 図 15.44 と図 15.45 は、カプセル化をしていて、フィールド変数の変更を行った場合のプログラムの例です。 図 15.44: カプセル化をしたプログラム例(図書館システム) 15.6. 集約 219 図 15.45: カプセル化をしたプログラム例(教務システム) これらの例では、Book クラスで変更があっても LibrarySystem クラスには影響していません。Student クラ スで変更があっても SchoolSystem クラスには影響していません。このように、フィールド変数には private アク セス修飾子をつけ、メソッドを介してフィールド変数にアクセスするようにしておきます。そうすると、フィール ド変数に変更があっても、その他のクラスは影響を受けず、修正をしなくてすむようにできます。 15.6 集約 オブジェクト指向においての主なクラス同士の関係として、これまで関連と継承を説明してきました。ここで は、もう 1 つ、集約について説明します。 15.6.1 集約とは? 集約とは、あるクラスのオブジェクトが、別のクラスのオブジェクトを所有する、という関係です。言い換えれ ば、あるクラスのオブジェクトが別のクラスオブジェクトの入れ物になっている、という関係です。 図 15.46 は、図書館システムでの集約のイメージです。 図 15.46: 図書館システムでの集約のイメージ 第 15 章 クラス同士の関係と連携 220 この例では、本棚について考えます。本棚とは、言うまでもなく、本を入れておくための棚、つまり本のための 入れ物と言えます。本は、本棚に入れられます。このように、入れ物と、その中に入れられる物(内容物)、とい う関係が集約です。 図 15.47 は、教務システムでの集約のイメージです。 図 15.47: 教務システムでの集約のイメージ 図 15.46 のような本棚と本の関係は、本棚がまさに入れ物と言えるため、わかりやすいかもしれません。では、 こちらの教務システムの方はどうでしょうか? 学校の授業では、その授業を受ける受講生がいます。従って、教務システムにおいて、授業に対してその受講生 が登録されるはずです。授業は、例えば授業名やその授業が行われる曜日・時限などの情報、つまりフィールドを 持つため、クラスと考えられます。また、受講生はすなわち学校の生徒であり、これまでも扱ってきたとおり、生 徒はも名前や学年などのフィールド変数を持つため、クラスと考えられます。 つまり、授業と生徒はどちらもクラスであり、授業クラスのオブジェクトは個々の授業を表します。また、授業 クラスのオブジェクトは、その授業に登録されている受講生、つまり生徒の情報を持つはずです。生徒の情報、す なわち生徒クラスのオブジェクトを、授業クラスのオブジェクトが持っている、ということになります。これは、 授業クラスのオブジェクトを入れ物として、生徒クラスのオブジェクトがその中に入っている、と言えるため、こ の授業クラスと生徒クラスの関係も集約と言えます。 このように、現実の世界では入れ物とは思えないようなものであっても、あるオブジェクト A が別のオブジェ クト B を持っているとき、プログラムの世界では、オブジェクト A は入れ物と言えます。 15.6.2 集約の定義 集約は、あるクラスのオブジェクトが、別のクラスのオブジェクトを持つ、という関係です。従って、クラスの 定義において、入れられる側(内容物の側)のクラスのオブジェクトを、フィールド変数として宣言します。 通常、入れられる物(内容物)であるオブジェクトは複数あります。従って、フィールド変数として宣言するも のは、本書の範囲では、配列を使います。 図 15.48 と図 15.49 は図書館システムでの集約のプログラム例です。 図 15.48: 集約のプログラム例(図書館システム) 図 15.49: 集約のプログラム例(教務システム) 15.7. プログラム例 221 図 15.48 の例では、BookShelf(本棚)クラスが Book(本)クラスのオブジェクトを集約しています。図 15.49 の例では、Course(授業)クラスが Student(生徒)クラスのオブジェクトを集約しています。 <コラム> 配列以外でも集約の表現は可能 本書では、集約の表現として、配列を使っています。しかし、Java には、同じクラスの複数のオブジェクトを まとめて扱うためのクラスもいくつか用意されています。例えば、java.util パッケージの Vector というクラス や java.util パッケージの HashMap など、様々なものがあります。配列ではなく、こういった Java に用意され ているクラスを使っても、集約を表現することができます。 15.7 プログラム例 ここでは、これまでの内容をまとめて、図書館システムと教務システムのプログラム例を説明します。 図 15.50∼図 15.56 は、図書館システムの処理の一部を記述したプログラム例です。このシステムでは、既存の 本のデータを読み込み、新しい本のデータを入力します。そして、入力された新しい本のデータと、既存の本の データを統合し、ファイルと標準出力に出力します。 図 15.50: 図書館システムのクラス(LibrarySystem) 図 15.51: 図書館システムのクラス(Book) 第 15 章 クラス同士の関係と連携 222 図 15.52: 図書館システムのクラス(Comics) 図 15.53: 図書館システムのクラス(BookShelf) 15.7. プログラム例 223 図 15.54: 図書館システムのクラス(ReadBookDatabase)図 15.55: 図書館システムのクラス(BookRegistration) それぞれのファイルの役割は、以下のとおりです。これまでは、本の情報の登録処理などは LibrarySystem ク ラスに記述していましたが、そうすると LibrarySystem クラスが非常に大きくなってしまいます。そこで今回は、 処理を分割して、別のクラスに役割分担しています。 第 15 章 クラス同士の関係と連携 224 図 15.56: 図書館システムのクラス(WriteBookDatabase) LibrarySystem.java (図 15.50): Book.java (図 15.51): 図書館システムの処理を開始させるクラス 本クラス Comics.java (図 15.52): コミッククラス BookShelf.java (図 15.53): 本棚クラス ReadBookDatabase.java (図 15.54): BookRegistration.java (図 15.55): ファイルから本のデータを読み込むための処理クラス 新しい本を登録するために本の除法を入力するための処理クラス WriteBookDatabase.java (図 15.56): 本のデータをファイルに保存するための処理クラス まず、ReadBookDatabase クラスの createBookEntry メソッドで、本のデータをファイルから読み取っていま す。本のデータは「ID, タイトル, 著者, 出版年, 置かれている本棚」という形式でファイルに保存されています。 また、コミックスに関しては、この本のデータに「ジャンル, 巻数」を加えたデータになっています。例えば、以 下のような内容が、ファイルに保存されているとします。 ¶ ³ 0001, 現代プログラミング事情, 毎日一郎,2008,book-1 0002,Java 三昧, 鈴木花子,2009,book-2 c1000, プログラミング道, 毎日次郎,2010,comics-3, 教養,1 µ ´ これらのデータを ReadBookDatabase クラスの createBookEntry メソッドで読み込み、Book クラスの配列と して返り値を返します。その返り値を LibrarySystem クラスで受け取った後、新しい本の登録を行います。本の 登録の処理は、BookRegistration クラスの registerBook メソッドを使って行います。今回は、簡易的な登録 とするため、コミックスの登録は除外しています。登録された本のオブジェクトは、LibrarySystem クラスで受 け取った、既存の本のオブジェクトと統合されます。 そして、WriteBookDatabase クラスの saveBookEntry メソッドを使って、既存の本と新しく登録された本の データをファイルに保存します。その後、すべての本のデータを標準出力に出力します。 このプログラムにおいて、Comics クラスは Book クラスを継承しており、かつ setID メソッドをオーバーライ ドしています。そのため、ReadBookDarabase クラスの createBookEntry メソッドでの setID メソッドの呼び出 しでは、setID メソッドの処理を担当するオブジェクトのクラスが Book クラスであるときと、Comics クラスで あるときで、処理の結果が異なります。 15.7. プログラム例 225 次に、図 15.57∼図 15.60 は、教務システムの処理の一部を記述したプログラム例です。このシステムでは、既 存の生徒のデータを読み込み、読み込んだ内容と、生徒の試験の得点の平均点を標準出力に出力します。 図 15.57: 教務システムのクラス(SchoolSystem) 図 15.58: 教務システムのクラス(Student) 第 15 章 クラス同士の関係と連携 226 図 15.60: 教 務 シ ス テ ム の ク ラ ス(ReadStudent- 図 15.59: 教務システムのクラス(SportCourseStudent)Database) それぞれのファイルの役割は、以下のとおりです。また、図書館システムと同様に、生徒の情報の登録処理など は SchoolSystem クラスに記述していると、SchoolSystem クラスが非常に大きくなってしまうため、処理を別の クラスに分割し、役割分担しています。 SchoolSystem.java (図 15.57): Student.java (図 15.58): 教務システムの処理を開始させるクラス 本クラス SportCourseStudent.java (図 15.59): ReadStudentDatabase.java (図 15.60): コミッククラス ファイルから本のデータを読み込むための処理クラス 15.8. エラーメッセージの意味と対策 227 まず、ReadStudentDatabase クラスの readFile メソッドで、生徒のデータをファイルから読み取っています。 生徒のデータは「学年, 学級, 出席番号, 氏名, 数学, 国語, 英語, 理科, 社会」(教科名は、その教科の得点)という 形式でファイルに保存されています。また、スポーツコース生に関しては、この生徒のデータに「体育, 専門種目」 を加えたデータになっています。例えば、以下のような内容が、ファイルに保存されているとします。 ¶ ³ 1,A 組,1, 相川太郎,85,70,98,52,79 1,B 組,2, 相沢花子,68,97,63,55,87 1,C 組,15, 田中三郎,51,96,74,48,80,100, 野球 µ ´ これらのデータを ReadStudentDatabase クラスの readFile メソッドで読み込み、Student クラスの配列とし て、返り値を返しています。それを SchoolSystem クラスで受け取ります。そして、試験の成績以外のデータと、 試験の平均点を標準出力で出力します。 このプログラムでは、SportCourseStudent クラスは Student クラスを継承しており、かつ calcAverage メソッ ドをオーバーライドしています。そのため、SchoolSystem クラスの printStudentInfo メソッドでの calcAverage メソッドの呼び出しでは、calcAverage メソッドの処理を担当するオブジェクトのクラスが Student クラスであ るときと、SportCourseStudent クラスであるときで、処理の結果が異なります。 これら図書館システムと教務システムのプログラムの詳しい処理の流れや処理方法は、プログラム内のコメン トを参照してください。また、これらのプログラム内で利用している instanceof という演算子は、15.2.4 のコラ ムで説明しているものです。また super というキーワードは、15.3 節のミニテクニックで説明しているものです。 15.8 エラーメッセージの意味と対策 ここでは、本章までの内容で、表示される可能性のあるコンパイルエラー・例外のメッセージについて説明を します。図 15.61∼図 15.63 のプログラムを例として考えていきます。なお、この 3 つのクラスは、お互いに連携 して動作することを想定して作成されたものです。 図 15.61: エラーが出るプログラム例(SampleA 1.java)図 15.62: エラーが出るプログラム例(SampleA 2.java) 第 15 章 クラス同士の関係と連携 228 図 15.63: エラーが出るプログラム例(SampleA 3.java) この図のプログラムを保存しているファイル名は、図 15.61 を「SampleA 1.java」、図 15.62 を「SampleA 2.java」、 図 15.63 を「SampleA 3.java」、プログラムの左側に書かれている数字は、プログラムの行数とします。 図 15.61∼図 15.63 の 2 つの Java ファイルをコンパイルしたときに表示されるエラーメッセージを説明します。 なお、2.3.1 節でも説明したとおり、エラーの内容によっては、あるエラーを修正し、再度コンパイルしたときに 表示されるエラーメッセージもありますが、ここでは、図 15.61∼図 15.63 のプログラムに含まれるエラーすべて について、1 行目から順に説明をしていきます。エラーメッセージの基本的な読み方については、2.3 節を参照し てください。 SampleA 2.java のコンパイルエラー ¶ ³ 8 行目「∼(クラス A) の...(メソッド A) は—(クラス B) の***(メソッド B) をオーバーライドできません。オーバーライドさ SampleA_2.java:8: SampleA_2 の setID(java.lang.String) は SampleA_1 の setID(java.lang.String) を オーバーライドできません。オーバーライドされたメソッドは static です。 public void setID(String bookID) { ^ µ ´ このエラーは「∼(クラス A) の...(メソッド A) は—(クラス B) の***(メソッド B) をオーバーライドできません。 オーバーライドされたメソッドは (static 修飾子・final 修飾子) です」というメッセージです。「 (static 修飾子・final 修飾子)」の部分には static や final、static final などが入ります。 15.3 節で説明したとおり、メソッドをオーバーライドするときは、static 修飾子と final 修飾子については、 以下のルールがありました。このエラーは、以下のルールを守っていないときに表示されます。 • 親クラスで、static 修飾子なしで定義されているメソッドは、オーバーライドするときも static 修飾子を つけてはならない • 親クラスで、static 修飾子をつけて定義されているメソッドは、オーバーライドするときも static 修飾子 をつけなければならない • 親クラスで、final 修飾子をつけて定義されているメソッドを、オーバーライドすることはできない この例では SampleA 1 クラスの setID メソッドを SampleA 2 クラスでオーバーライドしようとしています。し かし SampleA 1 クラスの setID メソッドには static 修飾子がついていますが SampleA 2 クラスでは、つけてい ません。 この例では SampleA 1 クラスの setID メソッドの static 修飾子を削除するか SampleA 2 クラスの setID メ ソッドに static 修飾子をつけるか、どちらかで、このエラーは解消されます。 15.8. エラーメッセージの意味と対策 229 このエラーが表示されたら、親クラス・子クラスのメソッドについている static が必要か否か、ついていない 方に static 修飾子をつけても良いか、また親クラスに final 修飾子がついているのであれば、その final 修飾 子は必要か、ということについて確認しましょう。 ¶ ³ 15 行目「∼(クラス A) の...(メソッド A) は—(クラス B) の***(メソッド B) をオーバーライドできません。スーパークラスで SampleA_2.java:15: SampleA_2 の getTitle() は SampleA_1 の getTitle() をオーバーライドでき ません。スーパークラスでの定義(public)より弱いアクセス特権を割り当てようとしました。 protected String getTitle() { ^ µ ´ このエラーは「∼(クラス A) の...(メソッド A) は—(クラス B) の***(メソッド B) をオーバーライドできませ ん。スーパークラスでの定義( (アクセス修飾子))より弱いアクセス特権を割り当てようとしました」という メッセージです。「 (アクセス修飾子)」の部分には public または protected が入ります。 15.3 節で説明したとおり、メソッドをオーバーライドするときは、表 15.2 に従って、親クラスと子クラスのメ ソッドのアクセス修飾子を決定する必要があります。このエラーは、この表の対応表に従っていないときに表示さ れます。 この例では SampleA 1 クラスの getTitle メソッドを SampleA 2 クラスでオーバーライドしようとしていま す。しかし SampleA 1 クラスの getTitle メソッドには public 修飾子がついていますが SampleA 2 クラスでは、 protected 修飾子がついています。15.3 節の表 15.2 によると、親クラスのメソッドに public 修飾子がついてい る場合は、そのメソッドをオーバーライドするときに、子クラスでつけることができるのは、public のみです。 そのため、このエラーが出ています。 この例では SampleA 1 クラスの getTitle メソッドのアクセス修飾子をまたは protected または private に 変更するか SampleA 2 クラスの getTitle メソッドのアクセス修飾子を public にするかどちらかで、このエラー は解消されます。 このエラーが表示されたら、15.3 節の表 15.2 に従って、アクセス修飾子をつけているか、確認しましょう。 SampleA 3.java のコンパイルエラー ¶ 15 行目「static でないメソッド∼を static コンテキストから参照することはできません」 ³ SampleA_3.java:11:static でない {\tt setPublicationYear(int)}メソッド を static コンテキスト から参照することはできません。 µ SampleA_1.setPublicationYear(2008); ^ ´ このエラーは「static でないメソッド∼を static コンテキストから参照することはできません」というメッ セージです。 15.1.1 節で説明したとおり、インスタンスメソッドは「オブジェクトの変数名. メソッド」の形式で呼び出しま す。インスタンスメソッドを、クラスメソッドのように呼び出そうとしたときに、このエラーは表示されます。 図 15.63 の例では SampleA 1.setPublicationYear(2008); として SampleA 1 クラスの setPublicationYear メソッドを呼び出そうとしています。つまり、このメソッドをクラスメソッドとして呼び出そうとしています。し かし、図 15.61 の SampleA 1 クラスの内容を見ると、この setPublicationYear メソッドには、static 修飾子 はついていません。従って、このメソッドは、インスタンスメソッドになります。そのため、このエラーが出てい ます。 この例では SampleA 3 クラスの SampleA 1.setPublicationYear(2008); を entry[0].setPublicationYear(2008); として、インスタンス変数として呼び出すように変更するか SampleA 1 クラスの setPublicationYear メソッド に static 修飾子をつけるか、どちらかでこのエラーは解消されます。 このエラーが表示されたら、以下のことを確認しましょう。 • 呼び出そうとしているメソッドはクラスメソッドか、インスタンスメソッドか 230 第 15 章 クラス同士の関係と連携 • 呼び出そうとしているメソッドがインスタンスメソッドであれば、クラス名ではなく、オブジェクト名を指 定して呼び出しているか