Java カプセル化 ウルトラセブンのカプセル怪獣とは関係ない

Javaの三大機能として

が有名ですが、今回はカプセル化の話でございます。 

カプセル化とは?

クラスは、 

  • 属性(フィールド)
  • 操作(メソッド)

といった感じで、大まかに属性と操作という2つのものを持つわけですが、カプセル化は、属性や操作にアクセス修飾子(access modifer)というものを付与することでアクセス制限を設けます。

アクセス修飾子

制限の範囲 名前 アクセスを許可する範囲
プログラム中の指定方法

f:id:ts0818:20170703194507p:plain

 private  自分自身のクラス
 private
 package private  自分と同じパッケージに属するクラス
(何も書かない)
 protected  自分と同じパッケージに属するか、自分を継承した子クラス
 protected
 public  すべてのクラス
 public

なぜ、カプセル化?

グローバル変数の汚染問題。カプセル化という概念がなかった時代は、誰でも自由に変数の中身を変更できたため、意図しない変更がどこで行われていたのか分かりにくいという問題があったようです。

 

アクセス修飾子のパターン

どのアクセス修飾子を使うかは、一般的には、クラスのメンバ(フィールドやメソッド)では、 

  • フィールドはすべてprivate
  • メソッドはすべてpublic

クラス自身は、特に理由がない限り、 

  • publicで修飾

するのが一般的のようです。

getterとsetter

privateにしたら、外部からフィールドにアクセスできない?という疑問を持った方、素晴らしいですね。でも、安心してください、アクセスできますよ。

getterとsetterというメソッドを使うことで、フィールドにアクセスできます。

例:getter

public class Hero {
  /**
  * フィールド変数
  */
  private String name;
  
  /**
  * コンストラクタ
  */
  public Hero(String name) {
    this.name = name;
  }
  
  /**
  * getter
  * return String name
  */
  public String getName() {
    return this.name;
  }
}
public class King {
  void talk(Hero h) {
    // HeroクラスのgetterメソッドであるgetName()でHeroクラスのprivateなフィールドを取得
    System.out.println("王様:ようこそ、地獄の3丁目へ、勇者" + h.getName() + "よ。");
  }
}

実行クラス

public class Main {
  public static void main(String[] args) {
    // Heroクラスのインスタンスを生成
    Hero h = new Hero("リック");
    // Kingクラスのインスタンス生成
    King king = new King();
    // Kingクラスのtalk()メソッド
    king.talk(h);
    
  }
}

getterメソッドによりHeroクラスのprivateなフィールドの値を、Kingクラスから取得できています。

例:setter

public class Hero {
  /**
  * フィールド変数
  */
  private String name;
  
  /**
  * コンストラクタ
  */
  public Hero(String name) {
    this.name = name;
  }
  
  /**
  * setter
  * param String name
  */
  public setName(String name) {
   this.name = name; 
  }
  
  /**
  * getter
  * return String name
  */
  public String getName() {
    return this.name;
  }
}

実行クラス

public class Main {
  public static void main(String[] args) {
    // Heroクラスのインスタンスを生成
    Hero h = new Hero("リック");
    // setter
    h.setter("カール");
    System.out.println("勇者の名前は? " + h.getName);
  }

}    

というような感じで、setterメソッドでprivateなフィールドの中身を変更することができるようです。一般的に、

public データ型 set変更したいフィールド名(データ型 任意の変数名) {
  this.フィールド名 = 任意の変数名;
}
public データ型 get変更したいフィールド名 {
  return this.フィールド名;
}

というように、set〇〇()、get〇〇()というように〇〇の部分には、変更・取得したいフィールド名を指定することが多いみたいです。

カプセル化の演習

実際にカプセル化を行ってみました。 

package chap10;
/**
 * Heroクラス
 * @author ts0818
 * 作成日: 2017/07/03
 */
public class Hero {
  /**
   * フィールド変数
   */
	private int hp;
  private String name;
  private Sword sword;

  /**
   * コンストラクタ
   */
  Hero(String name) {
  	this.hp = 100;
  	this.name = name;
  }

  /**
   * getter name
   * return String name
   */
  public String getName() {
  	return this.name;
  }

  /**
   * setter name
   * param String name
   */
  public void setName(String name) {
  	this.name = name;
  }

  /**
   * getter hp
   * @return int hp
   */
  public int getHp() {
  	return this.hp;
  }

  /**
   * setter hp
   * param int hp
   */
  public void setHp(int hp) {
  	this.hp = hp;
  }

  /**
   * getter sword
   * return Sword sword
   */
  public Sword getSword() {
    return sword;
  }

  /**
   * setter sword
   * @param Sword sword
   */
  public void setSword(Sword sword) {
  	this.sword = sword;
  }

  /**
   * die() メソッド
   */
  private void die() {
  	System.out.println(this.name + "は力尽きた!");
  	System.out.println("GAME OVERです。");
  }

  /**
   * attack() メソッド
   * param Matango m
   */
  public void attack(Matango m) {
  	System.out.println(this.name + "の攻撃!");
  	System.out.println("マタンゴ" + m.getSuffix() + "に" + sword.getDamage() + "ポイントのダメージを与えた。");
  	m.setHp(m.getHp() - sword.getDamage());

  	if(m.getHp() <= 0) {
  		System.out.println("マタンゴ" + m.getSuffix() + "を倒した!");
  	} else {
  		System.out.println("マタンゴ" + m.getSuffix() + "から2ポイントの反撃を受けた");
  		this.hp -= 2;
  		if(this.hp <= 0) {
  			this.die();
  		}
  	}
  }
}
package chap10;
/**
 * Swordクラス
 * @author ts0818
 * 作成日: 2017/07/03
 */
public class Sword {

	/**
	 * フィールド変数
	 */
	private String name;
	private int damage;

	/**
	 * コンストラクタ
	 */
	public Sword(String name, int damage) {
		this.name = name;
		this.damage = damage;
	}

	/**
	 * getter name
	 * return String name
	 */
	public String getName() {
		return this.name;
	}

	/**
	 * setter
	 * param String name
	 */
	public void setName(String name) {
		this.name = name;
	}

	/**
	 * getter damage
	 * return int damage
	 */
	public int getDamage() {
		return this.damage;
	}

	/**
	 * setter damage
	 * param int damage
	 */
	public void setDamage(int damage) {
		this.damage = damage;
	}
}

package chap10;
/**
 * Matangoクラス
 * @author ts0818
 * 作成日: 2017/07/03
 */
public class Matango {
  /**
   * フィールド変数
   */
	private int hp;
	private int LEVEL = 10;
	private char suffix;

	/**
	 * コンストラクタ
	 */
	public Matango(int hp, char suffix) {
		this.hp = hp;
		this.suffix = suffix;
	}

	/**
	 * getter hp
	 * return int hp
	 */
	public int getHp() {
		return this.hp;
	}

	/**
	 * setter hp
	 * param int hp
	 */
	public void setHp(int hp) {
		this.hp = hp;
	}

	/**
	 * getter suffix
	 * return chsr suffix
	 */
	public char getSuffix() {
		return this.suffix;
	}

	/**
	 * setter suffix
	 * param char suffix
	 */
	public void setSuffix(char suffix) {
		this.suffix = suffix;
	}

	/**
	 * run() メソッド
	 */
	void run() {
		System.out.println("マタンゴ" + this.suffix + "は逃げ出した!");
	}

}
    

実行クラス

package chap10;

public class Main {

	public static void main(String[] args) {
		// 
		Main main = new Main();
		Hero h = main.createHero("リック");

	}
	
	Hero createHero(String name) {
		Hero h = new Hero(name);
		System.out.println("勇者" + h.getName() + "の登場。");
		Sword s = new Sword("光の剣", 10);
		h.setSword(s);
		System.out.println("武器は" + h.getSword().getName() + "、破壊力は" + h.getSword().getDamage());		
		return h;
	}

}

Matangクラスのインスタンスや、attack()メソッドとか実行してないけど、一通りクラスにgetterやsetterを実装できました。

講師の先生の解答。勉強になります。

package chap10Ex;

import java.util.Random;

public class Main2 {

	public static void main(String[] args) {

		Main2 main = new Main2();
		Hero h = main.createHero("センセイ");		// 勇者の登場
		Matango[] m = main.createMatango(3);	// お化けきのこの登場
		main.battle(h, m); // 戦闘シーン
		Wizard w = main.createWizard("ファウスト");	// 魔法使いの登場
		w.heal(h);

	}

	// 勇者の生成
	Hero createHero(String name){
		Hero h = new Hero(name);
		Sword s = new Sword("ヒカリの剣", 10);
		h.setSword(s);
		System.out.println("勇者" + h.getName() + "の登場。生命力HP:" + h.getHp() + "、武器は" + h.getSword().getName() + "、破壊力は" + h.getSword().getDamage());
		return h;
	}

	// お化けキノコの生成
	Matango[] createMatango(int num){
		Matango[] m = new Matango[num];
		int[] suffix = new int[num];
		Random r = new Random();
		for (int i = 0; i < m.length; i++) {
			int hp = r.nextInt(30) + 10;
			char ch = (char)  ('A' + i);
			m[i] = new Matango(hp, ch);
			System.out.println("お化けキノコ" + m[i].getSuffix() + "の登場。生命力HP:" + m[i].getHp());
		}
		return m;
	}

	// 魔法使いの生成
	Wizard createWizard(String name){
		Wizard w = new Wizard();
		w.setName(name);
		w.setHp(100);
		w.setMp(100);
		Wand wand = new Wand();
		wand.setName("魔法の杖");
		wand.setPower(1.5);
		w.setWand(wand);
		System.out.println("魔法使い" + w.getName() + "の登場。生命力HP:" + w.getHp() + "、" + w.getWand().getName() + "の魔法力は" + w.getWand().getPower());
		return w;
	}

	//戦闘メソッド
	void battle(Hero h, Matango[] m){
		System.out.println("\n" + h.getName() + "はお化けキノコ軍団を見つけた。");
		for (int i = 0; i < m.length; i++) {
			h.attack(m[i]);
			if (m[i].getHp() < 5) { 	m[i].run(); 	}
		}
		System.out.println(h.getName() + "の生命力HP:" + h.getHp() + "\n");
	}
}

次回は、『継承』あたりに突入です。