カプセル化
カプセル化はOOPのコア機能の一つです。このレッスンでは、オブジェクトデータを保護する方法を学びます。
カプセル化とは
カプセル化はデータ(属性)とそのデータを操作するメソッドを一緒にバンドルし、内部実装の詳細を隠します。
カプセル化の利点
| 利点 | 説明 |
|---|---|
| データ保護 | 外部からの直接アクセスと変更を防止 |
| 柔軟性 | setterにバリデーションロジックを追加可能 |
| 保守性 | 内部の変更が外部の呼び出しに影響しない |
アクセス修飾子
Javaには4つのアクセス修飾子があり、クラス、属性、メソッドの可視性を制御します。
| 修飾子 | 同じクラス | 同じパッケージ | サブクラス | 異なるパッケージ |
|---|---|---|---|---|
public |
✅ | ✅ | ✅ | ✅ |
protected |
✅ | ✅ | ✅ | ❌ |
| デフォルト(修飾子なし) | ✅ | ✅ | ❌ | ❌ |
private |
✅ | ❌ | ❌ | ❌ |
例:アクセス修飾子
JAVA
public class Person {
public String name; // パブリック、どこからでもアクセス可能
protected int age; // 保護付き、同じパッケージとサブクラスからアクセス可能
String address; // デフォルト、同じパッケージからアクセス可能
private double salary; // プライベート、このクラスからだけアクセス可能
}
💡 推奨: 属性には
privateを使用し、getter/setterメソッドでアクセスしてください。
getterとsetter
getter/setterが必要な理由
JAVA
public class Person {
private String name;
private int age;
// getter
public String getName() {
return name;
}
// setter
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
// setterにバリデーションロジックを追加
if (age >= 0 && age <= 150) {
this.age = age;
} else {
System.out.println("無効な年齢");
}
}
}
例:getter/setterの使用
JAVA
public class PersonTest {
public static void main(String[] args) {
Person p = new Person();
// setterで値を設定
p.setName("Alice");
p.setAge(25);
// getterで値を取得
System.out.println("名前: " + p.getName());
System.out.println("年齢: " + p.getAge());
// setterのバリデーション
p.setAge(-5); // 出力: 無効な年齢
}
}
JavaBean規約
JavaBeanは特定の規約に従う特別なタイプのJavaクラスです。
JavaBeanルール
| ルール | 説明 |
|---|---|
| プライベート属性 | privateで修飾 |
| getter/setterを提供 | getXxx()とsetXxx() |
| 引数なしコンストラクタ | 必須 |
| Serializableを実装 | オプション、シリアライズ用 |
例:標準的なJavaBean
JAVA
import java.io.Serializable;
public class User implements Serializable {
// プライベート属性
private String username;
private String password;
private int age;
// 引数なしコンストラクタ
public User() {}
// 引数付きコンストラクタ
public User(String username, String password, int age) {
this.username = username;
this.password = password;
this.age = age;
}
// getter/setter
public String getUsername() {
return username;
}
public void setUsername(String username) {
this.username = username;
}
public String getPassword() {
return password;
}
public void setPassword(String password) {
if (password.length() >= 6) {
this.password = password;
} else {
System.out.println("パスワードは6文字以上でなければなりません");
}
}
public int getAge() {
return age;
}
public void setAge(int age) {
if (age >= 0 && age <= 150) {
this.age = age;
} else {
System.out.println("無効な年齢");
}
}
@Override
public String toString() {
return "User{username='" + username + "', age=" + age + "}";
}
}
カプセル化の実践
例:銀行口座
JAVA
public class BankAccount {
private String accountId;
private double balance;
private String owner;
public BankAccount(String accountId, String owner) {
this.accountId = accountId;
this.owner = owner;
this.balance = 0;
}
// 入金
public void deposit(double amount) {
if (amount > 0) {
balance += amount;
System.out.println("入金: " + amount + ", 残高: " + balance);
} else {
System.out.println("入金額は0より大きくなければなりません");
}
}
// 出金
public void withdraw(double amount) {
if (amount > 0 && amount <= balance) {
balance -= amount;
System.out.println("出金: " + amount + ", 残高: " + balance);
} else if (amount > balance) {
System.out.println("残高不足");
} else {
System.out.println("出金額は0より大きくなければなりません");
}
}
// 残高確認
public double getBalance() {
return balance;
}
public void showInfo() {
System.out.println("口座: " + accountId + ", 所有者: " + owner + ", 残高: " + balance);
}
public static void main(String[] args) {
BankAccount account = new BankAccount("10001", "Alice");
account.showInfo(); // 口座: 10001, 所有者: Alice, 残高: 0.0
account.deposit(1000); // 入金: 1000.0, 残高: 1000.0
account.deposit(500); // 入金: 500.0, 残高: 1500.0
account.withdraw(200); // 出金: 200.0, 残高: 1300.0
account.withdraw(2000); // 残高不足
}
}
例:商品価格のバリデーション
JAVA
public class Product {
private String name;
private double price;
private int stock;
public Product(String name, double price, int stock) {
this.name = name;
setPrice(price);
setStock(stock);
}
public String getName() {
return name;
}
public double getPrice() {
return price;
}
public void setPrice(double price) {
if (price >= 0) {
this.price = price;
} else {
System.out.println("価格はマイナスにできません");
}
}
public int getStock() {
return stock;
}
public void setStock(int stock) {
if (stock >= 0) {
this.stock = stock;
} else {
System.out.println("在庫はマイナスにできません");
}
}
// 合計価格を計算
public double getTotalPrice(int quantity) {
if (quantity > 0 && quantity <= stock) {
return price * quantity;
} else {
System.out.println("無効な数量");
return 0;
}
}
@Override
public String toString() {
return name + " - $" + price + " (在庫: " + stock + ")";
}
}
getter/setterの命名規則
| 属性型 | getter | setter |
|---|---|---|
boolean married |
isMarried() |
setMarried() |
String name |
getName() |
setName() |
int age |
getAge() |
setAge() |
⚠️ 特殊ケース:
boolean型の場合、getterはisプレフィックスを使用し、getではありません。
❓ よくある質問
Q なぜpublic属性を使わないのですか?
A public属性はアクセス制御を提供しません。setterはデータの整合性を保護するためのバリデーションロジックを追加できます。
Q すべての属性にgetter/setterが必要ですか?
A いいえ。読み取り専用属性にはgetterだけが必要です。書き込み専用属性にはsetterだけが必要です。
Q コンストラクタとsetterの違いは何ですか?
A コンストラクタはオブジェクト作成時に呼び出され、setterはオブジェクト作成後に属性を変更します。
📖 まとめ
- カプセル化は内部実装を隠し、データを保護する
- 属性にはprivateを使用し、getter/setterでアクセス
- 4つのアクセス修飾子:public、protected、デフォルト、private
- JavaBean規約:プライベート属性、getter/setter、引数なしコンストラクタ
📝 演習
- Studentクラス: カプセル化された属性を持つStudentクラスを定義し、スコアを0-100の間でバリデーション
- Dateクラス: カプセル化された年/月/日を持つ日付クラスを定義し、日付のバリデーションを提供
- BankAccountの改良: パスワードバリデーションを追加し、出金時にパスワードを要求
次のレッスン
次のレッスンでは、継承を学びます — コード再利用のための重要なメカニズム。



