コンテンツにスキップ

9-2. クラス・インターフェース・継承

  • クラスの定義方法(フィールド・コンストラクタ・メソッド)を習得する
  • アクセス修飾子によるカプセル化の実装を理解する
  • 継承(extends)とオーバーライドの仕組みを学ぶ
  • インターフェース(implements)と抽象クラスの使い分けを理解する
  • record によるデータクラスの簡潔な宣言方法を知る

第4章でオブジェクト指向の概念(カプセル化・継承・ポリモーフィズム)を学んだ。ここではJavaでの具体的な実装方法を習得する。

// クラスの定義
public class Book {
// フィールド(インスタンス変数)
private String title;
private String author;
private int price;
// コンストラクタ
public Book(String title, String author, int price) {
this.title = title;
this.author = author;
this.price = price;
}
// メソッド
public String getTitle() { return title; }
public String getAuthor() { return author; }
public int getPrice() { return price; }
public void setPrice(int price) {
if (price < 0) throw new IllegalArgumentException("価格は0以上である必要があります");
this.price = price;
}
public String describe() {
return String.format("『%s』 著者: %s, 価格: %d円", title, author, price);
}
// toString のオーバーライド
@Override
public String toString() {
return describe();
}
}
// オブジェクトの生成と使用
Book book = new Book("Java入門", "田中", 2800);
System.out.println(book.getTitle()); // "Java入門"
System.out.println(book.describe()); // 『Java入門』 著者: 田中, 価格: 2800円
System.out.println(book); // toString() が呼ばれる
アクセス修飾子と可視性
┌─────────────┬─────────┬─────────┬───────────┬────────────┐
│ 修飾子 │ 同クラス │ 同パッケ │ サブクラス │ 全てのクラス│
│ │ │ ージ │ │ │
├─────────────┼─────────┼─────────┼───────────┼────────────┤
│ private │ ✅ │ ❌ │ ❌ │ ❌ │
│ (なし) │ ✅ │ ✅ │ ❌ │ ❌ │
│ protected │ ✅ │ ✅ │ ✅ │ ❌ │
│ public │ ✅ │ ✅ │ ✅ │ ✅ │
└─────────────┴─────────┴─────────┴───────────┴────────────┘

フィールドは private、外部アクセスが必要なものはメソッドで公開するのが基本(カプセル化)。

public class Counter {
private static int count = 0; // クラス全体で共有
private int id;
public Counter() {
count++;
this.id = count;
}
public static int getCount() { return count; } // インスタンスなしで呼べる
public int getId() { return id; }
}
Counter c1 = new Counter();
Counter c2 = new Counter();
System.out.println(Counter.getCount()); // 2(static メソッドはクラス名で呼ぶ)
System.out.println(c1.getId()); // 1
System.out.println(c2.getId()); // 2

データを保持するだけのクラスは record で簡潔に書ける。

// 通常のクラス(コンストラクタ・getter・equals・hashCode・toString が冗長)
// → record で自動生成
public record Point(int x, int y) {}
Point p = new Point(3, 4);
System.out.println(p.x()); // 3
System.out.println(p.y()); // 4
System.out.println(p); // Point[x=3, y=4]
p.equals(new Point(3, 4)); // true
// 基底クラス(スーパークラス)
public class Animal {
protected String name;
public Animal(String name) {
this.name = name;
}
public void speak() {
System.out.println(name + " が何か言った");
}
public String getName() { return name; }
}
// 派生クラス(サブクラス)
public class Dog extends Animal {
private String breed;
public Dog(String name, String breed) {
super(name); // 親のコンストラクタを呼ぶ
this.breed = breed;
}
@Override // オーバーライドを明示(付けないとコンパイラが警告を出せない)
public void speak() {
System.out.println(name + " がワンワンと吠えた");
}
public String getBreed() { return breed; }
}
// 使用例
Dog dog = new Dog("ポチ", "柴犬");
dog.speak(); // "ポチ がワンワンと吠えた"
dog.getName(); // "ポチ"(親のメソッドを継承)
Animal animal = dog; // アップキャスト(Animalとして扱える)
animal.speak(); // "ポチ がワンワンと吠えた"(実際のオブジェクトのメソッドが呼ばれる)
Animal animal = new Dog("ポチ", "柴犬");
if (animal instanceof Dog dog) { // Java 16以降のパターンマッチング
System.out.println(dog.getBreed()); // "柴犬"
}
// 古い書き方
if (animal instanceof Dog) {
Dog d = (Dog) animal;
System.out.println(d.getBreed());
}
// final クラス:継承禁止
public final class Immutable { ... }
// final メソッド:オーバーライド禁止
public class Base {
public final void cannotOverride() { ... }
}
// String クラスは final → 継承できない

インターフェースは「何ができるか」という契約を定義する。Javaでは複数インターフェースを実装できる(クラスは単一継承のみ)。

// インターフェース定義
public interface Printable {
void print(); // 抽象メソッド(実装なし)
// デフォルトメソッド(Java 8以降):実装を持てる
default void printWithBorder() {
System.out.println("---");
print();
System.out.println("---");
}
}
public interface Saveable {
void save(String filename);
}
// インターフェースの実装
public class Document implements Printable, Saveable {
private String content;
public Document(String content) {
this.content = content;
}
@Override
public void print() {
System.out.println(content);
}
@Override
public void save(String filename) {
System.out.println(filename + " に保存しました");
}
}
// 使用例
Document doc = new Document("Hello, Java!");
doc.print(); // "Hello, Java!"
doc.printWithBorder(); // "---\nHello, Java!\n---"
doc.save("test.txt"); // "test.txt に保存しました"
// インターフェース型として扱える(ポリモーフィズム)
Printable p = new Document("Hi");
p.print();
使い分けの基準
┌─────────────┬──────────────────────────────────────────┐
│ 継承 │ 「AはBの一種である」(is-a) 関係 │
│ │ 例:Dog extends Animal │
├─────────────┼──────────────────────────────────────────┤
│ インター │ 「Aはこの機能を持つ」(can-do) 関係 │
│ フェース │ 例:Document implements Printable │
└─────────────┴──────────────────────────────────────────┘

継承を前提とした「一部だけ実装したクラス」。インターフェースとクラスの中間的な存在。

public abstract class Shape {
protected String color;
public Shape(String color) {
this.color = color;
}
// 抽象メソッド:サブクラスで必ず実装する
public abstract double area();
// 通常のメソッド:サブクラスに共通の実装を提供
public void describe() {
System.out.printf("色: %s, 面積: %.2f%n", color, area());
}
}
public class Circle extends Shape {
private double radius;
public Circle(String color, double radius) {
super(color);
this.radius = radius;
}
@Override
public double area() {
return Math.PI * radius * radius;
}
}
public class Rectangle extends Shape {
private double width, height;
public Rectangle(String color, double width, double height) {
super(color);
this.width = width;
this.height = height;
}
@Override
public double area() {
return width * height;
}
}
// 使用例
Shape[] shapes = {
new Circle("", 5.0),
new Rectangle("", 4.0, 6.0),
};
for (Shape s : shapes) {
s.describe(); // ポリモーフィズム:実際のオブジェクトの area() が呼ばれる
}
// 色: 赤, 面積: 78.54
// 色: 青, 面積: 24.00
概念キーワード説明
クラスclassフィールドとメソッドを持つ設計図
カプセル化private + getter/setter内部状態を隠蔽し、メソッドで公開
継承extends親クラスの機能を引き継ぐ(単一継承)
オーバーライド@Override親クラスのメソッドを再定義する
インターフェースimplements「できること」の契約(複数実装可)
抽象クラスabstract一部だけ実装した、継承前提のクラス
recordrecordデータ保持クラスの簡潔な宣言