クラスの継承

前回のまとめ

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

クラスの継承

継承のポイント

  • 継承とは、既存のクラスをもとに新しいクラスを定義する機能のこと。
  • 元となるクラスをスーパークラス(親クラス)、新しく定義するクラスをサブクラス(子クラス)という。
  • サブクラスはスーパークラスのメンバー(フィールドとメソッド)を受け継ぐ。
  • サブクラスでは新しくメンバーを加えることができる。
  • スーパークラスのメソッドをサブクラスで書き換える(動作を変える)ことができる(オーバーライド)。オーバーライドするときはアノテーションをつけたほうが良い。

  • サブクラスのインスタンスは、スーパークラスのインスタンスとして扱うことができる。
  • スーパークラスを指定していなくても(指定していない場合は)、Objectクラスがスーパークラスになる。つまり、すべてのクラスの元をたどるとObjectクラスに行き着く。
  • サブクラスからスーパークラスのメンバーを呼び出すことができるが、その場合に「super」を使う場合がある。

継承の利用例1

クラス図1

継承の利用例2

クラス図2

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

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

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

そして、配列は、参照型として扱われる(要素がプリミティブ型であっても)。

(7)、(8)、(9)でどのように変化するのか確認しよう。

比較

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

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

では、参照型で比較したい場合はどうしたらいいのだろうか?これは、地味にフィールドを比較していくしかない。Circleクラスの場合はフィールドが1つだけなので簡単だが、フィールドがたくさんある場合は大変だ。なので、比較が必要なクラスは比較するためのメソッドを作っておくのが良い。じゃあ、そのメソッドをどうやって作るか、利用するか、ということに興味があったら「java equals override」といったキーワードで調べよう。

参照型あれこれ

文字列

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

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

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

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

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

配列

C言語の配列

C言語では、データをまとめて扱う方法(の1つ)として、「配列」があった。例えば、int型の変数を5つ扱う場合には、

と宣言することによって、a[0]、a[1]、a[2]、a[3]、a[4]という5つの変数が使える。

プリミティブ型の配列

  • Main1.java: プリミティブ型の配列の利用例

宣言とインスタンス化

Java言語では、「int型の配列」は「「int型の配列」という型」があると考えた方が良い。だからというわけでもないが、C言語のように変数名の後ろにかっこがくるのではなく、「int[]」を型として扱うように書く。そして「インスタンス化」が必要。インスタンス化はnew演算子を用いる。

注意して欲しいのは、プリミティブ型の配列の場合、インスタンス化時に各要素は勝手に初期化されてしまう、ということ。例えば、

は、bが初期化(値の代入)がされていないのでエラーだが、

はエラーにならない。aの各要素は0で初期化されている。もちろん、

はaの宣言だけでインスタンス化がされていないので、何もできない。次のように宣言とインスタンス化を分けるのはOK。

要素へのアクセス

C言語と同様に添え字でアクセスできる。

添え字に変数を使うこともできるので、for文を使ってアクセスすることもできる。

また要素数を直接書かずに、次のように書くこともできる。

ここで、a.lengthに注目しよう。これは「配列aの要素数」を表している。

実は、配列というのは、クラスのインスタンスのようなもので、lengthというフィールドを持っているように扱える(ただし読み出し専用)。配列の要素も、「添え字でアクセスできる特殊なフィールド」と考えて良い。

つまり、

のイメージはこんな感じ。

宣言と同時に初期化

要素数の指定は必要ない。

クラス型(参照型)の配列

  • Main2.java: クラス型(参照型)の配列の利用例

参照型の配列も同様だが、各要素はそれぞれインスタンス化する必要がある。例えば、lesson07で作成したRectangleクラスの配列を作ってみよう。別のパッケージのクラスを使うためのimport文については教科書P.167を参照。

この状態では、各要素はインスタンス化されていないことに注意(プリミティブ型の配列の場合は、配列のインスタンス化のタイミングで要素が初期化されていた)。
だから、配列をインスタンス化しただけでは、次のコードはエラーになる。

aの0番目の要素a[0]がインスタンス化されていない(つまりa[0]という入れ物が空っぽ)だからだ(これが所謂「ぬるぽ」)。

だから、各要素をインスタンス化(あるいはインスタンスを代入)してやらなければならない。

forの利用や初期化等はプリミティブ型の場合と同様。

その他のトピックス

  • mainメソッドの引数はString[]である。これはコマンドラインから実行するときに指定することができる。つまり、プログラムの実行時に(コンパイル時ではなく)値を渡すことができる!!
  • 他にも、多次元配列に関して等、知っておいた方がいいことがあるが、本講義では取り扱わない。実用の観点からは配列よりもコレクションフレームワークを使うことの方が多い(来年度のJava IIで扱う)。ただし、処理二よっては配列を使った方が都合が良い場合もあるので、ぜひ調べてみて欲しい。

メソッド (3) コンストラクタ

コンストラクタ: インスタンス化の時に実行される特殊なメソッド

これまでの作ってきたクラスは、主に次のような使い方をしてきた。

  1. インスタンス化を行い、
  2. 各フィールドの値をセットし、
  3. メソッドを呼び出す。

大抵のクラスでは、インスタンス化の時に行いたい処理がある。上記の例では「各フィールドの値をセットし、」がそれ。
この処理を記述するための専用のメソッドとし、コンストラクタ(constructor)という仕組みがある。

コンストラクタとは

  • インスタンス化の時に実行されるメソッド
  • 定義の書式が普通のメソッドと違う
    • メソッド名をクラス名と同じにする
    • 戻り値の型は書かない(注意: voidではない)
  • 複数のコンストラクタを定義することができる。メソッド名は同じなので、引数の型、数、並び順で区別される。

インスタンス化の注意

  • コンストラクタを1つでも定義した場合は、定義したコンストラクタでしかインスタンス化できない。例えば、引数有りのコンストラクタを1つだけ定義したクラスは、引数無しでインスタンス化することができない。
  • コンストラクタを1つも定義しない場合は、デフォルトコンストラクタが自動的に用意されるので、引数無しでインスタンス化することができる。

コンストラクタを定義して使ってみよう

  • BMIの算出
    • Person.java (コンストラクタを定義しない場合)
    • Main1.java (コンストラクタを定義しない場合)
    • Person.java (コンストラクタを定義する場合)
    • Main1.java (コンストラクタを定義する場合)
  • 三角形

メソッド (2)

前回のまとめ等

  • メソッド
    • 教科書P.74
    • メソッドとは?Cにおける関数と大体同じ。
    • メソッドは、クラスの中に定義される。
    • メソッドの定義には「public」をつける(今のところ、本講義では)
    • Cとの違い: メソッドは、同じクラスのフィールドを使うことができる。
    • trivia: 教科書に「return (x);とも書きます。」とあるが、今時、カッコはつけない(つけるべきではない)ので、当該箇所は無視すること。詳しくは「c言語 return カッコ」等で検索。
  • クラスの概念(教科書P.68)
    • フィールドとメソッドのことを「メンバー」という。
    • クラスはメンバーのみで構成される(メンバー以外の構成要素はない)。
    • クラスは設計図。利用するときはインスタンス(実体、オブジェクト)を作成する。
  • クラスの定義(書式)
    • クラス名は大文字から始める。
    • ファイル名は「クラス名.java」にする(基本的に1クラス1ファイル)。
    • フィールド2つ、メソッド1つ(引数が2つの場合)のクラスの例:

もっとメソッドを定義してみよう

Personクラスのポイント

  • 「/**」から始まるコメントはJavaDocというソースコード中にコードの説明を書いて後から簡単に参照する仕組み。IntelliJ IDEAではカーソルを合わせてF1キーを押すと参照できる。
  • setメソッド(23-29行目)
    • フィールド名と引数名が同じ場合は、どのように扱われるか?メソッド本体では、引数の方が優先されて使われる。メソッド本体でフィールドを参照したい場合は「this.フィールド名」とする(教科書P.110)。
    • メソッドを介してフィールドの値をセットしている。なぜわざわざメソッドを介してセットするのか考えてみよう。
  • getStatusメソッド(35-49行目): 特筆する点無し。
  • getBmiCategoryメソッド(55-64行目)
    • 同じクラス内のメソッドを呼び出すことができる(教科書P.76)
  • getBmiAfterDieting(55-64行目)
    • 引数有りのメソッド

メソッド (1)

前回のまとめ等

  • 変数・演算子・制御文はCとだいたい同じだが、Cにはなかった型としてbooleanとStringがある。比較演算子等がbooleanを返すことに注意。
  • パッケージ(教科書P.164〜)
    • クラスをひとまとめにしたもの
    • パッケージが違えば名前のクラスでも同時に存在できる
    • パッケージに所属した状態とは?: 1) パッケージを表すディレクトリ(フォルダの中にソースコードが入っている)、2) ソースコードにパッケージ宣言が書かれている
  • クラスとは(前回までの説明。クラスの説明の全てではないことに注意すること)
    • データと処理をまとめたもの(教科書P.68)
    • 既存の型を組み合わせて、「新しい型」を作る仕組み
    • 構造体に関数がついたもの(C言語がきちんとわかっている人向けの説明)
  • 前回作ったクラスについて
    • 既存の型(int型)を組み合わせて、新しい型を作った。
    • クラスの中で定義される変数のことを「フィールド」という(教科書P.72)。
    • クラスの定義とその中のフィールドの定義には「public」をつける(今のところ、本講義では)。
    • クラスは「設計図」といえる。実際に使うには、「実体」を作る必要がある。実体のことを「インスタンス」という(教科書では「オブジェクト」と呼んでいる)。
    • インスタンス化(インスタンスを作ること)にはnew演算子を使う(教科書P.70,71)。
    • インスタンスのフィールドにアクセスするには「.」を使う(例えば、以下はフィールドに代入する場合)。

復習

  1. パッケージ「d00000.lecture05」を作る。
  2. d00000.lecture05に、前回と同様にPersonクラスを作る。
  3. さらに次のフィールドを追加する。
    • 名前を表すnameフィールド
    • 年齢を表すageフィールド
    • 運転免許証の有無を表すhasDriverLicenseフィールド

クラスにメソッドを追加する

  • 教科書P.74
  • メソッドとは?Cにおける関数と大体同じ。
  • メソッドは、クラスの中に定義される。
  • Cとの違い: メソッドは、同じクラスのフィールドを使うことができる。
  • メソッドの定義には「public」をつける(今のところ、本講義では)
  • フィールドとメソッドのことを「メンバー」という。
  • trivia: 教科書に「return (x);とも書きます。」とあるが、今時、カッコはつけない(つけるべきではない)ので、当該箇所は無視すること。詳しくは「c言語 return カッコ」等で検索。

フィールド2つ、メソッド1つ(引数が2つの場合)のクラスの例:

PersonにBMIを計算するメソッドを付け加えてみよう。

ポイント:

  • BMIの計算には2つの値(身長と体重)が必要。Cの場合は関数の引数としたが、Personクラスにメソッドを記述する場合、フィールドの値を使えば良い(使うべき)なので、引数無しになる。

Javaにおける変数・演算子・制御文とクラス

IntelliJ IDEAでJavaのプログラム作成して実行するための基礎知識

  • 本講義では最初から「パッケージ(教科書P.164)」を用いる。同じクラス名であってもパッケージが違えば区別できる。
  • 「src」を右クリック→New→Package→「d00000.lecture04」と入力(d00000はユーザ名)しOKをクリック
  • Print.java(教科書P.4)をIntelliJで作成して実行しよう。
  • 対象のパッケージを右クリック→New→Java Class
  • mainメソッドの中身を追記する。
  • IntelliJで作成したPrint.java
  • 教科書との違い
    • 1行目: package宣言がある。
    • 3行目: classの前にpublicが付いている。
  • ポイント
    • Cの場合はmain関数から実行が開始されるが、Javaの場合はクラスの中に定義されたmainメソッドが実行の起点となる。
    • mainメソッドの定義には「public static」がつく。とりあえずつけておく(今後、解説)。

IntelliJ IDEAでよく使う機能(使って欲しい機能)

  • Rerformat Code(インデントを整える等、ソースースコードを綺麗に整形する): Alt + Ctrl + L
  • Rename(変数やメソッド等の名前を変更する):対象にカーソルがある状態でShift + F6

Android StudioはIntelliJ IDEAがベースであり、操作方法はほぼ同じなので参考になる。

変数・演算子・制御文

変数・演算子・制御文について、C言語との違いを中心に。詳しくは教科書を参照すること。

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

実際に実行してみるとわかりやすい。確認用のクラス(例えばSandbox.javaのように)を作って確かめながら進めよう。

変数(教科書P.8〜)

数値(教科書P.10)

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

文字(教科書P.12)

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

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

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

文字列(教科書P.13)

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

Java言語ではString型を使う。

「+」で連結することができる(教科書P.13)。

boolean型(教科書P.34)

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

例えば、

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

Java言語には真偽を表すboolean型(論理型)がある。boolean型は値としてtrue(真)かfalse(偽)をとる。比較演算子や論理演算子はboolean型を返す演算子ということに注意する(演算子については後述)。

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

型の種類

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

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

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

変数の宣言

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

演算子(教科書P.25〜)

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

C言語とだいたい同じ。

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

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

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

制御文

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

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

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

クラス

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

とりあえず、現段階では「変数と関数をまとめたもの」と考えよう。とりあえず、変数だけをまとめた場合(変数だけをまとめた型、つまりCにおける構造体とほぼ同様)を考えよう。例として「BMIを計算するプログラム」を作成する。

参考: BMIと適正体重

クラスを使わない場合

比較のため、まずは(mainメソッドが含まれるクラス以外の)クラスを作成しない場合を考えてみよう。

2人分のデータを扱うために、double型の変数を4つ作成している。

CalcBmi1

身長と体重をフィールドとして持つPersonクラス

次にデータを身長と体重をまとめて扱うためのPersonクラスを作成し、それを用いてBMIを計算してみよう。

ポイント

  • Personクラスではクラスに所属するフィールドを2つ定義している。
  • mainメソッドでは、Personクラスののインスタンスを生成し、インスタンスのフィールドに値を代入している。

Personというクラスを作ることによって、Personという型を使えるようになったと考えよう。Person型は、これまで使ってきた型(intやdouble等のプリミティブ型)と違い、値を1つだけ格納するのではなく複数の値を格納することができる。

CalcBmi2

何が嬉しいのか?これまでは関連するデータであっても別の変数で管理するしかなかった(あるいは、変数名で工夫するか、配列にするか)が、クラス(=新しい型)を定義することによって、関連するデータをまとめて扱えるようになる。

クラスにフィールドを追加しよう

次のフィールドを(定義に)追加してみよう。型は(指定がない場合は)適切なものを考えよう。課題ではないが、次回講義で実装例を紹介するので、取り組んでおくこと。

  • 名前を表すnameフィールド
  • 年齢を表すageフィールド
  • 運転免許証の有無を表すhasDriverLicenseフィールド