Java の基本(9) ・・・ オブジェクト
今回はやっと、Java の Java らしい一面である「オブジェクト」という概念について学ぶ。これは、BASIC, FORTRAN, C といった「手続き型」プログラミング言語や、LISP などの「関数型」プログラミング言語、あるいは Prolog などに代表される「論理型」プログラミング言語とは言語設計の思想がまったく異なる、「オブジェクト指向型」と総称されるプログラミング言語を特徴付ける本質的な一面である。
もっと詳しいことは ここの後半(§1.3) と ここ。
オブジェクト指向プログラミング(object-oriented programming)では、プログラムが対象としようとしている世界に属す「もの」(=オブジェクト、object)を中心に据えて、それらの特徴を記述することがプログラミングである。
こういったオブジェクトの属性・性質、すなわち、仕事・動作(機能・能力)や道具(情報・データ)をきちんと決めることによって、抽象的概念としての「客」とか「ウエイトレス」とか「コック」といったものを特徴付けることができる。
オブジェクト指向言語では、
Java をはじめとするオブジェクト指向型言語では、データ型という概念はもっと一般化されてクラスというものになり、個々のデータ型の値を取る '変数' という「もの」もオブジェクトという概念に一般化される。すなわち、オブジェクトは、変数のように単に一定のタイプのデータを保持できるというだけでなく、複数のタイプのデータを同時に持つことができるし、一定の機能(つまり、何らかの処理を実行できる能力)もいくつか併せ持っている。したがって、オブジェクトは単なる「変数」以上に一般的な「もの」であるが、抽象的な概念ではなく、具体的に存在する「もの」である。
String はクラスであり、クラスはデータ型の一般化であるから、int 型の変数 n を
A String クラスのオブジェクトの初期化/生成の仕方
B String クラスのオブジェクトの宣言と初期化・生成を同時に行なうには
やはり配列の場合と同様に、宣言@と初期化・生成A(またはA’)を一緒にして
上の表から、各メソッドがどのようなデータ型の引数を何個取り、どんなデータ型の値を戻り値とするかを読み取って欲しい。
例えば、
static でないメソッドの使い方
この例のように、static でないメソッド(表の中で static と付いていないもの)は、オブジェクトを必ず生成して
今の時点では参考程度の理解でよいが、
String str = "abcdefg";
のとき、
str.compareTo("xyz")
の値は負である。オブジェクト str の部分は
"abcdefg".compareTo("xyz")
のように書いてもよい。
str.lenth() や
"abcdefg".length()
の値は 7 である。
str.substring(2)
の値は文字列 "cdefg" であり、
"abcdefg".substring(3,6)
の値は文字列 "def" である(文字列の終わりは endIndex-1 番目の文字であり、この例の場合 "defg" ではないので注意のこと)。
String.valueOf(12>345)
のように使う。この例の場合、値は文字列 "false" である。つまり、
static なメソッドの使い方
そのメソッドが定義されているクラスの名前と結びつけて
クラス名 .メソッド名(このメソッドの実引数)
のように使わなければならない。
参照型には配列型、クラス型、インターフェース型の3つがあるが、String はもちろんクラス型である(つまり、String クラス)。クラス型については後で再度取り上げる。
英字の文章を読み込み、その中に現われる各単語の出現頻度を求め、棒グラフで表わせ。
オブジェクトというものの基本が理解できたら、すでに基本を学んだ「配列」と、int, char, float, double, boolean といった基本データ型ではないがそれに劣らずよく使う「文字列」についてもここで学ぶ。文字列は String という「クラス」の具体的なオブジェクトであり、データ型ではない。
一方、配列はクラスオブジェクトとは異なるタイプのオブジェクトであるが、これらは「参照型」という概念で統一される。
オブジェクト指向とは
「オブジェクト指向」は object-oriented の訳語である。すなわち、オブジェクトというものに「適合する」、オブジェクトというものに「向いている」、オブジェクトを「ベースにしている」という意味である。したがって、オブジェクトとは何かということがわからないと、オブジェクト指向型言語の(したがって Java の)本質は理解できないし、なぜ「そのように書くのか」に対する理屈も理解できない。
例えば、レストランという世界を考えよう。レストランでは個々の客、個々のウェイトレス、個々のコックなどがそれぞれオブジェクトであり、個々の客やウェイトレスやコックは誰も決められた一定の仕事(動作)をする(言い換えれば、それぞれに特徴的な独自の機能=役割を持っている)。例えば、客は料理の注文をし、ウエイトレスは客からの注文をコックに伝えたり、調理が終った料理を客に出したりし、コックは料理を作る。このような機能(動作)のそれぞれを Java ではメソッド(method)という。
一方、こういった仕事をするためにはいろいろな種類の道具や材料(コックは鍋や包丁など、ウエイトレスはメニューや注文伝票やPDAなど)が必要であるし、個々のウエイトレスや個々の客や個々のコック(すなわち、個々のオブジェクト)を区別する名前や年齢、性別などの性質・特性(属性、attribute)を持っている。このような属性を保持するもの(変数)を Java ではフィールドという(他のオブジェクト指向言語ではデータメンバーとかメンバー変数とか、単にメンバーとか、スロットともいう)。
オブジェクト指向でないプログラミング言語ではメソッドのことを通常、関数といい、メッセージのことを関数の引数というのが普通である。
クラスは一種のテンプレート(template、雛形、鋳型)であると考えることもできる。
各クラスの具体的オブジェクトは、その鋳型を使って生成する。このようなことをインスタンス化(instantiation)という。
例えば、レストランにおいては、客、ウェイトレス、コックという3つのクラスがあ
り、各クラス(例えば、ウェイトレスというクラス)に具体的に A, B, C という3人の
ウェイトレスがいるならば、A, B, C それぞれはウェイトレスというクラスのインスタ
ンス(オブジェクト)である。客 D (客というクラスのオブジェクトの1つ)がウェイ
トレス C というオブジェクトに渡すメッセージは注文する料理であり、そのメッセージ
を受けてウェイトレスはコックに料理の注文を出す(この動作がウェイトレスというオ
ブジェクトが備えているメソッドの1つである)。客は、ウェイトレスがどのような知
識を持っているかとか、受けた注文をどのようにコックに伝えるのかは知らない(知る
必要がない)。
このように、あるオブジェクトの持っている情報・データや機能が外部
から分からないようにすること(情報隠蔽)をカプセル化(capsulation)といい、これもオブジェクト指向言語がもつ特徴の1つである。外部から見える情報があってもよい(publicなフィールドやメソッド)。カプセル化、public については現段階では分からなくてもよい。
データ型の拡張としてのクラス
Java のデータ型の基本についてはすでに学んだ:
基本型
論理型 boolean
数値型
整数型 byte, short, int, long
文字型 char
浮動小数点型 float, double
参照型 この§で学ぶ
null型 この§で学ぶ
先ずここでは、Java のクラスという概念をデータ型の一般化として捉える視点からオブジェクトについて説明する。
例えば、文字列 "abc" や "uvwxyz" はそれぞれオブジェクトである。一方、文字列全体(あるいは別の言い方をすれば、文字列という概念)のことを Java では String クラスと言う。つまり、 "abc" や "uvwxyz" などの個々の文字列はどれも String クラスのオブジェクトである。このように、クラスは、ある一定のタイプのデータを保持できると同時に一定の機能を有したオブジェクト全体を呼ぶ呼称であり、そういったオブジェクトとは何かということを特徴付ける抽象的な概念である。
変数など(実体がある) ⇒ オブジェクト
データ型(抽象的概念) ⇒ クラス
データ型の宣言 ⇒ オブジェクトの定義と生成
クラスの例 ・・・ String クラス
我々はすでに、
プログラム例: ここ
といった Java のプログラムを書いてきた。実は、これは ex5 というクラスを定義しているプログラムである。そのような「クラスの定義の仕方」は次回に学ぶとして、ここでは Java のパッケージの1つである
java.lang
の中の1つのクラスとして定義されている String クラスについて、その基本的なことを学んでおきたい。
java.lang パッケージは Java のパッケージの中でも最も基本的なクラスの定義を含んでいるものなので、わざわざ
import java.lang.*;
と書かなくても暗黙に取り込まれる(よって、書かなくてもよい)ことになっている。
@ String クラスのオブジェクトの宣言の仕方
int n;
と宣言するのと同様に、s が String クラスのオブジェクトであることを "宣言" するには
String s; // @
のように書く(オブジェクトは変数の一般化であることに注意)。
ただし、宣言しただけではオブジェクトは生成されない(s は文字列の名前だよ、ということが宣言されるだけであって、s が値として持つ具体的な文字列は生成されない)。配列の宣言を思い出すとよい(やはり、宣言だけでは配列の実体は生成されなかった)。
String s;
のように、すでに宣言されている String オブジェクト s の実体を生成する(すなわち、s を初期化して、具体的文字列を値として持たせる)ためには、配列の生成・初期化と同様に
new演算子
を使って次のように書く:
s = new String("abcd"); // A
これも、やはり配列の初期化の場合と同様に、new を省略して、
s = "abcd"; // A’
と書いてもよい。
String s = new String("abcd"); // B
と書くことや、さらに new を省略して
String s = "abcd"; // B’
と書くことも許されている。
文字列に対する演算
String クラスには、2つの文字列の大小関係、相当性、文字列の一部(部分文字列)の取り出し、文字列をつなげる、等の操作を行なうためのメソッドが定義されている。それらのいくつかを挙げると・・・ (一般に、こういうメソッドやフィールド等について調べたいときには、API: http://java.sun.com/j2se/1.5.0/docs/api/index.html を参照せよ)、
String クラスのメソッド 意 味
char charAt(int index) この文字列の指定した添字位置の文字
int compareTo(String anotherString) この文字列と引数の文字列とを辞書式順序で比較
String concat(String str) この文字列の末尾に引数の文字列を連接
int indexOf(int c) 文字cをこの文字列の先頭から探し、最初の出現位置を求める
int indexOf(String str) 文字列strをこの文字列の先頭から探し、最初の出現位置を求める
int length() この文字列の長さ
String substring(int beginIndex) この文字列の指定した添字位置から末尾までの部分文字列
String substring(int beginIndex, int endIndex) この文字列の指定した開始添字位置から指定した終了添字位置までの部分文字列
String toLowerCase() 小文字に変換
String toUpperCase() 大文字に変換
static String valueOf(boolean b) 論理値bを文字列に変換
static String valueOf(char c) 文字cを文字列に変換
static String valueOf(char[ ] data) 文字配列dataを文字列に変換
static String valueOf(double d) double型実数dを文字列に変換
static String valueOf(float f) float型実数fを文字列に変換
static String valueOf(int i) 整数iを文字列に変換
String str = "abcdefg";
のように生成されているとき、
char c = str.charAt(2);
のように使い、そのときの str が「この文字列」である。
右辺の値である str.charAt(2) すなわち "abcedfg" の2文字目の文字 'c' が、左辺にある char 型の変数 c に代入される(2文字目の文字は 'b' ではないことに注いのこと)。
オブジェクト名 .メソッド名(このメソッドの実引数)
のように使わなければならない。
の2種類があり、
(例) String クラスのメソッド
String abc = new String("abc"); // Stringクラスのオブジェクトabcの生成
String pqrst = "pqrst"; // Stringクラスのオブジェクトpqrstの生成
// (abcの場合とは別の書き方)
String s25 = "abcdefg".substring(2,5); // 部分文字列の取り出し
char c = pqrst.charAt(3); // 指定位置の文字
System.out.println(pqrst.length()); // 文字列の長さ
System.out.println(pqrst.compareTo(abc)); // 文字列の大小比較
String PQRST;
PQRST = pqrst.toUpperCase(); // 大文字への変換
System.out.println(PQRST.toLowerCase()); // 小文字への変換
String s123;
s123 = String.valueOf(123); // 数値を文字列へ変換
参照型
一般に、変数には、それが保持する値を格納しておくための場所がコンピュータのメモリ(主記憶装置)上に割り当てられる。例えば、
int a = 123; double x;
と宣言された変数 a, x にはそれぞれ4バイト、8バイトの領域が確保され、a, x が持っている現在値はそこに格納される。
+-----+
a | 123 | a の値を入れる場所に、初期値123が入る
+-----+
+----------+
x | | x の値を入れる場所は確保されるが、値は未定
+----------+
この後で、例えば
x = 12.3;
を実行すると、x の格納場所に浮動小数点数の値 12.3 が入れられる:
+----------+
x | 12.3 |
+----------+
一方、配列の場合には、
一般に、参照が指すデータのことをオブジェクトと言う。
String クラスの変数は String オブジェクト(文字列データ)への参照である。例えば、
String s1;
という宣言では、s1 が String クラスであることが宣言されるだけで、具体的な文字列データ(文字列オブジェクト)は生成されていない:
+----------+
s1 | | s1 の値(参照)は定義されていない
+----------+
このあとで、
s1 = new String("This is a string");
あるいは
s1 = "This is a string";
とすると、String クラスのオブジェクト s1 の実体(すなわち、具体的な文字列) "This is a string" が生成され、s1 にはその文字列が格納されているメモリ領域のアドレスが入る(s1 には "This is a string" へのポインターが入る):
+----------+
s1 | ・---+------> "This is a string"
+----------+