Java/文法/クラス

出典: フリー教科書『ウィキブックス(Wikibooks)』
ナビゲーションに移動 検索に移動

定義方法[編集]

コード例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の実体 (インスタンスと呼ばれます) ごとに異なる値を持つことができる変数 (以下、フィールドと呼ぶ) としてnamepriceが宣言されています。
ClassSyntaxmain「メソッド」では、Productのインスタンスをnew演算子 (new演算子に到達したときに実行される処理群を「コンストラクタ」の中に記述することができます。
コンストラクタについては後述) を用いて生成することができます。

上記コードは、javacコマンドでコンパイルすると動きます。javaコマンドでインタプリタ的に実行してもエラーになります。

つまり、上記コードの場合、コマンド

javac ClassSyntax.java
java ClassSyntax

で実行することになります。

C言語やC++とは異なり、Javaには「構造体」を表すキーワードはありませんが、メンバーを全て public にしたクラスが構造体( Struct )に相当します。

クラス変数とフィールド[編集]

クラス内の上述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;

    for (int i = 0; i < drink.length; i++) {
      System.out.println(drink[i].name + "の単価は" + drink[i].price + "円");
    }
  }
}
実行結果
牛乳の単価は150円
コーヒー牛乳の単価は180円

メソッド[編集]

先述のコードで、17行目から30行目は、mainClassSyntaxのメソッドとして定義しています。 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;
  }
}
構文
class クラス名 {

  アクセス修飾子 データー型1 フィールド名1;
  アクセス修飾子 データー型2 フィールド名2;
  
  アクセス修飾子 データー型 メソッド名() {
    // そのメソッドの処理;
  }
  
}

コンストラクタ[編集]

コンストラクタを用いるとクラスの初期化を定型化でき、内部構造の詳細を隠蔽できます。 コンストラクタの名前はクラス名です。 コンストラクタの戻値はありません。

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のコンストラクタはひとつのクラスにつき原則ひとつですが、例外的に、引数の型によって処理を変える場合はそれぞれの引数の型に応じたコンストラクタを別々に書けます。よって、引数の型で処理を変える場合は、ひとつのクラス内のコンストラクタの個数が複数になります。

コンストラクターのあるクラスのインスタンスの配列化とprivateフィールド・メソッド[編集]

#インスタンスの配列化で紹介したコードを、privateフィールド・コンストラクター・メソッド・初期化された配列と拡張for文の組合せで再実装すると、下記のように整頓されたコードになります。

ClassSyntax2.java
class Product {
  private String name;
  private int price;

  Product(String name, int price) {
    this.name = name;
    this.price = price;
  }

  public void println() {
    System.out.println(String.format("%sの単価は%d円", name, price));
  }
}

class ClassSyntax2 {
  public static void main(String[] args) {
    Product[] drinks = {
      new Product("牛乳", 150),
      new Product("コーヒー牛乳", 180),
    };

    for (Product drink: drinks) {
      drink.println();
    }
  }
}
実行結果
牛乳の単価は150円
コーヒー牛乳の単価は180円
フィールドを private にすることで、フリーハンドなフィールドへのアクセスを禁止しカプセル化を実現しています。
フィールドを private にしたので、表示を行う手続きをメソッド化しました。
この際、表示イメージとコードを近づけるため、String.format("%sの単価は%d円", name, price)のように書式化メソッドを使用しました。

列挙型( enum )[編集]

多くのモダンなプログラミング言語には、列挙型( enum )が提供されていますが、Java ではクラスとして定義されるので、フィールドやコンストラクターを含むメソッドも定義できます。

[TODO:コード例]