再評価のための復習: コンストラクタ

コンストラクタとは

インスタンス化の時(new演算子を使う時)に実行されるメソッド。

コンストラクタの定義

  • コンストラクタはメソッドの一種なのでメソッドの定義の仕方とあまり変わらない。下記の点がコンストラクタに特有であることに注意する。
    • メソッド名は自由につけられない。コンストラクタはクラス名と同じにする。
    • 戻り値(返り値)の型は記述しない。

コンストラクタが定義されているクラスの例

インスタンス化

ポイント

  • コンストラクタの名前はクラス名と同じでなければならないため、複数のコンストラクタを定義するには(メソッド名で区別できないので)、引数の形(個数、型の並び順)が違っていなければならない。つまりコンストラクタのオーバーロード。(教科書P.72等)
  • コンストラクタを定義しないクラスには、デフォルトコンストラクタが自動的に生成される(定義したことになる)。デフォルトコンストラクタは、引数なしで何の処理もしない。
  • ハマりやすいポイント: 引数ありのコンストラクタを定義しただけだと、引数なしでインスタンス化することができない(1つもコンストラクタを定義しない場合にしかデフォルトコンストラクタが生成されないから)。

教科書

  • P.74〜77: コンストラクタ
  • P.72: オーバーロード

再評価のための復習: メソッド

メソッドの概念

Method concept

  • メソッドを呼ぶと定義された処理が実行される
  • メソッドを呼ぶ時に引数を渡す場合がある。
  • メソッドを呼ぶと戻り値が返ってくる場合がある。

メソッドの定義

例: int型の数値を2つ足した合計を返すメソッドの定義

メソッド

引数

  • メソッド名の後ろのカッコの中に、型と引数名を書く。
  • 複数の場合はカンマで区切る
  • 引数がない場合は書かない(カッコは必要)

戻り値(返り値)

  • 戻り値の肩をメソッド名の前に書く。
  • 戻り値はreturn文で指定する。
  • 戻り値がない場合は、戻り値の型として「void」を指定する。return文はなくても良い(あっても良いがreturnの後ろに何も書かない)。

アクセス修飾子

  • フィールドと同様にアクセス修飾子をつけることで、そのメソッドを呼び出すことのできる呼び出し元を制限することができる。
  • アクセス修飾子は戻り値の型の前に書く。

処理で使える変数

  • 引数
  • フィールド
  • その場で定義した変数

教科書

  • P.66: メソッドの定義
  • P.68: メソッドの呼び出し(1)
  • P.70: メソッドの呼び出し(2)

再評価のための復習: フィールド

最もシンプルなフィールドの定義

型名 フィールド名;

これにアクセス修飾子や初期化がつく。

フィールド定義の例

外部からのアクセス(アクセス修飾子の違い)

教科書

再評価のための復習: インスタンス

インスタンス化

  • クラスは「型の設計図」のこと。
  • クラス(設計図)からインスタンス(実体)を作ることができる。
  • インスタンス化: インスタンスを作ること。
  • インスタンス化はnew演算子を使う。
  • コンストラクタはインスタンス化の時に使われるメソッド(今後解説する)で、その時に使われる引数を記述する。下の例は引数なし。

インスタンス化

1行でまとめて書くこともできる。

エラーになる例

person3がインスタンス化されていない(注: 厳密には「初期化されていない」)ためコンパイルエラー(コンパイル時にエラーになってコンパイルできない)になる。

コンパイルできる(person4はnullで初期化されている)が、実行時にエラーになる(ランタイムエラー)。person4がインスタンス化されてない(実態がない)のに使おうとしたため。

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

  • インスタンス化が必要なのは参照型(クラス型)
  • newは使わない。
  • 初期化は必要

次はOK。

1行で書くのもOK。

初期化がないのはNG(コンパイルエラー)。

基本型でnullは扱えないのでNG(コンパイルエラー)。

再評価のための復習: クラスの概要

クラスとは

  • クラスとは型の設計図のこと。注: ここではわかりやすさのため設計図に例えるが、設計図の比喩が適切でない場合もあるので注意すること。「適切でない場合」については今後解説する。
  • クラスから作られるものをインスタンス(実体)という。
  • 基本的に、1クラス1ファイルで定義する。ファイル名は「クラス名.java」とする。
  • クラスにはメンバーによって構成される(クラスの中にかけるのはメンバーだけ)。
  • メンバーの種類にはフィールドとメソッドがある(フィールドとメソッド以外のものはない)。
  • [慣例] クラス名は大文字ではじめる。
  • [慣例] フィールド名とメソッド名は小文字ではじめる。

クラスの構造

カウンターアプリを作る

JavaFXでWindowを表示する

  • JavaFXのアプリケーションはjavafx.application.Applicationを継承して作る。
  • ちなみにjavafx.application.Applicationは抽象クラス(アブストラクトクラス)。
  • startメソッドをオーバーライドして、そこで各種設定(部品配置等)を行う。startメソッドはJavaFXにおけるコンストラクタのような役割。
  • アプリケーションの起動は「Application.launch(アプリケーションのクラス名.class);」で行う。

ボタンなどを配置する

イベントハンドラー(オブザーバー)

  • ボタンクラスはイベント(ボタンがクリックされた等)が発生した時に、何か処理をすること(させること)ができる。
  • 「イベントが発生した時の処理」を担当するクラスを「イベントハンドラー(event handler)」という。
  • もっと詳しく知りたい人へ: Observerパターン
  • (JavaFxの場合)ButtonのイベントハンドラーはEventHandlerインターフェースを実装する。
  • ボタンに関するイベントが発生すると、ボタンに登録したイベントハンドラーのhandleメソッドが実行される。

ExampleHandlerをきちんと動作させる(カウントボタンでラベルに表示されるカウンターを増やし、リセットボタンで0にする)には次の2つができるようにすればいいが、煩雑なコードになる。

  • labelのsetTextメソッドを呼べるようにする
  • countButtonとresetButtonのどちらからイベントが発生したか区別できるようにする

カウンターの機能をうまく実現するには、CounterAppがイベントを処理するようにするのが簡潔になる。

JavaFXについて知る

インターフェース(復習)

  • クラスと同様で、型を定義できる。
  • クラスと違って、単体では使えない(インスタンスを作ることができない)。クラスに実装(implements)する。
  • インターフェースで記述できるメソッドは抽象メソッド(本体がない)。したがってクラスでそのメソッドをオーバーライドして実装しなければ(本体を書かなければ)ならない。

例えると:

  • クラス: 設計図(どのような機能があるのか、その機能をどのような処理で実現するのかが書いてある)。
  • インスタンス: 製品・生産物・実体(設計図に基づいて作られるもの)。
  • インターフェース: 規格(設計の時点で持つべき機能が定義されている。どのように実現するのかは書いていない)。

設計図が違っても、同じ規格に準拠していれば、それらの設計図から作られた実体は同じように扱うことができる(その規格の範囲内では)。

例えばスマホ内蔵のバッテリーは特に標準規格がないので、あるスマホバッテリーを他のスマホで使うことはできない。
乾電池は形・サイズ・電圧の規格(単一、単二等)が合っていれば(インターフェースが合っていれば)、メーカや電池系(マンガン、アルカリ等)が違っても(処理の実装が違っても)入れ替えて使うことができる(注: 厳密には、電池系についても規格があるのでマンガンとアルカリで入れ替えてはダメな場合等もある)。

インターフェース

キャッシュレジスターをプログラムする。

レジで会計する(税込の合計金額を計算してレシートを印刷する)ときに必要なのは、商品の値段と商品名だ。だから、Bookクラスに「値段を取得するメソッド」と「商品名を取得するメソッド」があればOK。

では、Bookで表現できないものをレジで会計するには、どのようにすればいいのか?

  • Book以外も受け付けるようにレジを改良する(クラスごとに保存、計算するプログラムが必要になる)。
  • Bookより上位のクラス(例えば商品クラス)を定義し、それを継承するように書き直す(Java言語は単一継承のためうまく実現できない場合も)。
  • getPriceメソッドをなんとかして呼ぶ(リフレクションを使う)。ちなみに、ダック・タイピングをサポートする言語ならば簡単だが、Java言語はそうでは無い。
  • 商品を表すインターフェースを定義し、レジで扱いたいクラスはそれを実装(implements)する。
  • etc…

インターフェースとは

クラスに特定のメンバー(メソッドとフィールド、主にメソッド)が存在することを保証する仕組み。クラスと同様に型として扱うことができる。

  • インターフェースには「クラスが持っていてほしいメンバー」を定義する。ただしメソッドの本体(実際に実行される部分)は無い(抽象メソッドという)。
  • インターフェースをimplementsしたクラスは、そのインターフェースに定義されているメンバー全体を実装する(メソッドの本体も書く)。

まずはBookだけを計算できるレジ

商品を表すProduct

  • クラスと同様にメンバー(フィールド、メソッド)を持つ。
  • ただし、メソッドは本体がない(抽象メソッド)。

Productを実装(implements)

Product対応レジ

getterとsetter

getter/setterとは

  • getterはフィールドを取得(ゲット)するためのメソッド。メソッド名は「getフィールド名」とする。フィールド名の最初を大文字に変える。対応するフィールドをreturnする。 
  • setterはフィールドに設定(セット)するためにメソッド。メソッド名は「setフィールド名」とする。フィールド名の最初を大文字に変える。引数をフィールドにセットする。

getter/setterでフィールドにアクセスする利点(例)

  • フィールドをprivateとしてgetterだけ用意すれば読み出し専用のフィールドとなる。
  • セッターでセットする値をチェックすることができる。
  • etc…

Bookの改良

Bookのpriceを後から書き換えられないようにしよう。

継承: Objectクラス

(Objectクラスを除く)全てのクラスはObjectクラスを継承している

  • extendsを書かない場合は、自動的にObjectクラスを継承する(「extends Object」と同じ)。
  • Objectのメソッドをオーバーライドして動作を変えることができる。

UMLによる継承の関係の図示

Diagram

toStringメソッド

System.out.printlnで表示しようしたり、文字列(Stringクラスのインスタンス)を+演算子で繋げようとすると、ObjectクラスにあるtoStringメソッドが呼ばれて、そのインスタンスを表す文字列型が取り出される。

例えば、

とすると、

lesson11.d00000.Comic@5e481248

等と表示される。これは、ObjectのtoStringが

と定義されているから(例えば、JDK6のソースコードhttp://hg.openjdk.java.net/jdk6/jdk6/jdk/file/48e891c51be5/src/share/classes/java/lang/Object.java#l235)。

オーバーライドして、インスタンスの情報を見やすく表示するようにしてみよう。Bookクラスに

を加えることで、

の表示が変わる(Bookを継承しているComicの場合も)。