目次
要約
- クラスは**データ(フィールド)と振る舞い(メソッド)**の集合です。
- カプセル化(
private
+公開メソッド)、不変設計、継承/抽象化/インターフェースを正しく使い分けます。 equals/hashCode/toString
の契約、ジェネリクス、可変長引数なども実務で必須です。
1. クラスの基本構成
public class Account {
// フィールド(状態)
private String id;
private int balance;
// コンストラクタ
public Account(String id, int initial) {
this.id = id;
this.balance = initial;
}
// メソッド(振る舞い)
public void deposit(int amount) {
if (amount <= 0) throw new IllegalArgumentException("amount > 0");
this.balance += amount;
}
public int getBalance() { return balance; }
}
- フィールドは原則
private
、外部公開はメソッド経由にします。 this
はインスタンス自身を指します。
2. アクセス修飾子とカプセル化
public
:どこからでもアクセス- (指定なし):同一パッケージ内
protected
:同一パッケージ+サブクラスprivate
:同一クラス内のみ
方針:最小権限の原則。まずprivate
、必要時のみ公開します。
3. コンストラクタと不変オブジェクト
public class User {
private final String id; // 再代入不可
private final String name;
public User(String id, String name) {
if (id == null || name == null) throw new IllegalArgumentException();
this.id = id;
this.name = name;
}
public String id() { return id; }
public String name() { return name; }
}
- フィールドを
final
にし**不変(immutable)**にすると安全・シンプル。 - 代替として**
record
(Java 16+)**も有効:record User(String id, String name){}
4. メソッドの基本:インスタンス/static
/オーバーロード/オーバーライド
class MathUtil {
// staticメソッド:状態を持たない汎用処理
public static int clamp(int v, int min, int max) {
return Math.max(min, Math.min(max, v));
}
}
class Greeter {
private final String prefix;
public Greeter(String prefix) { this.prefix = prefix; }
// オーバーロード(同名・引数違い)
public String greet(String name) { return prefix + ", " + name; }
public String greet(String first, String last) { return greet(first + " " + last); }
@Override // オーバーライド(継承時に振る舞いを差し替え)
public String toString() { return "Greeter(" + prefix + ")"; }
}
static
はインスタンスに依存しないユーティリティへ限定。- オーバーロイドは引数の型/個数で区別、戻り値だけでは区別不可。
5. 継承・抽象クラス・インターフェース
abstract class Shape {
// 具体実装はサブクラスへ委譲
public abstract double area();
}
class Circle extends Shape {
private final double r;
public Circle(double r){ this.r = r; }
@Override public double area(){ return Math.PI * r * r; }
}
interface Printable {
String print(); // 抽象メソッド
default String header(){ return "[PRINT]"; } // 既定実装
}
- 抽象クラス:共通実装+抽象メソッドを混在。
- インターフェース:契約(API)の提示。多重実装可。
default
で共通実装も可。 - is-aでない継承は避け、委譲を優先します。
6. equals
/ hashCode
/ toString
と Comparable
import java.util.Objects;
public final class Product implements Comparable<Product> {
private final String code;
private final int price;
public Product(String code, int price) {
this.code = Objects.requireNonNull(code);
this.price = price;
}
@Override public boolean equals(Object o) {
if (this == o) return true;
if (!(o instanceof Product p)) return false;
return price == p.price && code.equals(p.code);
}
@Override public int hashCode() { return Objects.hash(code, price); }
@Override public String toString() { return "Product[" + code + "," + price + "]"; }
@Override public int compareTo(Product o) {
int c = this.code.compareTo(o.code);
return (c != 0) ? c : Integer.compare(this.price, o.price);
}
}
equals
を実装したら、hashCode
も整合させます(同値→同ハッシュ)。- ソートが必要なら
Comparable
またはComparator
を使用します。
7. ネスト/内部クラス
public class Outer {
private int base = 10;
// staticネスト:Outerと独立(Outerのインスタンス不要)
public static class Nested {
public int twice(int x){ return x * 2; }
}
// 非static内部クラス:Outerのインスタンスに結びつく
public class Inner {
public int addBase(int x){ return x + base; } // Outer.this.baseへアクセス可
}
}
- 依存がないなら**
static
ネスト**。依存が強いときのみ内部クラスを使います。
8. ジェネリクス(型パラメータ)
class Box<T> {
private final T value;
public Box(T value){ this.value = value; }
public T get(){ return value; }
}
// メソッドのジェネリクス
class Util {
public static <T> T first(java.util.List<T> list){ return list.get(0); }
}
- ジェネリクスで型安全とキャスト削減を実現します。
- ワイルドカード概念:読み取り中心は
? extends T
、書き込み中心は? super T
。
9. 可変長引数(varargs)
static int sum(int... xs) { // 0個以上のintを受け取る
int s = 0;
for (int x : xs) s += x;
return s;
}
- 呼び出し側は
sum(1,2,3)
や配列sum(arr)
のどちらも可。 - 実引数が多すぎる設計は避け、ビルダーや値オブジェクトを検討します。
10. パッケージと可視性・import
- 先頭に
package com.example.app;
を宣言(1ファイル=1公開クラスが原則)。 - 別パッケージの型は
import
で参照。 - 大規模では**モジュール(module-info.java)**で依存を明示します。
11. 1ファイル実用サンプル(要点総合)
import java.util.Objects;
import java.util.List;
public class ClassMethodDemo {
// --- 不変値オブジェクト ---
public static final class Point {
private final int x, y;
public Point(int x, int y){ this.x = x; this.y = y; }
public int x(){ return x; }
public int y(){ return y; }
@Override public boolean equals(Object o){
if (this == o) return true;
if (!(o instanceof Point p)) return false;
return x == p.x && y == p.y;
}
@Override public int hashCode(){ return Objects.hash(x, y); }
@Override public String toString(){ return "Point(" + x + "," + y + ")"; }
}
// --- 抽象/具象 ---
static abstract class Shape { public abstract double area(); }
static final class Rect extends Shape implements Printable {
private final int w, h;
public Rect(int w, int h){
if (w <= 0 || h <= 0) throw new IllegalArgumentException();
this.w = w; this.h = h;
}
@Override public double area(){ return (double) w * h; }
@Override public String print(){ return header() + " Rect " + w + "x" + h + " area=" + area(); }
}
static final class Circle extends Shape implements Printable {
private final int r;
public Circle(int r){ if (r <= 0) throw new IllegalArgumentException(); this.r = r; }
@Override public double area(){ return Math.PI * r * r; }
@Override public String print(){ return header() + " Circle r=" + r + " area=" + area(); }
}
interface Printable {
String print();
default String header(){ return "[PRINT]"; }
}
// --- ユーティリティ(static) ---
static class Mathx {
static int clamp(int v, int min, int max){ return Math.max(min, Math.min(max, v)); }
static <T> T first(List<T> list){ return list.get(0); } // ジェネリックメソッド
}
// --- varargs ---
static int sum(int... xs){ int s=0; for(int x:xs) s+=x; return s; }
public static void main(String[] args) {
Point p = new Point(3, 5);
Rect r = new Rect(4, 6);
Circle c = new Circle(5);
System.out.println(p); // Point(3,5)
System.out.println(r.print()); // [PRINT] Rect 4x6 area=24.0
System.out.println(c.print()); // [PRINT] Circle r=5 area=78.5398...
System.out.println(Mathx.clamp(120, 0, 100)); // 100
System.out.println(sum(1,2,3,4)); // 10
List<String> names = List.of("Alice","Bob");
System.out.println(Mathx.first(names)); // Alice
}
}
ベストプラクティス要点
- 最小権限(
private
中心)と不変設計を基本にします。 - ビジネスルールはメソッドへ集約し、データと振る舞いを同居させます。
- 継承は慎重に、まずインターフェース+委譲を検討します。
equals/hashCode
は同時実装し、toString
で診断容易性を高めます。- ユーティリティは
static
に限定、状態を持たせない方針で統一します。