なぜ「オブジェクト指向」が必要になったのか
第5章までの知識で、計算や繰り返しは書けます。しかし業務システムは、数万行、数十万行のコードになります。そうなると「どこに何が書いてあるか」「この変数は誰が変えているのか」が分からなくなり、修正が怖くてできない状態になります。
オブジェクト指向は、この問題への答えとして広まった「コードの整理術」です。考え方の中心はひとつ、「関係するデータと処理を、ひとまとまりの『もの(オブジェクト)』として扱う」ことです。Javaは、この考え方を言語の土台に据えて設計されています。
クラスとインスタンス:ひな形と記入済みの書類
第3章で、クラスは「部品をまとめる箱」だと学びました。オブジェクト指向の視点では、クラスはひな形(設計図)です。そして、そのひな形から作られた個々の実体をインスタンス(またはオブジェクト)と呼びます。
Invoice請求書のひな形「請求番号・取引先・金額の欄がある」
「税込金額を計算できる」
String や BigDecimal もクラスで、"請求書" という文字列はStringクラスのインスタンスです。コードで見るとこうなります。少し長いですが、構造は第3章と同じ「クラスの箱の中にいろいろ入っている」だけです。
public class Invoice {
// --- フィールド:この「もの」が持っている情報 ---
private String number; // 請求番号
private String customer; // 取引先
private int amount; // 税抜金額
// --- コンストラクタ:インスタンスを作るときの初期化手順 ---
public Invoice(String number, String customer, int amount) {
this.number = number;
this.customer = customer;
this.amount = amount;
}
// --- メソッド:この「もの」ができること ---
public int amountWithTax() {
return amount + amount / 10; // 10%税込(簡略化)
}
public String getCustomer() {
return customer;
}
}
private String number;フィールド:インスタンスごとに持つデータ。「欄」にあたる。privateは「外から直接さわれない」の意味(後述)public Invoice(...) { ... }コンストラクタ:クラス名と同じ名前の特別なメソッド。newで作られるときに1回だけ動き、欄に初期値を入れるthis.number = number;「このインスタンスの number 欄に、受け取った number を入れる」。thisは「自分自身」public int amountWithTax()メソッド:この請求書ができること。自分のamountを使って計算する
そして、ひな形から実体を作るのが new です。
Invoice inv = new Invoice("1001", "A商事", 100000); // 記入済み請求書を1枚作る
System.out.println(inv.getCustomer()); // → A商事
System.out.println(inv.amountWithTax()); // → 110000
読んでみる:new Invoice(...) で請求書を1枚作り、inv という変数に入れた。あとは inv.〜() で、その請求書に「取引先を教えて」「税込金額を計算して」と頼んでいる。データ(金額)とそれを使う処理(税込計算)が、同じ箱の中にあるのがポイントです。
第3章の static の意味がここで分かる
static が付いたメソッドやフィールドは、「個々の記入済み書類」ではなく「ひな形そのもの」に属します。main メソッドに static が付いていたのは、「まだ1枚も書類を作っていない起動直後でも、ひな形から直接呼べるようにするため」です。
カプセル化:中身を勝手にさわらせない
先ほどのコードで、フィールドに private が付いていました。これは「このデータはクラスの外から直接書き換えられない」という制限です。外から金額を変えたければ、クラスが用意した公開メソッド(public)を通すしかありません。
これをカプセル化と呼びます。なぜそんな面倒なことをするかというと、「誰でも金額欄を直接書き換えられる」状態だと、どこで値が変わったのか追えなくなるからです。公開メソッドを通すようにしておけば、そこに「マイナスの金額は受け付けない」「承認済みなら変更不可」といったチェックを1か所で入れられます。
内部統制との相性
カプセル化は、業務でいう「勝手に台帳を直させない。必ず決められた手続きを通す」という統制の考え方そのものです。良く設計されたJavaのシステムでは、重要なデータの変更経路が限られているため、「この金額を変えられる処理はどこか」を特定しやすくなっています。監査やシステムレビューで「変更経路が限定されているか」を確認する際の、コード側の裏付けです。
継承:共通部分をまとめて、違いだけ書く
請求書・見積書・納品書は、どれも「番号・取引先・金額」を持ち、「税込金額を計算する」ことができます。違うのは、請求書には支払期限があり、見積書には有効期限がある、といった一部だけです。
こういうとき、共通部分を親クラスにまとめ、それぞれの文書は親を継承(extends)して「違う部分だけ」を書きます。
public class Invoice extends Document { // Document を継承する
private String dueDate; // 請求書だけが持つ項目
// 番号・取引先・金額・税込計算は親から引き継ぐので書かなくてよい
}
インターフェース:「できること」の取り決め
インターフェース(interface)は、「このメソッドを持っていること」だけを約束する取り決めです。中身は書かず、「何ができるか」の一覧だけを定めます。
// 取り決め:「印刷できるもの」は print() メソッドを持つこと
public interface Printable {
void print();
}
// 請求書は「印刷できるもの」の取り決めを守る(implements)
public class Invoice implements Printable {
public void print() {
System.out.println("請求書を印刷します");
}
}
インターフェースがあると、使う側は「相手が請求書か見積書か」を気にせず、「Printable なら print() を呼べる」とだけ考えればよくなります。これは業務でいう「書式は違っても、承認フローに乗せるには申請日と承認者欄が必須」といった共通ルールと同じ発想です。
継承(extends)
インターフェース(implements)
パッケージとインポート:クラスの整理棚
業務システムには何百、何千というクラスがあります。それをフォルダのように分類するのがパッケージです。第4章で import java.math.BigDecimal; という行がありましたが、あれは「java.math という棚にある BigDecimal クラスを使います」という宣言です。
package com.example.accounting.invoice; // このクラスの所属(棚の場所)
import java.math.BigDecimal; // 標準ライブラリから借りる
import java.time.LocalDate; // 日付クラスも借りる
public class Invoice { ... }
コードの冒頭に import がずらりと並んでいるのはこのためです。何を借りているかを見れば、「このクラスは日付計算と金額計算をしているらしい」と当たりが付きます。
この章のまとめ
- オブジェクト指向は「関係するデータと処理をひとまとまりとして扱う」整理術。大規模コードを管理するために生まれた
- クラスはひな形、インスタンス(オブジェクト)は
newで作った実体。クラスの中身はフィールド(情報)とメソッド(できること) - カプセル化(
private)で、データの変更経路を限定する。内部統制の考え方と同じ - 継承は「〜の一種」、インターフェースは「〜ができる」の取り決め。現代は継承を控えめに、インターフェースを好む
- パッケージはクラスの棚。
importを見れば何を使っているかの当たりが付く
確認クイズ
この章の内容を3問で確かめましょう。選ぶとすぐに解説が表示されます。
Q1. 「クラス」と「インスタンス」の関係のたとえとして最も適切なものはどれですか?
new で作られた具体的な実体です。Q2. フィールドに private を付ける(カプセル化する)主な目的はどれですか?
Q3. 「請求書クラスは Printable インターフェースを実装している」とはどういう意味ですか?