カプセル化とは?
クラスのフィールドやメソッドへのアクセスを制限する機能のこと。
フィールドやメソッドが想定しない用途で利用されるのを防ぐ役割がある。
//例:フィールドに文字列のname、整数のmoneyを持つUserクラス
public class User {
String name;
int money;
public User(String name, int money) {
this.name = name;
this.money = money;
}
}
Userクラスのインスタンスはname、moneyが自由に変えられてしまう。
public class Main {
public static void main(String[] args) {
User user = new User("鈴木太郎", 100);
System.out.println(user.name + "の所持金は" + user.money + "円");
//空白の名前にされてしまうことや
user.name = "";
//金額がマイナスにされてしまう可能性がある
user.money = -1000;
System.out.println(user.name + "の所持金は" + user.money + "円");
}
}
実行結果
鈴木太郎の所持金は100円
の所持金は-1000円
カプセル化の方法
カプセル化するには他のクラスから変更されたくないフィールドやメソッドのアクセス修飾子をprivate にする。
こうすることで他のクラスから変更されるのを防ぐことができる。
フィールドはprivate、メソッドはpublicにするのが定石。
//例:Userクラスをカプセル化
public class User {
private String name;
private int money;
public User(String name, int money) {
this.name = name;
this.money = money;
}
}
privateフィールドへのアクセス方法
フィールドをprivateにすると、他のクラスからはフィールドの値を取得、変更することができなくなる。
public class User {
//このままだとインスタンスのname, moneyが変更できない
private String name;
private int money;
public User(String name, int money) {
this.name = name;
this.money = money;
}
}
このようなときはフィールドの値を取得、変更するメソッドをクラスに定義することでクラス外から安全にフィールドの値を取得、変更することができる。
フィールドの値を取得するメソッドをゲッター、変更するメソッドをセッターと呼ぶ。
ゲッターの定義方法
ゲッターのメソッド名は「getフィールド名」のようにするのが一般的。
返り値としてフィールドの値を返す。
//例:Userクラスにname、moneyのゲッターを追加
public class User {
private String name;
private int money;
public User(String name, int money) {
this.name = name;
this.money = money;
}
//nameのゲッター
public String getName() {
return this.name;
}
//moneyのゲッター
public int getMoney() {
return this.money;
}
}
//ゲッターを使ってnameとmoneyを取得
public class Main {
public static void main(String[] args) {
User user = new User("鈴木太郎", 100);
System.out.println(user.getName() + "の所持金は" + user.getMoney() + "円");
}
}
実行結果
鈴木太郎の所持金は100円
セッターの定義方法
セッターのメソッド名は「setフィールド名」のようにするのが一般的。
引数として受け取った値をフィールドの値に代入する。
//例:Userクラスにname、moneyのセッターを追加
public class User {
private String name;
private int money;
public User(String name, int money) {
this.name = name;
this.money = money;
}
//nameのゲッター
public String getName() {
return this.name;
}
//nameのセッター
public void setName(String name) {
this.name = name;
}
//moneyのゲッター
public int getMoney() {
return this.money;
}
//moneyのセッター
public void setMoney(int money) {
this.money = money;
}
}
public class Main {
public static void main(String[] args) {
User user = new User("鈴木太郎", 100);
System.out.println(user.getName() + "の所持金は" + user.getMoney() + "円");
user.setName("佐藤太郎");
user.setMoney(500);
System.out.println(user.getName() + "の所持金は" + user.getMoney() + "円");
}
}
実行結果
鈴木太郎の所持金は100円
佐藤太郎の所持金は500円
ゲッター、セッターを使うメリット
ゲッターとセッターを使うとprivateにしたフィールドも取得、変更できるのでpublicと変わらないのでは?と思うかもしれないが、ゲッター、セッターを使うと以下のようなメリットがある。
1. フィールドを読み取り専用、書き込み専用にすることができる
フィールドに対してゲッターのみ定義し、セッターを定義しなければそのフィールドは読み取り専用になり、他のクラスから変更されるのを防ぎつつ、フィールドの値を使用できる。
また、フィールドに対してセッターのみ定義し、ゲッターを定義しなければそのフィールドは書き込み専用になる。(このような使い方はあまりない)
//例:Userクラスのmoneyのセッターを無効にしてmoneyを読み取り専用にする
public class User {
private String name;
private int money;
public User(String name, int money) {
this.name = name;
this.money = money;
}
//nameのゲッター
public String getName() {
return this.name;
}
//nameのセッター
public void setName(String name) {
this.name = name;
}
//moneyのゲッター
public int getMoney() {
return this.money;
}
//moneyのセッターが無ければ
//他のクラスからmoneyが変更されるのを防げる
//
// public void setMoney(int money) {
// this.money = money;
// }
}
2. フィールドの名称を変更しても他のクラスに影響しない
フィールドの名称を後から変更する際、ゲッター、セッターを使用していない場合はフィールドを使用しているすべてのクラスのコードを修正する必要がある。
しかし、ゲッター、セッターを使用している場合、ゲッター、セッターのメソッド内のコードを修正するだけで済む。
//例:UserクラスのnameをuserNameに変更してもメインメソッドは変更不要
public class Main {
public static void main(String[] args) {
User user = new User("鈴木太郎", 100);
System.out.println(user.getName() + "の所持金は" + user.getMoney() + "円");
user.setName("佐藤太郎");
System.out.println(user.getName() + "の所持金は" + user.getMoney() + "円");
}
}
public class User {
private String userName; //name -> userNameに変更
private int money;
public User(String userName, int money) {
this.userName = userName;
this.money = money;
}
//userNameのゲッター
public String getName() {
return this.userName;
}
//userNameのセッター
public void setName(String userName) {
this.userName = userName;
}
//メインメソッドにuserNameという変数が登場しないのでコード変更不要
public class Main {
public static void main(String[] args) {
User user = new User("鈴木太郎", 100);
System.out.println(user.getName() + "の所持金は" + user.getMoney() + "円");
user.setName("佐藤太郎");
System.out.println(user.getName() + "の所持金は" + user.getMoney() + "円");
}
}
実行結果
鈴木太郎の所持金は100円
佐藤太郎の所持金は100円
3. フィールドの値を変更する前に値を検査できる
セッターのメソッド内に、引数の値を検査するコードを追加することでフィールドの値が想定しない値に変更されるのを防ぐことができる。
例:UserクラスのuserNameが変更される前に適切な文字数か検査
//userNameのセッター
//引数の文字列が3文字以上10文字以内ならuserNameを変更
public void setName(String userName) {
if (userName.length() < 3) {
System.out.println("名前が短過ぎます");
} else if (userName.length() > 11) {
System.out.println("名前が長過ぎます");
} else {
this.userName = userName;
System.out.println("名前を「" + this.userName + "」に変更しました");
}
}
public class Main {
public static void main(String[] args) {
User user = new User("鈴木太郎", 100);
System.out.println(user.getName() + "の所持金は" + user.getMoney() + "円");
user.setName("鈴木");
user.setName("俺の名前は鈴木太郎!!");
user.setName("佐藤太郎");
System.out.println(user.getName() + "の所持金は" + user.getMoney() + "円");
}
}
実行結果
鈴木太郎の所持金は100円
名前が短過ぎます
名前が長過ぎます
名前を「佐藤太郎」に変更しました
佐藤太郎の所持金は100円
まとめ
- カプセル化とはクラスのフィールドやインスタンスの使用を制限する機能
- 外部のクラスから想定外のフィールドの書き込みやメソッドの使用を防ぐことができる
- フィールドはprivate、メソッドはpublicにするのが定石
- フィールドをprivateにした際はゲッター、セッターを使用してフィールドの値を扱う