フォームの基礎

前回のまとめ

  • @ResponseBodyアノテーションをつけない場合はテンプレートエンジンを使う設定なる。
  • 使うテンプレートはメソッドの戻り値で指定する。
  • テンプレートに値を渡すには、ModelMapクラスを使う。
  • テンプレートで文字列を埋め込むには、要素(タグ)の属性として「th:text=”${モデルの名前}”」を指定する。

フォームを使う

「BMIの計算」を例として、「ブラウザから何か入力して、サーバで処理を行い、結果をブラウザで表示する」ということを行ってみよう。サーバに何か送るにはフォーム(HTMLのformタグとその関連タグ)を使う。

処理の流れを次のシーケンス図に示す。

sequence

テンプレートとコントローラを作成しよう。

ポイント:

  • テンプレート:
    • form要素の内側でinput要素を使う。
    • form要素のaction属性で値をどこにリクエストするか指定する。
    • form要素のmethod属性の違い: getはフォームの値がURLに含まれる。postはURLに含まれない(リクエストボディに記述される)
    • input要素のtype属性で種類が指定できる: textはテキストフォーム、submitは送信ボタン。
    • input要素の区別はname属性で行う。
  • コントローラ(主に14〜26行目):
    • フォームからの値を受け取るには、メソッドに引数を加え、@RequestParamアノテーションをつける。
    • @RequestMappingアノテーションはgetでもpostでも動作する。

formt要素のaction属性を書かない場合は、同じURLにリクエストを送ることになる。action属性を使わない例は次のようになる。

ポイント:

  • テンプレート:
    • form要素のaction属性を書かない場合は同じURLにリクエストする。
  • コントローラ(主に28〜40行目):
    • @GetMappingアノテーションはgetだけで動作し、@PostMappingアノテーションはpostだけで動作する。
    • @GetMappingアノテーションと@PostMappingアノテーションで同じパスを指定することができる。

復習

テンプレートを使う

前回のまとめ

  • Spring BootのアプリケーションはSPRING INITIALIZRでプロジェクトの雛形を作成することができる。
  • @Controllerアノテーション: クラスに付与するアノテーションで、そのクラスがコントローラであることを示す。「コントローラとは何か」は今回解説する。
  • @RequestMappingアノテーション: アクセス先のパス(URLの後ろ側)を表す。クラスとメソッドにそれぞれつけることができ、結合したものが最終的なパスを表す。
  • @ResponseBodyアノテーション: メソッドに付与するアノテーションで、戻り値をそのままブラウザに返すことを表す。
  • サーバのポート番号はデフォルトで8080だが、application.propertiesファイルに設定を記述することで変えることができる。

初学者のためのFAQ

 

  • Q: これまでクラスを作った(書いた、定義した)ら、new演算子でインスタンス化しないと使えなかったけど、コントローラのインスタンス化は?

    A: フレームワークがインスタンス化してくれる。フレームワークとは「全体の処理・流れ定義(実装)されており、その中の一部に自分のコードを組み込んでいくシステム」のこと。わかりやすい解説としてSoftware frameworkソフトウエアのフレームワークとはなにか等。


テンプレートを使う

前回は、文字をブラウザに表示したが、これでは表現力がなさすぎる。HTMLで表示するようにしてみよう。もちろん、コントローラからHTMLで出力するようにしてもいいが、ものすごく大変なので、テンプレートを用意しそこに文字をはめ込んで表示するようにしよう。

Spring Web MVC framework

これから作るウェブアプリケーションはSpringのWeb MVCフレームワークを利用している。このフレームワークにおける処理の流れを次の図に示す(http://docs.spring.io/spring/docs/current/spring-framework-reference/htmlsingle/#mvc-servletより引用)。

Mvc

フロントコントローラがリクエストを適切なコントローラに渡し、コントローラが出力するデータをビュー(テンプレート)が表示するという仕組みだ。このうち、自分で作るのは、「コントローラ」と「ビューテンプレート」である。コントローラからビューに「モデル(要するにデータ)」を渡すことによって表示内容を動的に(つまりメソッド実行時に)変えることができる。

テンプレートエンジン(テンプレートに関する処理を行うソフトウェア)は、前回、SPRING INITIALIZRでプロジェクト作成時に指定したThymeleafが使われる。

コントローラ

テンプレートを利用するコントローラを作成しよう。

前回との違い:

  • メソッドに@ResponseBodyがつかなくなること。これによりメソッドのreturnで指定した文字列がブラウザに直接送られるのではなく、テンプレートを使うようになる。メソッドのreturnで指定する文字列は「どのテンプレートを使うか」を指定するために使う。
  • テンプレートに値を渡すには、ModelMapクラスを使う。メソッド引数としてModelMapを指定すると使えるようになる(フレームワークがテンプレートに引き渡すためのインスタンスを作ってくれる)。ModelMapのaddAttributeメソッドでテンプレートに渡すモデルの「名前」と「値」を指定する。

テンプレート

テンプレートを置く場所(基点)はResourcesのtemplatesだ。コントローラで「lect02/hello」とした場合、templatesの中にlect02というディレクトリを作り、その中にhello.htmlというファイルを作る。

NetBeansのパッケージの表示の仕方によっては、作業が難しい。プロジェクトウィンドウの背景部分を右クリックし、「Javaパッケージの表示方法」を「ツリー」にすると階層構造がわかりやすい。または、プロジェクト表示ではなく、ファイル表示にするとわかりやすい。

templates

コントローラでは3つのテンプレートを指定している。それぞれ作成しよう。

ポイント

  • 単に表示をするだけの場合は素のHTMLで良い。Thymeleafの機能を使う場合は「th:」から始まる要素(タグ)や属性を使う。
  • htmlタグの属性に追加した「xmlns:th=”http://www.thymeleaf.org”」は名前空間の指定。最近のThymeleafならなくても動くが、つけておいたほうが良い。
  • 文字列を埋め込む場合は、要素(タグ)の属性として「th:text=”${モデルの名前}」を指定する。子ノード(要するにタグに挟まれたところ)がモデルの値で置き換わる。他にも指定方法がある。counter.htmlにいくつか例を示す。

実行・確認

実行して、確認しよう。

どのようなHTMLが出力されているかはChromeのデベロッパーツール等を使うと良い。


復習

NetBeansでSpring Bootアプリケーションの開発を始める

プロジェクトの雛形を作成

  1. ブラウザで「http://start.spring.io」にアクセスする。
  2. 「Gradle Project」を選択する。
  3. 「Java」を選択する。
  4. 「2.0.1」を選択する。
  5. グループIDとして、ここでは「ユーザ名」を使う。
  6. アーティファクトIDは、ここでは「webapp」とする。
  7. 利用する依存ライブラリを入力する。名前の一部を入力すると選択肢が現れるのでクリックして選択する。ここでは「Web」と「Thymeleaf」を選択する。
  8. 選択されたものが表示されているので「Web」と「Thymeleaf」が選択されていることを確認する。
  9. クリックするとプロジェクトのファイル(ZIPファイル)のダウンロードが始まる。

Spring Initializr

NetBeansでインポート

メニューから[ファイル]→[プロジェクトをインポート]→[ZIPから]を選択し、ダウンロードしたファイル(webapp.zip)を指定してインポートする。

project

Spring Bootアプリケーションの実行

アプリケーションを実行するためのタスクが定義されているため、それを使う。

プロジェクトを右クリックして、[Task]→[boot]→[bootRun]を選択する。

アプリケーションが立ち上がると、出力ウィンドウの最後に「Started WebappApplication」と表示される。

その1行前に「Tomcat started on port(s): 8080 (http)」と出力されているということは、8080番ポートにアクセスすれば何らかの反応があるはずである。http://localhost:8080/にアクセスしてみよう。

サーバが出力したエラーページ

エラーメッセージが表示されたが、これは、まだコンテンツを返すプログラムを作っていないために表示されるもので、正常な動作である。アプリケーションを終了するには、出力ウィンドウの左側にあるStopボタンをクリックする。

8080ポートが使えない場合(演習室では使えない)はポート番号を変える必要がある。18080に変えるにはapplication.propertiesに次の1行を追加する。

何か表示してみる

Lect01Controllerクラスを作成して実行し、次のURLにアクセスしてみよう。

クラスの継承(まとめ)

継承とは

  • 継承するとメンバーを受け継ぐ(メンバーのアクセス修飾子に注意)
  • 継承の元となるクラス(親)は「スーパークラス」、継承によって作られた新しいクラス(子)は「サブクラス」
  • Javaの継承は「単一継承(親は1つだけ)」で、すべてのクラスの祖先となるのはObjectクラス(extendsを書かないクラスはextends Objectが省略されていると考えれば良い)。

(メンバーを受け継ぐことに加え)サブクラスでできること

  • メンバーを加えることができる(メンバーを加えない≒スーパークラスと同じ、なので加えないと意味がない)
  • 「super」でサブクラスからスーパークラスのメソッド(コンストラクタを含む)を呼び出すことができる
  • スーパークラスにあるメソッドをオーバーライド(上書き)することができる

継承の使い所

  • (なんらかの理由でクラスのメンバーの追加・修正ができない場合に)継承して機能を拡張する。
  • 共通部分をスーパークラスとしてまとめ、個別の機能をサブクラスで記述する(この用途には「抽象クラス(教科書P.114)」が適切な場合がある)
  • 同じように呼び出すことができるメソッドを持たせる(この用途には「インタフェース(教科書P.116)」が適切な場合がある)。
  • etc…

クラスの継承(2)

前回のまとめ

  • 継承するとメンバーを受け継ぐ
  • ただしメンバーのアクセス修飾子に注意
  • 継承の元となるクラスは「スーパークラス」、継承によって作られた新しいクラスは「サブクラス」
  • super: サブクラスからスーパークラスのメソッド(コンストラクタを含む)を呼び出すことができる
  • スーパークラスにあるメソッドをオーバーライド(上書き)することができる

サブクラスからスーパークラスへのキャスト

コード全体: Main1.java

サブクラスはスーパークラスのメンバーをすべて持っている。
よって、サブクラスのインスタンスはスーパークラスのインスタンスとみなしても(として扱っても)大丈夫だ。

実際に、次のようなコードが書ける。

これは、次のような状況だ。

サブクラスからスーパークラスへのキャスト

comicで使う場合はComicクラスのインスタンスとして扱うことができるが(当たり前)、comicAsBookで使う場合はBookクラスのメンバーしか使えない。つまり

は実行できるが、

はコンパイルエラーになる。

これができると何が嬉しいのだろうか?1つは「BookとComicを(Bookの情報だけになるが)同じように扱える」ということだ。前回は
BookとComicでそれぞれ配列を作ってデータを扱っていたが、まとめてBookの配列として扱うことができる。つまり、

とすることができる。ただし、bookList[1]は本来Comicだが、Book扱いになる。

Book扱いになる(つまりComicで追加されたメンバーは使えない)が、オーバーライドされたメソッドはどういう扱いになるのだろうか。
次の実行結果を確認しよう。

コメントにも書いてある通り、「どのクラスとして扱っているか」ではなく、「インスタンス化した時のクラス」の実装が使われる。

すべてのクラスはObjectクラスを継承している

コード全体: Magazine.java, Main2.java, Main3.java

雑誌を表すMagazineクラスを作ってみよう。

Objectクラス

オーバーライドする時にIDEを使うと、候補となるメソッドが表示されるが、Bookクラスで定義していないメソッドがあるのが確認できるはず。これはObjectクラスのメソッドである。
実は(Objectクラスを除く)すべてのクラスはObjectクラスを継承している。正確には「extends」で指定しない場合には、Objectクラスが自動的に継承される。
つまり「Objectを継承したBook」を継承しているのがMagazineという関係である。

すべてのクラスがObjectを継承しているのなら、Objectの配列は参照型ならすべて格納できる、ということになる。

toStringメソッド

ここでは、toStringメソッドをオーバーライドしてみよう。これにより、System.out.printlnの引数として指定した時に表示する文字列を指定することができる。

では、確認しよう。

instanceof演算子とスーパークラスからサブクラスへのキャスト

Object型のままだと、その中のインスタンスが何であってもできることが限られる。実際のインスタンスの型を判別してキャストしよう。型の判別にはinstanceof演算子(教科書P.126)を使う。instanceof演算子で、「指定した型にキャストできるか」を判断することができる。Bookクラスにキャストできる(つまりBookクラスかBookの子孫となるクラス)かどうか判別してCSVを出力してみよう。

equalsメソッド

参照型の比較には==が使えない場合が多い。比較が必要なクラスはObjectクラスのequalsメソッドをオーバーライドする、という慣例になっている(「慣例」と書いたが、多くのライブラリが前提としてるので、ほぼルールと言って良い)。
例えば、文字列の比較の例を見てみよう。

Magazineのequalsメソッドをオーバーライドしてみよう。

結果を確認しよう。

クラスの継承(1)

前回のまとめ

  • 型は大別してプリミティブ型と参照型の2つ。
  • プリミティブ型は「箱」のイメージでOK。代入は「値をコピー」
  • 参照型は「商品タグ」のイメージ。インスタンスを指し示すのがインスタンス名(変数名)。代入は「商品タグが同じところに繋がる(ポインターのコピー)」
  • プリミティブ型と参照型では、代入や比較等で(初学者にとって)直感的でない違いがあるので注意する。この違いはコードを実行して確かめること。
  • 文字列や配列は参照型。よく使われるので「特別扱い」されている(使いやすくなっている)。

クラスの継承

本を管理する

(書店を経営しているつもりになって、あるいは自分の蔵書について)本を管理するソフトを作ることを考えよう。まず、本(小説とか)を管理するクラスを作ろう。

このクラスを使った例を次に示す。

GitHubで見る:

「巻」を管理できるように拡張する

漫画などは「第1巻」、「第2巻」と、同じタイトルでシリーズになっているものが多い。この「巻」の管理はBookクラスではできないので新しくComicクラスを作ることを考えよう。ただし、1から新しく作るのではなく、Bookクラスを元に「巻」に関する情報が扱えるように拡張することを考える。

ポイント:

  • 3行目: 「extends Book」で「Bookクラスを継承する」という意味になる。
    • この場合、Bookクラスが「スーパークラス」、Comicクラスが「サブクラス」ととなる。
    • Bookクラスにあるメンバー(フィールドとメソッド)が引き継がれる(使える)。
    • アクセス修飾子(教科書P.170)によって違いがあるので注意する。
  • 4-7行目: Comicクラスだけで使えるフィールド
  • 9-14行目: Bookクラスとは違うコンストラクタにしたい(インスタンス化時に追加したフィールドに値をセットしたい)ので定義する
  • 11行目: superをメソッドのように使う(括弧をつけて引数を渡す)とスーパークラス(この場合Bookクラス)のコンストラクタが実行される。
  • 16-28行目: 巻の情報も出力したいので、getInfoCSVメソッドを上書き(オーバーライド)する。
  • 17行目: @Overrideアノテーション(教科書P.180)
  • 21,24行目: オーバーライドする前の元のメンバーを使いたい場合は、superに「.」をつけてアクセスする(thisと同じような感じ)。
  • 28-35行目: Comicクラスだけで使えるメソッド

このクラスを使った例を次に示す。

GitHubで見る:

プリミティブ型(基本型)と参照型(クラス型)

The types of the Java programming language are divided into two categories: primitive types and reference types.

Java言語の型は次の2つのカテゴリーに分けられる: プリミティブ型と参照形。

プリミティブ型(基本型)

次の8種類。整数はすべて符号付きで、C言語と違ってunsignedはない。

格納できる値 備考
boolean 真偽値(trueもしくはfalse)
char 1文字 Unicodeのコードを表す16ビットの整数。教科書P.12
byte 整数(8ビット) 教科書P.10
short 整数(16ビット) 教科書P.10
int 整数(16ビット) 教科書P.10
long 整数(32ビット) 教科書P.10
float 実数(32ビット) 教科書P.11
double 実数(64ビット) 教科書P.11

基本型の変数は、「箱」のイメージで問題ない。変数名は箱についている名前。

では、次のコードを実行してみよう(コード全体)。

(1)では、aに0、bに1を代入している。

(1) プリミティブ型の宣言と初期化

(2)で代入という操作をすると、aの値がコピーされてbに入る。

Primitive2

(3)で、aに代入するとaの値が変わる。bの値は変わらない。

Primitive3

参照型(クラス型)

参照型はクラス型ともいい、要するに、クラスによる型のこと。クラス型の変数は、クラス(やインターフェース)の実体(インスタンス)を格納する。

例えば、円(通貨じゃなくて図形の方)を表すCircleクラスを定義して、インスタンス化することを考えよう。半径を表すフィールドを持つクラスである。

この場合の変数は、プリミティブ型の時と違って、「箱」のイメージではない!「商品タグ」のイメージが適切である。特に代入演算子を使った時の処理について注意が必要だ。実際に実行して、代入がどのような操作なのか確認しよう。

(4)では、インスタンス化して、フィールドに値を代入している。変数名(インスタンス名)は商品タグのようにインスタンスに紐づいている。

(4) 参照型のインスタンス化とフィールドへの代入

(5)で、代入の操作を行っているが、これは「c2の紐付け先をc1のものと同じにする」という意味になる(C言語がわかっている人向けの説明: ポインタのコピー)。元々c2が紐づいていたインスタンスはどこからも使われていない状態になったので、ガベージコレクタ(教科書P.92)によって、そのうちメモリから消される。

Reference2

(6) c1のフィールドの値を代入によって変更すると、c2のフィールドも同じになる(というか同じものを指し示している)。

Reference3

このように、参照型では(初学者にとっては)直感と違う処理が行われるので注意する。この辺りについては「シャローコピー」や「ディープコピー」といった単語で検索してみると理解が深まるだろう。

比較

代入だけではなく、比較についても注意しなければならない。次のコードを実行して見よう(コード全体)。

参照型の比較は、「指し示しているインスタンスが同じか」という比較をしているので、フィールドの値がすべて同じであったとしても指し示しているインスタンスが違えばfalseとなる。参照型は、普通は「==」で比較してはいけない、と覚えておこう(もちろん、同じインスタンスを指し示しているか、という判別が必要なときはこれで良い)。

では、参照型で比較したい場合はどうしたらいいのだろうか?これは、地味にフィールドを比較していくしかない。Circleクラスの場合はフィールドが1つだけなので簡単だが、フィールドがたくさんある場合は大変だ。なので、比較が必要なクラスは比較するためのメソッドを作っておくのが良い。じゃあ、どうやって作るか、利用するか、ということは今後の講義で解説する。

参照型あれこれ

文字列

Javaの文字列はStringクラスのインスタンスだ。つまり参照型だ。だから、比較などでは注意しなければならない。また、文字列は非常によく使われるので、特別扱いされている。

特別扱い1: 普通のクラスはnew演算子でインスタンス化を行うが、文字列のインスタンス化はダブルクォーテーションで囲むことによってインスタンス化することができる!

ちなみに、ダブルクォーテーションを使わず、charの配列から文字列を作ると次のようになる。

特別扱い2: +演算子で結合ができる!(教科書P.13、ちなみに、「演算子のオーバーロード」という機能がある言語では、+演算子の機能を変えることができる)

Javaの文字列は様々なメソッドが用意されているので使ってみよう。

配列

配列も参照型である。

lengthという読み取り専用の特殊なフィールドのようなものがある(教科書P.23)。

例えば、for文でループ回数に使えば、文字列の長さが変わってもその部分のコードを書き換える必要はなくなる。

補足(1): 「変数は初期化されていない可能性があります」というエラーについて

Javaでは「値が入っていない変数を使おうとした時」にエラーになる。NetBeansでは次のようなエラーが表示される。

変数aは初期化されていない可能性があります

ちょっとややこしいので、どのような時にOK or NGになるかを解説する。

ソースコード全体

[OK] aに値が入っているのでエラーにならない

変数aに値が代入されているので問題ない。

[NG] aに値が入っていないのでエラーになる

変数aに値が代入されてないので、13行目のreturnがエラーになる。

[NG] aに値が入らない場合があり得るのでエラーになる

変数xが0.5未満の時は変数aに値が代入される(乱数については教科書P.199を参照)が、それ以外の時はaに値は代入されない。xが0.5未満かどうかは実行時に決まることだが、コンパイル時に「代入されない可能性」があればエラーになる。

[OK] aに値が入るのが確実なのでエラーにならない

条件分岐があったとしても、全ての条件で代入していればエラーにならない。ただし、「全ての条件」とはコンパイラが判断できる条件のことなので、次の場合エラーになる。

[NG] aに値が入るのが確実だが、コンパイラが判断できないのでエラーになる

「xが0.5未満」と「xが0.5以上」という条件をチェックしているので、このどちらかにしか当てはまらないが、コンパイラは「どのような条件か」についてはチェックしないので、「この2つの条件に当てはまらない場合」があり得ると判断しエラーになる。else節をつければ全ての条件について記述できるのでエラーにならない。

パッケージ(2): 他のパッケージの利用・アクセス修飾子、クラスの基礎(3): コンストラクタ(メソッドの一種)

前回のまとめ

  • メソッドの定義・利用(呼び出し)について
    • C言語とだいたい同じ
    • メソッドが所属するクラスのフィールドにアクセス(読み・書き)できる。
    • メソッドが所属するクラスのメソッドを呼び出すことができる。
    • 他のインスタンスのメンバーは「インスタンス名.メンバー名」で使える。
  • 課題の解答例: MedicalRecord.java(48〜56行目)

パッケージ(2): 他のパッケージの利用・アクセス修飾子

他のパッケージに所属するクラスの利用方法とアクセス修飾子の機能について確かめよう。

  1. パッケージ「d00000.lecture06」を作成する。
  2. そこにMain1クラスを作成する。
  3. Main1クラスにmainメソッドを作成する。

最終的なソースコード: Main1.java

他のパッケージに所属するクラスを直接使うにはクラス名の前に「パッケージ名.」をつける(9行目)。

d00000.lecture05.MedicalRecord mr1 = new d00000.lecture05.MedicalRecord();

import文を使うと「パッケージ名.」を省略することができる(3行目)。

import d00000.lecture05.MedicalRecord;

この場合、単に「MedicalRecord」と書いた場合は「d00000.lecture05.MedicalRecord」として扱われる。同じ名前のクラスを複数使いたい場合は、パッケージ名を省略できるのは1つだけ。ちなみに、import文なしでパッケージ名を省略した場合は、同じパッケージのものを指す。

注意: 教科書の説明だと、import文を使わないと他のパッケージのクラスが使えないように読めるが、ここで確かめたように、そうではない。import文は単に他のパッケージのクラスを利用するときの「パッケージ名.」を省略できるようにする、という機能である。C言語の#includeとは全然違う。

他のパッケージで使えるクラスは、classの前に「public」というアクセス修飾子がついていなければならない。また、インスタンス化ができたとしても、その中のメンバーにも適切なアクセス修飾子がついていなければならない。「クラスにつくアクセス修飾子」と「メンバーにつくアクセス修飾子」の2種類があることに注意しよう。

クラスの基礎(3): コンストラクタ(メソッドの一種)

メソッドを追加して、MedicalRecordクラスをもっと「使える」クラスにしていこう。

  1. d00000.lecture06にMain2クラスを作成する。
  2. Main2クラスにmainメソッドを作成する。
  3. d00000.lecture05のMedicalRecord.javaをd00000.lecture06にコピーする(前回と同じくリファクタリングコピーを使う)。

最終的なソースコード: Main2.javaMedicalRecord.java

これまでの利用の仕方を確認していこう(Main2.java: 5〜11行目)。8〜10行目をコメントアウト(コメントにすること)して実行してみよう。このようにフィールドをセットし忘れたりするとおかしな動作をすることがある。クラスの設計については色々な考え方があるが、ここでは、「セットしやすいように工夫する」とか「セットし忘れても大丈夫なようにする」といった考え方で改良してみよう。

まずは、フィールドをセットする専用のメソッドを用意してみよう(Main2.java: 15行目、MedicalRecord.java: 58〜68行目)。これを使うことで、3つのフィールドを1行でセットできる。thisについては教科書の110ページを参照すること。メソッドの引数とフィールドの名前が被った場合、メソッドの本体では引数の方が優先して使われる。このような名前が被った状態でフィールドの方を使うためには「this.」をつけてフィールドの方であることを示す必要がある。また、表示する文字列を作るメソッドも用意しよう(Main2.java: 16行目、MedicalRecord.java: 70〜78行目)。これでだいぶシンプルになった。

次は、インスタンス化したら、デフォルトで値を設定するということを考えよう。これにはコンストラクタ(constructor)というちょっと特殊なメソッドを追加することで実現することができる。コンストラクタは、インスタンス化のタイミングで自動的に実行されるメソッドで、次のようにして定義したメソッドがコンストラクタにとして扱われる。

  • メソッド名はクラス名と同じにする。
  • メソッドの戻り値(返り値)の型は記述しない。

まずは引数なしのコンストラクタを追加しよう(MedicalRecord.java: 80〜91行目)。いつものようにインスタンス化する(Main2.java: 19行目)と、このコンストラクタが実行される。最初はMedicalRecord.javaの85行目はコメントを外して、コンストラクタが実行されたことを確認しよう。

次に、引数ありのコンストラクタを追加しよう(MedicalRecord.java: 93〜104行目)。これを使えば、任意の値をインスタンス化の時に設定することができる(Main2.java: 23行目)。

コンストラクタを作ると、配列を作成することも簡単になる(Main2.java: 26〜30行目)。また、任意の値とともインスタンス化することができるので、名前をつけずにインスタンス化し、そのメソッドを実行するなんてことにも利用しやすくなる(Main2.java: 33行目)。

コンストラクタにまつわるハマりどころ

実は、インスタンス化にはコンストラクタが絶対に必要。

今まで、コンストラクタを定義してなかったのにインスタンス化ができていたのはなぜか? コンストラクタを1つも定義しないと、デフォルトコンストラクタが作成される。これがインスタンス化に使われる。

ハマりどころ: コンストラクタを1つでも定義すると、デフォルトコンストラクタは作成されない。引数ありのコンストラクタだけを定義した場合、引数なしの形でインスタンス化することができない。

クラスの基礎(2): メソッドの定義・利用(呼び出し)

前回のまとめ

  • パッケージ(教科書P.164〜)
    • クラスをひとまとめにしたもの
    • パッケージが違えば名前のクラスでも同時に存在できる
  • クラスの基礎(教科書P.66〜)
    • 「フィールド」と「メソッド」で構成され(それ以外のものは無い)、それらを「メンバー」という。
    • クラスは「設計図」。そのままでは使えない。「実体(インスタンス)」を作る必要がある(「クラスは設計図」という比喩だけでは説明できない機能があることに注意)。 
    • 教科書ではインスタンスではなく「オブジェクト」という言葉を使っているが、「オブジェクト」は文脈によって意味が違ったりするので、本講義では「インスタンス」という言葉を使う。
    • インスタンス化(インスタンスを作ること)にはnew演算子を使う。

準備

  • パッケージ「d00000.lecture05」を作成する。
  • 前回作ったMedicalRecord.javaを右クリックしてコピーする。
  • 今回作ったパッケージを右クリックして、貼付け→リファクタリング コピー→リファクタリングを選択する(パッケージ宣言を変更してコピーが行われる)。
  • 今回作ったパッケージにMainクラスを作成し、さらにmainメソッドを作成する。

メソッドの定義・利用(呼び出し)

MedicalRecordクラスにメソッドを追加しよう。

注: クラスやフィールドにアクセス修飾子の「public」が追加されている。解説は次回。

  • メソッドの定義
    • アクセス修飾子(public、private、protected、無し)
    • 戻り値(返り値)の型(戻り値がない場合はvoid)
    • メソッド名
    • 引数(型と引数名のペア)が0個以上
    • 本体
      • return文(本体の実行を終了し呼び出し元に値を返す)
  • メソッドが所属するクラスのフィールドにアクセス(読み・書き)できる。
  • メソッドが所属するクラスのメソッドを呼び出すことができる。
  • 他のインスタンスのメソッドを呼び出すときは次のよう記述する。
    インスタンス名.メソッド名(引数)