継承: スーパークラスとサブクラス

書籍を表すBookクラスを継承して、漫画本(巻という概念がある)を表すクラスを作ろう。

ソースコード

mainメソッドで、それぞれ使ってみよう。

実行結果:

Javaの絵本, アンク, 1580円
ドラえもん, 藤子不二雄, 500円

Comicのコンストラクタ

  • 継承したクラス(サブクラス)のコンストラクタでは、スーパークラスのコンストラクタを最初に呼ばなければならない。
  • スーパークラスのコンストラクタはsuper(引数)で呼ぶことができる。
  • スーパークラスに引数なしのコンストラクタ(デフォルトコンストラクタを含む)がある場合、「super();」という記述を省略することができる(つまり書かなくても呼ばれる)。

オーバーライド

  • スーパークラスと同じメソッドを定義するとそのメソッドを上書きすることができる。
  • スーパークラスのメソッドは super.メソッド名(引数)で呼び出すことができる。
  • @Overrideはなくてもいいが、つけておくとオーバーライドできているかチェックしてくれる(アノテーション)。

実行結果:

Javaの絵本, アンク, 1580円
ドラえもん, 藤子不二雄, 500円, 1巻

合計金額を計算する(1)

代入記号の意味

プリミティブ型(基本型)は「箱」のイメージ。代入によって、値がコピーされて箱に格納される。

Assignment1

クラス型(参照型)は「タグ」のイメージ。
代入によって、同じインスタンスに結びつく。

Assignment2

もっと色々知りたい人のために:

  • タグが1つも付いていないインスタンスはどうなるのか?→GC(ガベージコレクション、ゴミ集め)によってメモリから消えて無くなる。[教科書P.82]
  • Null Pointer(ヌルポ)とは、タグの先にインスタンスがない状態のこと。
  • クラス型のインスタンスをコピーするのはどうするのか?→自分で実装する。大抵はcloneメソッドをオーバーライドする。
  • インスタンスをコピーするコードを書いたけど、思った通りにコピーされてない。→シャローコピー(shallow copy)とディープコピー(deep copy)について調べよう。

サブクラスのインスタンスをスーパークラスのインスタンスとして扱う

Comicクラスのインスタンスは、Bookクラスのインスタンスとして扱うことができる。なぜならComicクラスはBookクラスを受け継いでいるから(継承しているから)。

多態性[教科書P.108]

  • comic2で呼び出すときは、Comicとして扱うので、volumeフィールドにアクセスできる。
  • book2で呼び出すときは、Bookとして扱うので、volumeフィールドにアクセスできない。
  • book2.getInfo()は上書きした方(ComicのgetInfo)が呼び出される(Bookとして扱う場合であっても、実体はComicクラスのインスタンスだから)。

合計金額を計算する(2)

ArrayListに数値を入れる

キーボードから入力した整数の和と積を計算しよう!(ArrayListにintを入れる)

ポイント

  • ArrayListにはプリミティブ型は入れられないが、ラッパークラス(プリミティブ型に対応したクラス型)を使うことで入れることができる。(教科書P.144)
  • 文字列から整数に変換するにはInteger.parseIntメソッドを使う。(教科書P.145)
  • 「quit」以外の文字列を入力すると、例外(エラー)が発生してプログラムがそこで終了する。例外が発生してもきちんと動くプログラム(例えばエラーメッセージを出して再入力させる等)にするには、例外処理を行うようにする。(教科書P.122)

キーボードから文字列を入力

キーボードから文字列を入力しよう。

ポイント:

  • Scannerクラス(教科書P.176)
  • 文字列を比較する(教科書P.53)

リスト

ArrayListとは(リストとは)

ArrayListは「順序付きのデータコンテナ」を表すクラスで、配列のように複数の変数(インスタンス)を入れておくことができる。

配列との違い:

  • プリミティブ型の変数は入れられない(ただしラッパークラスを使えばいいので支障はない)
  • 入れられる個数が変えられる。というか、追加できる(個数が増えていく)
  • 要素を操作する便利なメソッドがある
  • etc…

利用例

  • インスタンス化は、ArrayList<中に入れる型> インスタンス名 = new ArrayList<中に入れる型>();とする。
  • 要素の数は指定しない。最初は0。追加すれば勝手に増えていく。
  • 追加するにはaddメソッドを使う。
  • sizeメソッドで要素の数が取得できる。
  • 取り出す時はgetメソッドを使う。引数は0から始まる順番を指定する。
  • 全部消すにはclearメソッドを使う。

配列

C言語の配列

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

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

Java言語の配列

Java言語でも配列が使えるが、C言語とは違うところがある。

宣言とインスタンス化

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

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

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

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

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

要素へのアクセス

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

添え字に変数を使うこともできるので、for文を使ってアクセスすることもできる。例えば、全ての値を表示するには次のようにする。

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

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

つまり、

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

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

参照型の配列も同様だが、各要素はそれぞれインスタンス化する必要がある。
例えば、lesson05で作成したBookクラスの配列を作ってみよう。

これは、18行目でエラーになる。bookArrayの要素bookArray[0]がインスタンス化されていない(つまりbookArray[0]という入れ物が空っぽ)だからだ。これが有名な「ヌルポ」である。

だから、各要素をインスタンス化(あるいはインスタンスを代入)してやらなければならない。17〜19行目を削除して、次を追加しよう。

これで、先ほどのループも実行できる(28〜30行目を追加)。

もちろん(条件が合えば)for文を使って各要素のインスタンス化をしても良い。

mainメソッドの引数

mainメソッドの引数はString[]である。これはコマンドラインから実行するときに指定することができる。つまり、プログラムの実行時に(コンパイル時ではなく)値を渡すことができる!!

その他のトピックス

他にも、初期化や多次元配列に関して知っておいた方がいいことがあるが、本講義では取り扱わない。実用の観点からは配列よりもコレクションフレームワークを使うことの方が多く、その説明に時間を使いたいからである。ただし、処理二よっては配列を使った方が都合が良い場合もあるので、ぜひ調べてみて欲しい。

パッケージ、アクセス修飾子

名前空間、パッケージ

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

NetBeansでパッケージを作る

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

Newpackage1

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

Newpackage2

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

Newpackage3

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

Newpackage4

パッケージに所属するクラスを定義する

Newclass

  • パッケージのフォルダ内にクラスファイル(.java)を作成する。
  • パッケージ宣言を記述する。

同じパッケージに所属するクラスから使う

Bookクラスと同じようにMainクラスを作って、そこからBookクラスを使ってみよう。

Mainクラスにもpackage宣言がついたくらいで、これまでと特に違いはない。

違うパッケージに所属するクラスから使う

デフォルトパッケージにLesson05クラスを作成し、その中のmainメソッドから先ほどのBookクラスを使ってみよう。

違うパッケージから使うには、単にクラス名ではなく「パッケージ名.クラス名」で記述する。クラス名だけで使えるのは、同じパッケージに所属する(あるいはデフォルトパッケージに所属している)クラス。

ただし、まだこの状態ではエラーが出る。パッケージが異なるクラスからのアクセスには制限がかかっているからである。

アクセス修飾子

クラスの利用に関して制限をかけることができる。実は何も指定していない場合でも制限がかかっている。この利用の制限を設定するのが「アクセス修飾子」である。

アクセス修飾子の意味は4種類ある。とりあえず重要なのはpublicとprivate。

  • public
  • private
  • なし(つけない)
  • protected

どのような意味があるかは教科書を参照しよう。今回関係があるのは、「パッケージが同じ場合」と「パッケージが違う場合」について。

アクセス修飾子をつける対象はクラスとメンバー(フィールドとメソッド)。

先ほどのLesson5.javaのコンパイルエラーをなくすには、

  • Bookクラスを使うからclass Bookにpublicをつける。
  • コンストラクタを使うからコンストラクタにpublicをつける。
  • printInfoメソッドを使うからprintInfoメソッドにpublicをつける。

というように修正すれば良い。(注: このように「利用する側の都合」で「呼び出される側」を修正する、というのは設計がうまくいっていないことなので、あまり良くない。本来は、利用されるケースを想定してアクセス修飾子を適切に設定する)

これで、Lesson05.javaはコンパイルできるようになった。

ついでに、フィールドにはアクセスできない(例えば、book.price = 1000;等はエラーになる)ことを確認しておこう。

import

別のパッケージに所属しているクラスを使うには「パッケージ名.クラス名」とすれば良い。ただ、長くて面倒。

importを使うと、「パッケージ名.」を省略することができる。

import lesson05.d00000.Book;としておけば、このファイル内で「Book」と出てきたら、それは「lesson05.d00000.Book」という意味になる。

よくある質問

Q: パッケージに階層構造(包含関係)は?

A: ない。格納する場所(ソースコードの置き場所)は階層構造になるけど、各パッケージはなんの関係もできない。例えば「lesson05.p」と「lesson05.p.q」に包含関係は(見た目はありそうだけど)ない。

メソッド

メソッドとは(復習)

  • メソッドとはC言語の関数のような、「処理がまとめられたもの」である。
  • メソッドはクラスの中に作られる。
  • メソッドを実行するときに値を渡すことができる。その値のことを引数という。
  • メソッド内では、引数以外にも、インスタンスにあるフィールドも使うことができる。もちろんメソッド内で作った変数も使える。
  • メソッドの処理結果(値)を呼び出し元に返すことができる。その値を戻り値(返り値)という。

メソッドの例

Bodyクラスを題材にメソッドの実装例を見ていこう。実装したBodyの利用例も参照すること。

引数がなくて返り値がある

returnで返り値を指定する。

引数が1つで返り値がない

返り値がない場合は返り値の型の代わりに「void」と書く。

引数が2つで返り値がない

引数が複数の場合は「,」で区切る。

引数の名前がフィールドとかぶる場合、単にその名前を書くと引数の方が使われる。このような状況でフィールドの方を使うには「this.フィールド名」とする(教科書P.98)。

コンストラクタ

コンストラクタは「インスタンス化のときに実行されるメソッド」である。

例えば、これまではインスタンスを作成した後にフィールドに値を代入していたが、インスタンス化と同時に値を代入できるようなコンストラクタを定義してみよう。

コンストラクタの定義の注意点(普通のメソッドとの違い)は以下の2点。

  • コンストラクタの名前はクラス名と同じにする。自由につけられるわけではない。言い換えると、クラス名と同じ名前のメソッドはコンストラクタである。
  • コンストラクタには返り値がないので、返り値の型は記述しない。voidも書いてはいけない。

このコンストラクタを使ってインスタンスを作るには次のようにする。

ただし、この状態(コンストラクタを1つ定義した状態)でコンパイルしようとすると、Bodyクラスを呼び出しているクラス(Lesson03やLesson04)がエラーになってしまう。これは、「new Body()」に対応したコンストラクタが用意されていないからである。

「new Body()」に対応したコンストラクタも定義しよう。ここでは、乱数で身長と体重をセットすることにする。

これで、「new Body()」も使えるようになった。

そもそも、これまではなぜコンストラクタを定義していないのに「new Body()」でインスタンス化できていたのだろうか?それは、コンストラクタを1つも定義しない場合は、デフォルトコンストラクタという何もしないコンストラクタが生成されていたからである(教科書P.76)。デフォルトコンストラクタは、「コンストラクタを1つも記述しない場合」にだけ生成されることに注意すること。

クラスの基礎

クラスとは

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

クラスはメンバーによって構成される。メンバーにはフィールド(データ、変数)とメソッド(C言語における関数)の2種類がある。

身体データ(身長と体重)からBMIを計算するプログラムを作ってみよう(ソースコード全体

クラスを使わない例

クラスを使う例

まずはクラスを作る(定義する)。まずは身長と体重を格納するフィールドだけのクラスを考えよう。

  • クラス名はBodyとする。クラス名は英語の大文字から始めることに注意。
  • weightとheightというフィールドを持つ。

クラスは「型の設計図」なので、設計図から実体を作る必要がある。その実体のことを「インスタンス」という(教科書では「オブジェクト」という言葉を使っている「オブジェクト」は文脈によって意味が違ったりするので本講義では「インスタンス」という言葉を使う)。クラスからインスタンスを作ることを「インスタンス化」という。

mainメソッドの中でBodyクラスをインスタンス化しよう。インスタンス化はnew演算子で行う。

インスタンスのフィールドは「インスタンス名.フィールド名」で利用することができる。それぞれのフィールドに値を代入してみよう。

これでタローのデータをフィールドに格納することができた。クラスを使わない例と同様にBMIを計算できる。ジローについても同様。

次にBMIを計算するメソッドをBodyクラスに加えてみよう(5-9行目)。メソッド名はgetBmiとする。メソッドからはフィールドにアクセスすることができる(C言語における「グローバル変数」のように見えるが、クラス内でならアクセスできるということと、インスタンスごとにフィールドができるという点で大きく違う)。

メソッドを使ってBMIを取得するには、例えば、次のよう呼び出すことができる。フィールドと同様に「インスタンス名.メソッド名(引数)」で呼び出せる。

最終的に表示するコードは次のようになる。

ポイント

  • クラスは設計図で、設計図から実体(インスタンス)を作る必要がある。
  • クラスはメンバーからなる。メンバーにはフィールド(データ、変数)とメソッド(処理、C言語における関数)がある。
  • インスタンス化はnew演算子で行う。
  • インスタンスのメンバーを使うにはドット演算子を使う。

Lecture03_main.png

変数、演算子、制御文

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