Java/文法/クラス
定義方法[編集]
コード例1:
- ClassSyntax.java
/** * 商品を表す。 */ class Product { /** * 品名 */ String name; /** * 単価 */ int price; } class ClassSyntax { public static void main(String[] args) { Product milk = new Product(); milk.name = "牛乳"; milk.price = 150; Product juice = new Product(); juice.name = "ジュース"; juice.price = 200; System.out.println(milk.name + "の単価は" + milk.price + "円"); System.out.println(juice.name + "の単価は" + juice.price + "円"); } }
- 実行結果
牛乳の単価は200円 ジュースの単価は150円
- 解説
- 4行目から14行目までがクラス
Product
の、16行目から31行目までがクラスClassSyntax
の定義である。 Product
では、Product
の実体 (インスタンスと呼ばれる) ごとに異なる値を持つことができる変数 (以下、インスタンス変数と呼ぶ) としてname
とprice
が宣言されている。ClassSyntax
のmain
「メソッド」では、Product
のインスタンスをnew演算子 (new演算子に到達したときに実行される処理群を「コンストラクタ」の中に記述することができる。- コンストラクタについては後述) を用いて生成することができる。
上記コードは、javacコマンドでコンパイルすると動きます。javaコマンドでインタプリタ的に実行してもエラーになります。
つまり、上記コードの場合、コマンド
javac ClassSyntax.java java ClassSyntax
で実行することになります。
C言語やC++とは異なり、Javaには「構造体」の概念は無い。
クラス変数とインスタンス変数[編集]
クラス内の上述String name;
のような要素(フィールドという)を宣言する際、下記のようにstatic 修飾子をつけて宣言したら、そのフィールド要素を「クラス変数」という。
- 構文
class Product { static String name; // クラス変数 }
一方、static無しで宣言したらインスタンス変数という。
class Product {
String name; // インスタンス変数
}
後述の「メソッド」でも同様、static宣言されたメソッドのことをクラスメソッドと言う。
インスタンス変数の所属先は、それのインスタンスを生成した呼出側である。よって、呼出側・呼び出され側のクラスと異なる第三の別クラスからは、直接的にはインスタンスの値を読み取ることはできない。
一方、クラス変数の所属先は、クラス自体にある。この仕様のため、比較的に容易に、第三のクラスからも値の読み書きがしやすい。
- ClassSyntax.java
/** * static を使わない場合 */ class Product { String name; // インスタンス変数 int price; } class ClassSyntax { public static void main(String[] args) { Product milk = new Product(); milk.name = "牛乳"; milk.price = 150; Product cmilk = new Product(); cmilk.name = "コーヒー牛乳"; cmilk.price = 180; System.out.println(milk.name + "の単価は" + milk.price + "円"); System.out.println(cmilk.name + "の単価は" + cmilk.price + "円"); } }
- 実行結果
牛乳の単価は150円 コーヒー牛乳の単価は180円
- ClassSyntax.java
/** * static を使う場合 */ class Product { static String name; // クラス変数 static int price; } class ClassSyntax { public static void main(String[] args) { Product milk = new Product(); milk.name = "牛乳"; milk.price = 150; Product cmilk = new Product(); cmilk.name = "コーヒー牛乳"; cmilk.price = 180; System.out.println(milk.name + "の単価は" + milk.price + "円"); System.out.println(cmilk.name + "の単価は" + cmilk.price + "円"); } }
- 実行結果
コーヒー牛乳の単価は180円 コーヒー牛乳の単価は180円
static を使っているほうは、出力の1行目が「牛乳」ではなく「コーヒー牛乳」に変わっています。これは17行目と18行目でクラス側で管理されていた変数を書き換えたからです。フィールドをstatic修飾子をつけて宣言するとクラス側で管理することになるので、上記のように結果が変わります。
インスタンスの配列化[編集]
実際にクラスを使う場合は、インスタンスを配列にすると便利な場合も多い。その場合、配列の要素数だけ new 宣言が必要である。
要素数が100個とか多い場合にnew を 100個書くのは非現実的であるが、安心してもいいことに for 文を使うことで機械にnew宣言を繰り返しさせる仕組みがあるので大丈夫である。
下記のようなコードになる。
- ClassSyntax.java
class Product { String name; int price; } class ClassSyntax { public static void main(String[] args) { Product[] drink = new Product[2]; for(int i = 0; i < drink.length; i++){ drink[i] = new Product(); } drink[0].name = "牛乳"; drink[0].price = 150; drink[1].name = "コーヒー牛乳"; drink[1].price = 180; System.out.println(drink[0].name + "の単価は" + drink[0].price + "円"); System.out.println(drink[1].name + "の単価は" + drink[1].price + "円"); } }
- 実行結果
牛乳の単価は150円 コーヒー牛乳の単価は180円
.length プロパティの冒頭「l」は小文字である。間違えて大文字にするとエラーになる。
メソッド[編集]
先述のコードで、17行目から30行目は、main
をClassSyntax
のメソッドとして定義している。
javaではクラスの中にしかメソッドを定義することができない。
なお、特にクラスを新規作成する必要もなく単にメソッドを使いただけの場合なら、Hello Worldの定形文にあるクラス以外に新しいクラスを作る必要は無く、Hello Worldを作るときに書かれた既存のクラス内にそのまま追加したいメソッドを書けば済む。
ひとつのクラス内にメソッドはいくつあっても構わない。
おおむね、C言語でいう「関数」に相当する。Javaにはメソッドとは別に「関数」という概念は無い。
引数[編集]
メソッドは引数を持つことができる。
例2:
class Product { /** * 品名 */ String name; /** * 単価 */ int price; /** * 標準出力に商品の内容を書き出す */ void printDetails() { System.out.println(this.name + "の単価は" + this.price + "円"); } /** * 単価を更新する * @param newPrice 新しい単価 */ void setPrice(int newPrice) { this.price = newPrice; } }
※ 上記コードをコンパイルしても何も表示されないが正常である。mainもprintln命令も無いので何も表示されない。
なお、クラスの宣言において、メソッドなどの処理を除いた残りの String name;
やint price;
などの要素の型のみを定義している部分のことを「フィールド」という。後述するコンストラクタのあるコードまで考えれば、クラスからメソッドやコンストラクタなどを除いた残りの、要素の型を宣言している部分のことがフィールドである。
- 構文
class クラス名 { データ型1 フィールド名1; データ型2 フィールド名2; void メソッド名() { // そのメソッドの処理; } }
コンストラクタ[編集]
コンストラクタを用いるとクラスの初期化を定型化でき、内部構造の詳細を隠蔽できる。 メソッドの定義の際、メソッド名をクラス名と同じにすると、それがコンストラクタとして認識される。なお、コンストラクタでは戻り値の宣言をしてはならない。
例3:
class Product { /** * 品名 */ String name; /** * 単価 */ int price; Product(String name, int price) { this.name = name; this.price = price; } } class ClassSyntax { public static void main(String[] args) { Product milk = new Product("牛乳", 150); Product juice = new Product("ジュース", 200); System.out.println(milk.name + "の単価は" + milk.price + "円"); System.out.println(juice.name + "の単価は" + juice.price + "円"); } }
- 実行結果
牛乳の単価は150円 ジュースの単価は200円
Javaのコンストラクタはひとつのクラスにつき原則ひとつだが、例外的に、引数の型によって処理を変える場合はそれぞれの引数の型に応じたコンストラクタを別々に書ける。よって、引数の型で処理を変える場合は、ひとつのクラス内のコンストラクタの個数が複数になる。
コンストラクターのあるクラスのインスタンスの配列化[編集]
#インスタンスの配列化で紹介したコードを、コンストラクター・初期化された配列と拡張for文の組合せで再実装すると、下記のように整頓されたコードになる。
- ClassSyntax2.java
class Product { String name; int price; Product(String name, int price) { this.name = name; this.price = price; } } class ClassSyntax2 { public static void main(String[] args) { Product[] drinks = { new Product("牛乳", 150), new Product("コーヒー牛乳", 180), }; for (Product drink: drinks) { System.out.println(drink.name + "の単価は" + drink.price + "円"); } } }
- 実行結果
牛乳の単価は150円 コーヒー牛乳の単価は180円