クラスの継承

前回のまとめ

  • 型は大別してプリミティブ型と参照型の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文(本体の実行を終了し呼び出し元に値を返す)
  • メソッドが所属するクラスのフィールドにアクセス(読み・書き)できる。
  • メソッドが所属するクラスのメソッドを呼び出すことができる。
  • 他のインスタンスのメソッドを呼び出すときは次のよう記述する。
    インスタンス名.メソッド名(引数)

パッケージ(1): パッケージの作成、クラスの基礎(1): フィールド

前回のまとめ

  • 配列を作成するにはnew演算子を使い、次のように書く。
    double[] weight = new double[3];
    あとはC言語と大体同じ。
  • 配列の宣言と同時に初期化も行う場合は次のように書く。
    double[] height = {181.2, 167.5, 175.0};
  • メソッドはC言語の関数と大体同じ。
    • 戻り値(返り値)の型
    • メソッド名
    • 引数(引数の型と引数名)
    • return文

パッケージ

  • プログラミングでは、名前をつける作業が沢山ある。クラス名、フィールド名、メソッド名、インスタンス名(変数名)、等々。これらの名前を適切につけることはわかりやすいプログラムを書くことにおいて非常に重要。
  • 名前がかぶると困る。
  • 名前空間(Namespace)
  • Java言語における名前空間の仕組み: パッケージ
  • パッケージ名はユニーク(一意)なものをつける。ドメイン名を逆から記述するのが慣例。
  • 今回は「ユーザ名.lesson04」

NetBeansでパッケージを作る

ソースパッケージを右クリックして新規、Javaパッケージを選択する。

package1.png

パッケージ名を入力。場所がソースパッケージになっていることを確認する。

package2.png

ソース・パッケージの下にできていることを確認する。

package3.png

パッケージに対応してフォルダ(ディレクトリ)ができていることを確認する。

package4.png

クラスとは

  • データと処理をまとめたもの(教科書)
  • 構造体に関数がついたもの(C言語がきちんとわかっている人向けの説明)
  • 型の定義
  • etc..

クラスはフィールドとメソッドで構成される。例えば、下図は、フィールド2つ、メソッド2つを持つクラスを表す。

class.png

クラスの基礎(1): フィールド

カルテ(medical record)を表すクラスを作ろう。今回は、先ほど作ったパッケージに所属させる。パッケージを右クリックして作成する。パッケージが指定されていることを確認する。

medicalrecordclass.png

MedicalRecord.java

ポイント:

  • パッケージ宣言(1行目)
  • フィールドが3つだけのクラス

前回のプログラムでは、体重と身長を別の変数(配列)で管理していたが、同じ人のデータを1つのクラスで表すことができるようになる!

実際に、これを使ってBMIを計算するプログラムを作る。mainメソッドを持つMainクラスを作成しよう。先ほどと同様にパッケージに所属させる。

Main.java

ポイント:

  • パッケージ宣言(1行目)
  • クラスを実際に使う(インスタンスを作る)には、new演算子を使う。
    MedicalRecord mr1 = new MedicalRecord();
  • フィールドにアクセスするには「インスタンス名.フィールド名」とする。
    mr1.name = "たろう";
  • 配列の作成も同様。
    MedicalRecord[] mrArray = new MedicalRecord[3];
    ただし、これは、MedicalRecordが3つ入る配列を作る、というだけなので、それぞれの要素がインスタンス化されているわけではないことに注意!

オブジェクト指向プログラミング的ではないJavaプログラム

前回のまとめ

  • 数値を表す変数の型はC言語と大体同じ。
  • 整数はintを、実数はdoubleを使っておけば大抵は問題ない。
  • boolean型:
    • 真偽を表し、取りうる値は「true」か「false」の2つだけ。
    • C言語にはない型。C言語では0を偽、それ以外を真として扱っている。
    • if文等で使う。
    • 「==」、「>」、「<」等は演算子でboolean型を返す。例えば
      boolean a = (1 == 2);
      のaはfalseになる。
  • 制御構文(if else、for、while等)はC言語と大体同じ。

オブジェクト指向プログラミング

オブジェクト指向プログラミング(object-oriented programming, OOP)というプログラミングにおける考え方がある。これは、プログラムの組み方をオブジェクト(モノ)を中心にしよう(そうすることによって効率よくプログラムを作成することができる)という考え方である。JavaはOOPに基づいてプログラミングするための機能をサポートしたオブジェクト指向プログラミング言語(object-oriented programming language, OOPL)である。

注意: (特に日本においては)オブジェクト指向プログラミングのことを単に「オブジェクト指向」という言葉で扱い「オブジェクト指向分析設計」、「オブジェクト指向モデリング」といったものまでひっくるめてしまっている場合がある。単に「オブジェクト指向」と書かれているときは何を指しているか注意して読むこと。

ここでは(次回のOOPの解説の前提として)OOP的ではないプログラムとして、BMIを計算するプログラムを作成する。

  • ソースコード: BodyMassIndex.java
  • ポイント:
    • Javaにおける配列の使い方(C言語との違い)
    • メソッド(C言語における関数)
    • 「static」について

変数、演算子、制御文

前回のまとめ

  • NetBeansにおけるクラスの作成手順、実行方法
  • ソースコードのフォーマット
  • クラスの名前はアッパーキャメルケースにする

C言語との違いを中心に。詳しくは教科書を参照すること。

注: 「だいたい同じ」とは「(些細な)違いがある」ということなので全く同じと思わないこと。

変数

数値

C言語において、数値を格納する変数の型はint、long、float、double。Java言語でもだいたい同じ。

文字(単独の文字)

C言語で文字はchar型を使う。文字をシングルクォーテーションで囲むことによって文字を表現する。

これで、変数vには文字「a」が代入される(正確には文字「a」の文字コードである97が代入される。つまりcharは整数型)。

Java言語でもだいたい同じ。

文字列(複数の文字)

C言語ではchar型の配列として扱う。

Java言語ではString型を使う。

boolean型(論理型)

C言語では、真偽を表すのにint型を使い、真が0以外、偽が0としてきた。

例えば、

とした場合、aには0が格納される。

Java言語には真偽を表すboolean型がある。boolean型は値としてtrue(真)かfalse(偽)をとる。

論理演算はC言語と同じ。

型の種類

Java言語の型は、大きく2種類に分類される。

  • プリミティブ型
  • 参照型(クラス型)

ここまで紹介した型のうち、String型が参照型で、それ以外はプリミティブ型。とりあえず、ここでは「2つの種類がある(違いがある)」ということを覚えておくこと。

変数の宣言

C言語では変数の宣言は関数の最初で行う(最近のC言語ではそうでもない)。Javaでは関数(メソッド)の最初じゃなくても良い。

演算子

演算子とは「演算を表わす記号」のこと。

C言語とだいたい同じ。

演算子によって、被演算子(演算が作用する対象)として使える型が違うことに注意すること。

  • 算術演算子
  • 代入演算子
  • インクリメント演算子、デクリメント演算子
  • 比較演算子
  • 条件演算子
  • 論理演算子
  • キャスト演算子
  • ビット演算子

どんな演算が行われているか?演算の結果がどうなるか?を理解しよう。

制御文

Java言語の制御文は条件の判定にboolean型を使う(C言語は0かそれ以外かを判定する)。これ以外はC言語とだいたい同じ。

制御文の記述に関する注意: かっこは省略するな!ただ「省略できる」ということは知っていなければならない。

  • if else
  • for
  • while
  • break
  • continue
  • switch

NetBeansで「hello, world」

プロジェクトを作る

新規プロジェクト

新規 Javaアプリケーション

MyFirstProject 1

クラスを作る

MyFirstProject 2

New Javaクラス

メインメソッドを書く

実行する

MyFirstProject NetBeans IDE 8 1 と NetBeansでHello World