0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

【Java研修】新入社員向けコーディング問題⑧ 継承とインターフェース 編

0
Posted at

はじめに

Java研修コーディング問題集の第8回は 継承とインターフェース です。

継承はオブジェクト指向の柱の一つで、既存のクラスを拡張して新しいクラスを作る仕組みです。インターフェースと合わせて、ポリモーフィズム(多態性)の概念を理解しましょう。

難易度の見方

マーク 難易度 目安
基本 研修1週目レベル
⭐⭐ 応用 少し考える必要あり
⭐⭐⭐ チャレンジ 複数の知識を組み合わせる

問題1:基本的な継承 ⭐

問題

Animal(動物)クラスを親クラスとして、Dog(犬)と Cat(猫)の子クラスを作成してください。

  • Animalname フィールドと eat() メソッド
  • Dogbark() メソッドを追加
  • Catmeow() メソッドを追加

期待する出力

ポチが食事中...
ワンワン!
タマが食事中...
ニャー!
模範解答
class Animal {
    String name;

    Animal(String name) {
        this.name = name;
    }

    void eat() {
        System.out.println(name + "が食事中...");
    }
}

class Dog extends Animal {
    Dog(String name) {
        super(name);
    }

    void bark() {
        System.out.println("ワンワン!");
    }
}

class Cat extends Animal {
    Cat(String name) {
        super(name);
    }

    void meow() {
        System.out.println("ニャー!");
    }
}

public class Exercise01 {
    public static void main(String[] args) {
        Dog dog = new Dog("ポチ");
        dog.eat();
        dog.bark();

        Cat cat = new Cat("タマ");
        cat.eat();
        cat.meow();
    }
}

ポイント: extends で継承を宣言します。子クラスは親クラスのフィールドとメソッドを引き継ぎます。super(name) で親クラスのコンストラクタを呼び出しています。


問題2:メソッドのオーバーライド ⭐

問題

Animal クラスに sound() メソッドを追加し、各子クラスでオーバーライドしてください。

期待する出力

ポチの鳴き声: ワンワン!
タマの鳴き声: ニャー!
ピーちゃんの鳴き声: ピヨピヨ!
模範解答
class Animal {
    String name;

    Animal(String name) {
        this.name = name;
    }

    String sound() {
        return "...";
    }
}

class Dog extends Animal {
    Dog(String name) { super(name); }

    @Override
    String sound() {
        return "ワンワン!";
    }
}

class Cat extends Animal {
    Cat(String name) { super(name); }

    @Override
    String sound() {
        return "ニャー!";
    }
}

class Bird extends Animal {
    Bird(String name) { super(name); }

    @Override
    String sound() {
        return "ピヨピヨ!";
    }
}

public class Exercise02 {
    public static void main(String[] args) {
        Animal[] animals = {
            new Dog("ポチ"),
            new Cat("タマ"),
            new Bird("ピーちゃん")
        };

        for (Animal a : animals) {
            System.out.println(a.name + "の鳴き声: " + a.sound());
        }
    }
}

ポイント: オーバーライドは親クラスのメソッドを子クラスで再定義することです。Animal 型の変数に DogCat のインスタンスを入れても、実際のオブジェクトのメソッドが呼ばれます。これが ポリモーフィズム(多態性) です。


問題3:super キーワード ⭐⭐

問題

Vehicle(乗り物)クラスとその子クラス ElectricCar(電気自動車)を作成してください。ElectricCarVehiclegetInfo() を拡張して、バッテリー情報も含めてください。

期待する出力

--- 普通の車 ---
車名: カローラ、速度: 180km/h

--- 電気自動車 ---
車名: テスラ、速度: 250km/h
バッテリー: 75kWh、航続距離: 400km
模範解答
class Vehicle {
    protected String name;
    protected int maxSpeed;

    Vehicle(String name, int maxSpeed) {
        this.name = name;
        this.maxSpeed = maxSpeed;
    }

    void showInfo() {
        System.out.println("車名: " + name + "、速度: " + maxSpeed + "km/h");
    }
}

class ElectricCar extends Vehicle {
    private int batteryCapacity;
    private int range;

    ElectricCar(String name, int maxSpeed, int batteryCapacity, int range) {
        super(name, maxSpeed);
        this.batteryCapacity = batteryCapacity;
        this.range = range;
    }

    @Override
    void showInfo() {
        super.showInfo();
        System.out.println("バッテリー: " + batteryCapacity + "kWh、航続距離: " + range + "km");
    }
}

public class Exercise03 {
    public static void main(String[] args) {
        Vehicle car = new Vehicle("カローラ", 180);
        ElectricCar ev = new ElectricCar("テスラ", 250, 75, 400);

        System.out.println("--- 普通の車 ---");
        car.showInfo();
        System.out.println("\n--- 電気自動車 ---");
        ev.showInfo();
    }
}

ポイント: super.showInfo() で親クラスのメソッドを呼び出し、その後に追加の情報を出力しています。protected は同じパッケージと子クラスからアクセス可能なアクセス修飾子です。


問題4:抽象クラス ⭐⭐

問題

Shape(図形)抽象クラスを作成し、Circle(円)と Rectangle(長方形)で面積と周の長さを計算してください。

  • Shape(抽象クラス):abstract double area(), abstract double perimeter()
  • Circle:半径
  • Rectangle:幅と高さ

期待する出力

=== 図形の計算 ===
円(半径5.0)
  面積: 78.54
  周の長さ: 31.42

長方形(幅4.0×高さ6.0)
  面積: 24.00
  周の長さ: 20.00
模範解答
abstract class Shape {
    abstract double area();
    abstract double perimeter();
    abstract String describe();
}

class Circle extends Shape {
    private double radius;

    Circle(double radius) {
        this.radius = radius;
    }

    @Override
    double area() {
        return Math.PI * radius * radius;
    }

    @Override
    double perimeter() {
        return 2 * Math.PI * radius;
    }

    @Override
    String describe() {
        return "円(半径" + radius + ")";
    }
}

class Rectangle extends Shape {
    private double width;
    private double height;

    Rectangle(double width, double height) {
        this.width = width;
        this.height = height;
    }

    @Override
    double area() {
        return width * height;
    }

    @Override
    double perimeter() {
        return 2 * (width + height);
    }

    @Override
    String describe() {
        return "長方形(幅" + width + "×高さ" + height + ")";
    }
}

public class Exercise04 {
    public static void main(String[] args) {
        Shape[] shapes = {
            new Circle(5.0),
            new Rectangle(4.0, 6.0)
        };

        System.out.println("=== 図形の計算 ===");
        for (Shape s : shapes) {
            System.out.println(s.describe());
            System.out.printf("  面積: %.2f%n", s.area());
            System.out.printf("  周の長さ: %.2f%n", s.perimeter());
            System.out.println();
        }
    }
}

ポイント: 抽象クラス はインスタンス化できず、子クラスに実装を強制します。abstract メソッドは宣言のみで、実装は子クラスで行います。共通の「型」を定義したいが、実装は異なる場合に使います。


問題5:インターフェース ⭐⭐

問題

Printable(印刷可能)インターフェースを定義し、Report(レポート)と Invoice(請求書)で実装してください。

interface Printable {
    void print();
    int getPageCount();
}

期待する出力

=== 印刷ジョブ ===
--- レポート ---
[レポート] タイトル: 月次報告書
ページ数: 5ページ

--- 請求書 ---
[請求書] 請求先: 株式会社ABC
金額: 150000円
ページ数: 2ページ

合計ページ数: 7ページ
模範解答
interface Printable {
    void print();
    int getPageCount();
}

class Report implements Printable {
    private String title;
    private int pages;

    Report(String title, int pages) {
        this.title = title;
        this.pages = pages;
    }

    @Override
    public void print() {
        System.out.println("[レポート] タイトル: " + title);
    }

    @Override
    public int getPageCount() {
        return pages;
    }
}

class Invoice implements Printable {
    private String clientName;
    private int amount;
    private int pages;

    Invoice(String clientName, int amount, int pages) {
        this.clientName = clientName;
        this.amount = amount;
        this.pages = pages;
    }

    @Override
    public void print() {
        System.out.println("[請求書] 請求先: " + clientName);
        System.out.println("金額: " + amount + "円");
    }

    @Override
    public int getPageCount() {
        return pages;
    }
}

public class Exercise05 {
    public static void main(String[] args) {
        Printable[] documents = {
            new Report("月次報告書", 5),
            new Invoice("株式会社ABC", 150000, 2)
        };

        String[] labels = {"レポート", "請求書"};

        System.out.println("=== 印刷ジョブ ===");

        int totalPages = 0;
        for (int i = 0; i < documents.length; i++) {
            System.out.println("--- " + labels[i] + " ---");
            documents[i].print();
            System.out.println("ページ数: " + documents[i].getPageCount() + "ページ");
            totalPages += documents[i].getPageCount();
            System.out.println();
        }

        System.out.println("合計ページ数: " + totalPages + "ページ");
    }
}

ポイント: インターフェース は「何ができるか」を定義する契約です。implements で宣言し、すべてのメソッドを実装します。異なるクラスでも同じインターフェースを実装すれば、統一的に扱えます(ポリモーフィズム)。


問題6:複数インターフェースの実装 ⭐⭐

問題

以下の2つのインターフェースを定義し、SmartPhone クラスで両方を実装してください。

interface Callable {
    void call(String number);
}

interface Photographable {
    void takePhoto();
    int getPhotoCount();
}

期待する出力

=== スマートフォン: iPhone ===
090-1234-5678 に電話中...
写真を撮影しました(合計: 1枚)
写真を撮影しました(合計: 2枚)
080-9876-5432 に電話中...
写真を撮影しました(合計: 3枚)
保存されている写真: 3枚
模範解答
interface Callable {
    void call(String number);
}

interface Photographable {
    void takePhoto();
    int getPhotoCount();
}

class SmartPhone implements Callable, Photographable {
    private String modelName;
    private int photoCount;

    SmartPhone(String modelName) {
        this.modelName = modelName;
        this.photoCount = 0;
    }

    @Override
    public void call(String number) {
        System.out.println(number + " に電話中...");
    }

    @Override
    public void takePhoto() {
        photoCount++;
        System.out.println("写真を撮影しました(合計: " + photoCount + "枚)");
    }

    @Override
    public int getPhotoCount() {
        return photoCount;
    }

    String getModelName() {
        return modelName;
    }
}

public class Exercise06 {
    public static void main(String[] args) {
        SmartPhone phone = new SmartPhone("iPhone");

        System.out.println("=== スマートフォン: " + phone.getModelName() + " ===");
        phone.call("090-1234-5678");
        phone.takePhoto();
        phone.takePhoto();
        phone.call("080-9876-5432");
        phone.takePhoto();
        System.out.println("保存されている写真: " + phone.getPhotoCount() + "枚");
    }
}

ポイント: Javaのクラスは複数のインターフェースを実装できます(多重継承は不可)。カンマ区切りで implements Callable, Photographable と書きます。1つのクラスが複数の「役割」を持てるのがインターフェースの強みです。


問題7:instanceof と型キャスト ⭐⭐

問題

Animal 型の配列に Dog, Cat, Bird のインスタンスを入れ、instanceof を使って型を判定し、型に応じた処理を行ってください。

期待する出力

=== 動物たちの特技 ===
ポチは犬です → お手!
タマは猫です → 毛づくろい中...
ピーちゃんは鳥です → 飛行中!
ハチは犬です → お手!
ミケは猫です → 毛づくろい中...
模範解答
class Animal {
    String name;
    Animal(String name) { this.name = name; }
}

class Dog extends Animal {
    Dog(String name) { super(name); }
    void shake() { System.out.println("お手!"); }
}

class Cat extends Animal {
    Cat(String name) { super(name); }
    void groom() { System.out.println("毛づくろい中..."); }
}

class Bird extends Animal {
    Bird(String name) { super(name); }
    void fly() { System.out.println("飛行中!"); }
}

public class Exercise07 {
    public static void main(String[] args) {
        Animal[] animals = {
            new Dog("ポチ"),
            new Cat("タマ"),
            new Bird("ピーちゃん"),
            new Dog("ハチ"),
            new Cat("ミケ")
        };

        System.out.println("=== 動物たちの特技 ===");

        for (Animal a : animals) {
            if (a instanceof Dog) {
                System.out.print(a.name + "は犬です → ");
                ((Dog) a).shake();
            } else if (a instanceof Cat) {
                System.out.print(a.name + "は猫です → ");
                ((Cat) a).groom();
            } else if (a instanceof Bird) {
                System.out.print(a.name + "は鳥です → ");
                ((Bird) a).fly();
            }
        }
    }
}

ポイント: instanceof でオブジェクトの実際の型を判定できます。(Dog) a のようにキャストすると、子クラス固有のメソッドを呼べます。Java 16以降では if (a instanceof Dog dog) とパターンマッチングが使えます。


問題8:デフォルトメソッドと関数型インターフェース ⭐⭐⭐

問題

Logger インターフェースにデフォルトメソッドを定義し、異なるログ出力先を実装してください。

interface Logger {
    void log(String message);

    default void info(String message) {
        log("[INFO] " + message);
    }
    default void warn(String message) {
        log("[WARN] " + message);
    }
    default void error(String message) {
        log("[ERROR] " + message);
    }
}

期待する出力

=== コンソールロガー ===
[INFO] アプリケーション起動
[WARN] メモリ使用率が80%を超えました
[ERROR] データベース接続エラー

=== ファイルロガー(シミュレーション)===
[FILE] [INFO] バッチ処理開始
[FILE] [ERROR] ファイルが見つかりません
模範解答
interface Logger {
    void log(String message);

    default void info(String message) {
        log("[INFO] " + message);
    }

    default void warn(String message) {
        log("[WARN] " + message);
    }

    default void error(String message) {
        log("[ERROR] " + message);
    }
}

class ConsoleLogger implements Logger {
    @Override
    public void log(String message) {
        System.out.println(message);
    }
}

class FileLogger implements Logger {
    @Override
    public void log(String message) {
        System.out.println("[FILE] " + message);
    }
}

public class Exercise08 {
    public static void main(String[] args) {
        System.out.println("=== コンソールロガー ===");
        Logger console = new ConsoleLogger();
        console.info("アプリケーション起動");
        console.warn("メモリ使用率が80%を超えました");
        console.error("データベース接続エラー");

        System.out.println("\n=== ファイルロガー(シミュレーション)===");
        Logger file = new FileLogger();
        file.info("バッチ処理開始");
        file.error("ファイルが見つかりません");
    }
}

ポイント: デフォルトメソッド(Java 8以降)により、インターフェースにも実装を持たせることができます。共通の振る舞いはデフォルトメソッドで提供し、各クラスは log() の実装だけを変えれば良いため、コードの重複を防げます。


問題9:テンプレートメソッドパターン ⭐⭐⭐

問題

データ処理の流れ(読み込み→変換→出力)を抽象クラスで定義し、CSV処理とJSON処理で具体的な実装を行ってください。

期待する出力

=== CSV処理 ===
[1] CSVファイルを読み込み中...
[2] CSVデータを変換中...(3件のレコード)
[3] CSVデータを出力中...
処理完了!(3ステップ)

=== JSON処理 ===
[1] JSONファイルを読み込み中...
[2] JSONデータを変換中...(パース実行)
[3] JSONデータを出力中...
処理完了!(3ステップ)
模範解答
abstract class DataProcessor {
    // テンプレートメソッド
    final void process() {
        System.out.println("[1] " + readData());
        System.out.println("[2] " + transformData());
        System.out.println("[3] " + writeData());
        System.out.println("処理完了!(3ステップ)");
    }

    abstract String readData();
    abstract String transformData();
    abstract String writeData();
}

class CsvProcessor extends DataProcessor {
    @Override
    String readData() {
        return "CSVファイルを読み込み中...";
    }

    @Override
    String transformData() {
        return "CSVデータを変換中...(3件のレコード)";
    }

    @Override
    String writeData() {
        return "CSVデータを出力中...";
    }
}

class JsonProcessor extends DataProcessor {
    @Override
    String readData() {
        return "JSONファイルを読み込み中...";
    }

    @Override
    String transformData() {
        return "JSONデータを変換中...(パース実行)";
    }

    @Override
    String writeData() {
        return "JSONデータを出力中...";
    }
}

public class Exercise09 {
    public static void main(String[] args) {
        System.out.println("=== CSV処理 ===");
        DataProcessor csv = new CsvProcessor();
        csv.process();

        System.out.println("\n=== JSON処理 ===");
        DataProcessor json = new JsonProcessor();
        json.process();
    }
}

ポイント: テンプレートメソッドパターンはデザインパターンの一つで、処理の骨組みを親クラスで定義し、詳細を子クラスに委ねます。final を付けることで、処理の流れ(順番)を子クラスが変更できないようにしています。


問題10:社員管理システム(継承の総合問題)⭐⭐⭐

問題

以下のクラス構成で社員管理システムを作成してください。

  • Employee(抽象クラス):名前、社員番号、基本給
    • abstract int calculateSalary():給与計算
  • FullTimeEmployee:正社員(基本給 + ボーナス倍率)
  • PartTimeEmployee:パート(時給 × 勤務時間)
  • Manager:管理職(正社員を継承 + 管理手当)
  • Payable インターフェース:void showPaySlip()

期待する出力

=== 給与明細一覧 ===

[正社員] 田中太郎(E001)
  基本給: 300000円
  ボーナス: 600000円
  年収: 4200000円

[パート] 鈴木花子(E002)
  時給: 1200円 × 120時間
  月給: 144000円

[管理職] 佐藤部長(E003)
  基本給: 400000円
  管理手当: 80000円
  ボーナス: 800000円
  年収: 6560000円

--- 月間人件費合計 ---
正社員: 300000円
パート: 144000円
管理職: 480000円
合計: 924000円
模範解答
interface Payable {
    void showPaySlip();
}

abstract class Employee implements Payable {
    protected String name;
    protected String employeeId;
    protected int baseSalary;

    Employee(String name, String employeeId, int baseSalary) {
        this.name = name;
        this.employeeId = employeeId;
        this.baseSalary = baseSalary;
    }

    abstract int calculateMonthlySalary();
}

class FullTimeEmployee extends Employee {
    private double bonusMultiplier;

    FullTimeEmployee(String name, String employeeId, int baseSalary, double bonusMultiplier) {
        super(name, employeeId, baseSalary);
        this.bonusMultiplier = bonusMultiplier;
    }

    @Override
    int calculateMonthlySalary() {
        return baseSalary;
    }

    int calculateBonus() {
        return (int) (baseSalary * bonusMultiplier);
    }

    int calculateAnnualSalary() {
        return baseSalary * 12 + calculateBonus();
    }

    @Override
    public void showPaySlip() {
        System.out.println("[正社員] " + name + "(" + employeeId + ")");
        System.out.println("  基本給: " + baseSalary + "円");
        System.out.println("  ボーナス: " + calculateBonus() + "円");
        System.out.println("  年収: " + calculateAnnualSalary() + "円");
    }
}

class PartTimeEmployee extends Employee {
    private int hourlyRate;
    private int hoursWorked;

    PartTimeEmployee(String name, String employeeId, int hourlyRate, int hoursWorked) {
        super(name, employeeId, 0);
        this.hourlyRate = hourlyRate;
        this.hoursWorked = hoursWorked;
    }

    @Override
    int calculateMonthlySalary() {
        return hourlyRate * hoursWorked;
    }

    @Override
    public void showPaySlip() {
        System.out.println("[パート] " + name + "(" + employeeId + ")");
        System.out.println("  時給: " + hourlyRate + "円 × " + hoursWorked + "時間");
        System.out.println("  月給: " + calculateMonthlySalary() + "円");
    }
}

class Manager extends FullTimeEmployee {
    private int managementAllowance;

    Manager(String name, String employeeId, int baseSalary, double bonusMultiplier, int managementAllowance) {
        super(name, employeeId, baseSalary, bonusMultiplier);
        this.managementAllowance = managementAllowance;
    }

    @Override
    int calculateMonthlySalary() {
        return baseSalary + managementAllowance;
    }

    @Override
    int calculateAnnualSalary() {
        return calculateMonthlySalary() * 12 + calculateBonus();
    }

    @Override
    public void showPaySlip() {
        System.out.println("[管理職] " + name + "(" + employeeId + ")");
        System.out.println("  基本給: " + baseSalary + "円");
        System.out.println("  管理手当: " + managementAllowance + "円");
        System.out.println("  ボーナス: " + calculateBonus() + "円");
        System.out.println("  年収: " + calculateAnnualSalary() + "円");
    }
}

public class Exercise10 {
    public static void main(String[] args) {
        Employee[] employees = {
            new FullTimeEmployee("田中太郎", "E001", 300000, 2.0),
            new PartTimeEmployee("鈴木花子", "E002", 1200, 120),
            new Manager("佐藤部長", "E003", 400000, 2.0, 80000)
        };

        System.out.println("=== 給与明細一覧 ===");

        int totalMonthlyCost = 0;
        for (Employee e : employees) {
            System.out.println();
            e.showPaySlip();
            totalMonthlyCost += e.calculateMonthlySalary();
        }

        System.out.println("\n--- 月間人件費合計 ---");
        for (Employee e : employees) {
            String type;
            if (e instanceof Manager) {
                type = "管理職";
            } else if (e instanceof FullTimeEmployee) {
                type = "正社員";
            } else {
                type = "パート";
            }
            System.out.println(type + ": " + e.calculateMonthlySalary() + "円");
        }
        System.out.println("合計: " + totalMonthlyCost + "円");
    }
}

ポイント: 抽象クラス、インターフェース、継承、オーバーライド、ポリモーフィズムを組み合わせた総合問題です。Employee 型の配列でさまざまな種類の社員を統一的に扱えるのがポリモーフィズムの威力です。instanceof の判定順序に注意(ManagerFullTimeEmployee でもあるため、先に判定する必要があります)。


まとめ

問題 テーマ 難易度
問題1 基本的な継承
問題2 メソッドのオーバーライド
問題3 super キーワード ⭐⭐
問題4 抽象クラス ⭐⭐
問題5 インターフェース ⭐⭐
問題6 複数インターフェースの実装 ⭐⭐
問題7 instanceof と型キャスト ⭐⭐
問題8 デフォルトメソッド ⭐⭐⭐
問題9 テンプレートメソッドパターン ⭐⭐⭐
問題10 社員管理システム(総合問題) ⭐⭐⭐

次回は 例外処理 のコーディング問題です!


シリーズ一覧:Java研修コーディング問題集

  1. 変数・データ型・演算子 編
  2. 条件分岐(if / switch)編
  3. 繰り返し処理(for / while)編
  4. 配列 編
  5. メソッド 編
  6. 文字列操作(String)編
  7. クラスとオブジェクト 編
  8. 👉 継承とインターフェース 編(本記事)
  9. 例外処理 編
  10. コレクションと Stream API 編

著者: @kotaro_ai_lab
AI駆動開発やテック情報を毎日発信しています。フォローお気軽にどうぞ!

0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?