クラスの継承

前回のまとめ

  • 型は大別してプリミティブ型と参照型の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文でループ回数に使えば、文字列の長さが変わってもその部分のコードを書き換える必要はなくなる。