2019年10月1日
[ 前の資料 | 講義ホームページ・トップ | 次の資料 ]
メソッドシグネチャをいくつか集めたものに名前をつけたもの.典型的には,同一シグネチャのメソッドを持つ(異なる)クラスのオブジェクトをまとめて扱う際に便利である.具体的には
I
を定義する.implements I
) をつけて定義する.(そのクラスにはインターフェースに書かれたシグネチャを持つメソッドを定義する義務が生じる.)こうすることによって,I
型の変数には,I
を実装したクラスのオブジェクトをどれでも格納することができ,その変数を通じて I
で宣言されているメソッドを呼ぶことができる.この際,実際に呼ばれるメソッド定義は,その時に変数に格納されているオブジェクトのクラスに依存する(動的ディスパッチ(dynamic dispatch)).
Java プログラムは,実際にはコンパイラによってJava仮想機械の命令に変換され,それがJava 仮想機械によって実行されるわけだが,Java仮想機械を持ち出すことなく,Java プログラムの実行過程を理解する・説明できるようになることは,非常に大事である.
同様に,何のプログラミング言語であっても,その実行過程を,実装方式や実行する計算機のハードウェアに依存しない抽象的なレベルで理解する・説明できるようになるのが望ましい.
変数は,データを格納するための,名前がつけられた(メモリ)領域である.変数の働きを理解するためには,
といったことを理解する必要がある.ひとつの言語に上の項目が異なる,何種類もの○○変数が登場することも多い.Java での代表的な変数はインスタンス変数,局所変数,クラス変数(static変数とも呼ばれる)である.
int
),倍精度浮動小数点数(double
) などの数値,または,オブジェクトへの 参照 (背番号,IDのようなもの)である.オブジェクトそのものは(メモリに領域が割り当てられるが)変数には直接格納されない.パラメータや返り値を通じてやりとりされるのもあくまでオブジェクトへの参照である.クラス本体内でメソッド・コンスタラクタと並んで宣言される.
有効範囲はそのクラス内.(この説明は,実際には不正確なところがあるのだが,ひとまず近似としては十分であろう.)
new
式によってオブジェクトが生成されると,インスタンス変数のための領域が ヒープ(heap)1 と呼ばれるメモリ領域中に確保され,初期化子やコンストラクタで,その内容の初期化が行われる.ヒープはプログラム実行中に新たに必要となる(別の言い方をすると,プログラム実行開始時には確保されていない)メモリ領域を提供する仕組みである.後述するように,多くのプログラミング言語ではヒープ以外にも実行時スタックと呼ばれる領域も使ってプログラムの実行が進む.
そのオブジェクトが「使われなくなると」 ごみ集め(garbage collection) と呼ばれるプログラム実行系(Java なら Java 仮想機械)内の仕組みによって,その領域は解放される.
for
文,文の並びの途中で宣言される.(private
修飾子はつけてはいけない.)for
文の場合,その直後のメソッド本体/ブロック { ... }
が有効範囲,文の並びの宣言は,その直後からブロック終了までが有効範囲となる.for
文の実行などをきっかけとしてメモリ領域が確保され,ブロックの終了とともにその領域は解放される.static
修飾子が必要.インスタンス変数は多くの場合 private
という 修飾子(modifier) を伴って宣言するが,後述のように,この修飾子そのものは,変数がインスタンス変数であることを 意味しない .インスタンス変数と局所変数を区別するのはその宣言の場所である.
Java のオブジェクトは,概念的には,クラス名とインスタンス変数の値が集まったものである.
Javaでは new <クラス名>(<式1>,...,<式n>)
という形の式2でオブジェクトを生成する.この式の評価は以下のような過程で行われる:
<式1>
, ..., <式n>
を評価する(それらの値を \(v_1\), ..., \(v_n\) としよう).this
の領域も確保され,オブジェクトのIDが格納される.this
のための領域は解放される.new
式全体の値となる.メソッド呼び出し式は一般には
という形をしている(<式i>は変数であることもしばしばであるが,一般には式が書け,場合によっては,メソッド呼び出し式が入れ子になることもある.例えば,x.m1().m2()
と書けば,これは括弧を補えば (x.m1()).m2()
のことで,x.m1()
の呼び出しの結果として返される参照が指すオブジェクトに対して m2
を呼ぶ,という意味になる.
メソッド呼び出し式の評価は以下のような過程で行われる.
<式0>
, ..., <式n>
を順に評価する(それらの値を \(v_0\), ..., \(v_n\) としよう).this
も確保され,\(v_0\)が格納される.<メソッド名>
という名前のメソッドを選び,その本体を実行する.実行終了とともにパラメータとthis
のための領域は解放される.return
された値が式全体の値になる.冒頭でふれた 動的ディスパッチ の本質は呼び出すメソッドを選ぶステップにある.例えば,
public interface BinarySearchTree {
void insert(int n);
}
public class Leaf implements {
...
}
public class Branch implements {
...
}
のような定義がされている時,BinarySearchTree
型の変数は Leaf
や Branch
クラスから作られたオブジェクトを格納している可能性がある.この場合,同じメソッド呼び出し式でも \(v_0\) が参照するオブジェクトのクラスによって違うメソッドが呼び出される.例えば,
であれば,Leaf
中の insert
が呼ばれ(もし Leaf
に insert
の定義がなかったとしたらそもそもコンパイルできない),
であれば,Branch
中の insert
が呼ばれる.一般には,変数にどちらのクラスのオブジェクトが格納されているかはわからないので,実行しながら決めることになる.
メソッドやコンストラクタの実行の際に確保される,パラメータ変数のための領域を フレーム(frame) と呼ぶ.あるメソッドの実行中に使用できる変数は,このフレームに格納された変数のみである(インスタンス変数については後述).フレームは,メソッドの呼び出し毎に必ず 新たに 確保されることに注意したい.あるメソッド実行中に,例え同じ名前のメソッドが実行されることになっても,二度目の実行のための新たなフレームが用意されて,その下で実行が行われる.このため,二度目の実行中にパラメータ変数に代入を行ったとしても,最初の実行のフレームには影響が及ばない.
呼び出された側(callee)のフレームの(確保から解放までの)生存期間は,呼び出し元(caller)のフレーム生存期間に完全に含まれる,いわゆる入れ子構造になっているため,フレームはスタックを使って実装することが多い.
のi
のような局所変数は,現在実行中のメソッドのフレームを一時的に({}
内の実行中だけ)拡張することで確保していると考えられる.局所変数の生存期間は {}
の入れ子構造と対応する.
Java では,ある局所変数の有効範囲内で同名の変数を宣言することはできない.(インスタンス変数の名前と同じなのは許されている.)
はコンパイル時にエラーになる.(同名の変数を二度宣言してもエラーにならず,使う際には「一番近くで」宣言された変数を参照する,という規則になっている言語も多い.Java がなぜエラーにしているかはわからないが,紛らわしくてバグの温床になるからだろうか.)
上で,メソッドの実行中に使用できる変数はフレーム内の変数のみだ,と書いた.フレームにはメソッドのパラメータなどの局所変数しか入っていない.では,インスタンス変数はどこへ行ってしまたんだ,と思うかもしれない.その秘密は,コンストラクタ・メソッド呼び出しでフレームに導入される特殊な変数 this
にある.実は,インスタンス変数は全て this
経由で読み書きされているのだ!
例えば Branch
クラスの insert
メソッド中に
という文を書いたとしよう.v
はインスタンス変数だが,これは内部的には
の略記として扱われている.Java には <式>.<インスタンス変数名>
という「<式>
の評価結果であるところの参照が指すオブジェクトのインスタンス変数」という意味になる構文が用意されている.this
はフレーム中に,今,操作しようとしている Branch
オブジェクトを指す変数として存在するので,うまくインスタンス変数の値が取り出せる,というわけである.
この <式>.<インスタンス変数名>
という表記は,ほとんど変数のようなもので,上のような値の読み出しに使えるだけでなく,代入文で書き込みを行うこともできる.後で見るように,branch
を表すクラス Branch
のコンストラクタに
という代入が現れている.右辺は(内側の宣言が優先されるので)局所変数(コンストラクタパラメータの)left
の値(オブジェクト参照),左辺は,インスタンス変数の値を読み出しているわけではなく「this
が指すオブジェクトの中の,インスタンス変数 left
を 格納している場所 」を示している.
有効範囲が重なる同名の局所変数宣言(これは上に述べたようにエラーになる)と違って,インスタンス変数の有効範囲内で同名の局所変数を宣言するのはOK3となっているのは,このように this.
記法を使うことができるからかもしれない.
[ 前の資料 | 講義ホームページ・トップ | 次の資料 ]
Copyright 五十嵐 淳, 2016, 2017, 2018, 2019, 2020