9-2. 演習問題
問題1:選択問題
Section titled “問題1:選択問題”1-1. 次のコードの出力はどれか。
public class Animal { public void speak() { System.out.println("..."); }}public class Cat extends Animal { @Override public void speak() { System.out.println("ニャー"); }}
Animal a = new Cat();a.speak();- A.
"..." - B.
"ニャー" - C. コンパイルエラー
- D.
NullPointerException
解答と解説
正解:B
これがポリモーフィズムの核心だ。変数 a の型は Animal だが、実際に格納されているオブジェクトは Cat のインスタンス。Javaはメソッド呼び出しを 実行時 に実際のオブジェクトの型に基づいて解決する(動的ディスパッチ)。
そのため a.speak() は Cat の speak() が呼ばれ "ニャー" が出力される。
これにより、同じ Animal 型として扱いながらも、それぞれの動物が異なる振る舞いをするコードが書ける。
1-2. インターフェースと抽象クラスの違いとして正しいものはどれか。
- A. インターフェースはインスタンス化できるが、抽象クラスはできない
- B. インターフェースは複数実装できるが、クラスは1つしか継承できない
- C. 抽象クラスはメソッドの実装を持てないが、インターフェースは持てる
- D. インターフェースはフィールドを持てるが、抽象クラスは持てない
解答と解説
正解:B
- A:どちらもインスタンス化できない
- B:正しい。
implements InterfaceA, InterfaceBのように複数実装可能。クラスの継承はextends ParentClassの1つのみ - C:逆。抽象クラスはメソッドの実装を持てる。インターフェースも Java 8以降は
defaultメソッドで実装を持てる - D:逆。抽象クラスはフィールドを持てる。インターフェースは
public static final定数のみ
使い分けの基準:
- 継承(is-a):「AはBの一種」→ 抽象クラス
- 機能契約(can-do):「Aはこの機能を持つ」→ インターフェース
1-3. @Override アノテーションをつけずにメソッドをオーバーライドした場合どうなるか。
- A. コンパイルエラーになる
- B. 実行時エラーになる
- C. 動作はするが、コンパイラがオーバーライドのチェックをしてくれない
- D. 親クラスのメソッドが呼ばれるようになる
解答と解説
正解:C
@Override はオーバーライドの意図をコンパイラに伝えるためのアノテーションであり、省略してもメソッドは正常にオーバーライドされる。
しかし @Override がないと:
- 親クラスのメソッド名を typo しても「新しいメソッドの定義」として扱われ、オーバーライドになっていないことに気づけない
- 親クラスのメソッドシグネチャが変わったときに検出できない
これらのバグを防ぐため、オーバーライドするときは必ず @Override を付けるのが良いプラクティス。
1-4. final キーワードの説明として誤っているものはどれか。
- A.
finalクラスは継承できない - B.
finalメソッドはオーバーライドできない - C.
final変数は一度代入したら変更できない - D.
finalフィールドはstaticでなければならない
解答と解説
正解:D
final フィールドは static である必要はない。static final はクラス定数(Math.PI など)として使われるが、インスタンスの final フィールドはコンストラクタで初期化できる。
public class Circle { private final double radius; // static でない final フィールド
public Circle(double radius) { this.radius = radius; // コンストラクタで一度だけ設定できる } // this.radius = 5.0; // コンストラクタ外での再代入はエラー}final の3つの用途:
final変数・フィールド:再代入禁止finalメソッド:オーバーライド禁止finalクラス:継承禁止
問題2:穴埋め問題
Section titled “問題2:穴埋め問題”2. 次のコードの( )に入るコードを答えよ。
// (1) Animal を継承した Dog クラスpublic class Dog (1) Animal { public Dog(String name) { (2)(name); // 親クラスのコンストラクタを呼ぶ }
(3) // オーバーライドを明示するアノテーション public void speak() { System.out.println(name + " がワンワン!"); }}
// (2) Printable インターフェースを実装した Report クラスpublic class Report (4) Printable { @Override public void print() { System.out.println("レポートを印刷します"); }}解答と解説
extendssuper@Overrideimplements
解説
(1) extends でスーパークラスを指定して継承する。単一継承のみ可能。
(2) super(引数) でスーパークラスのコンストラクタを呼ぶ。サブクラスのコンストラクタの先頭(最初の文)でなければならない。
(3) @Override はコンパイラへの「これはオーバーライドである」という宣言。メソッド名や引数が親と一致しない場合にエラーを出してくれる。
(4) implements でインターフェースを実装する。複数のインターフェースを implements A, B, C のようにカンマ区切りで実装できる。
問題3:記述問題
Section titled “問題3:記述問題”3-1. 次のクラス設計に問題点があればそれを指摘し、改善案を示せ。
public class BankAccount { public int balance; // 残高
public void deposit(int amount) { balance += amount; }
public void withdraw(int amount) { balance -= amount; }}解答と解説
問題点:
-
balanceがpublicのため、外部から直接書き換えられるBankAccount account = new BankAccount();account.balance = -1000000; // 負の残高にできてしまう -
withdrawにバリデーションがなく、残高以上の引き出しができてしまう
改善後のコード:
public class BankAccount { private int balance; // private でカプセル化
public BankAccount(int initialBalance) { if (initialBalance < 0) throw new IllegalArgumentException("初期残高は0以上"); this.balance = initialBalance; }
public int getBalance() { return balance; } // 読み取り専用
public void deposit(int amount) { if (amount <= 0) throw new IllegalArgumentException("入金額は正の数"); balance += amount; }
public void withdraw(int amount) { if (amount <= 0) throw new IllegalArgumentException("出金額は正の数"); if (amount > balance) throw new IllegalStateException("残高不足"); balance -= amount; }}カプセル化の効果:
- 残高は
deposit/withdrawメソッドを通じてのみ変更できる - バリデーションにより不正な状態(負の残高など)を防げる
- 将来、ロギングや通知の追加も
deposit/withdrawメソッドを修正するだけでよい
3-2. Shape(抽象クラス)を継承した Triangle クラスを実装せよ。コンストラクタは底辺(base)と高さ(height)を受け取り、area() は 底辺 × 高さ / 2 を返すこと。
public abstract class Shape { protected String color; public Shape(String color) { this.color = color; } public abstract double area();}解答と解説
public class Triangle extends Shape { private double base; private double height;
public Triangle(String color, double base, double height) { super(color); // 親クラスのコンストラクタで color を初期化 this.base = base; this.height = height; }
@Override public double area() { return base * height / 2; }}
// 使用例Triangle t = new Triangle("緑", 6.0, 4.0);System.out.println(t.area()); // 12.0System.out.printf("色: %s, 面積: %.1f%n", t.color, t.area());// "色: 緑, 面積: 12.0"super(color) で親クラスの color フィールドを初期化する必要がある点が重要だ。color は protected のため直接アクセスもできるが、super() を使うのがOOPの慣習。
問題4:ハンズオン
Section titled “問題4:ハンズオン”下の Java WASM プレイグラウンドで Product クラスを完成させ、カプセル化と例外の振る舞いを確認せよ。
取り組む内容
Section titled “取り組む内容”Productクラスのフィールド・コンストラクタ・メソッドを実装するtoString()をオーバーライドして、指定形式で表示できるようにするpurchase(20)実行時に、在庫不足の例外が起きることを確認する- 例外メッセージを変えて、自分で挙動を観察してみる
確認したいポイント
Section titled “確認したいポイント”- フィールドを
privateにすることで、どんな不正操作を防げるか - 正常系と異常系をメソッドで分けて考えられるか
toString()を使うとデバッグ出力が読みやすくなる理由