3
1

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 Silver対策】継承とポリモーフィズム 厳選10問

3
Posted at

Java Silver試験で頻出の「継承とポリモーフィズム」に関する問題を10問収録しました。extends、super()、メソッドオーバーライド、多態性、instanceof、Objectクラスのメソッドなど、試験で問われやすいポイントを網羅しています。


問題1 ⭐(基本)

次のコードをコンパイル・実行すると、どうなりますか?

class Animal {
    void speak() {
        System.out.println("...");
    }
}

class Dog extends Animal {
    void speak() {
        System.out.println("Woof!");
    }
}

public class Main {
    public static void main(String[] args) {
        Animal a = new Dog();
        a.speak();
    }
}
  • A. ...
  • B. Woof!
  • C. コンパイルエラー
  • D. 実行時エラー
解答と解説

正解: B

これはポリモーフィズム(多態性)の基本例です。変数aの型はAnimalですが、実際のオブジェクトはDogです。インスタンスメソッドの呼び出しは実行時のオブジェクトの型Dog)に基づいて解決されるため、Dogspeak()が呼ばれます。

これを「動的束縛(dynamic binding)」または「動的ディスパッチ」と呼びます。


問題2 ⭐⭐(応用)

次のコードをコンパイル・実行すると、どうなりますか?

class Parent {
    Parent() {
        System.out.println("Parent constructor");
    }
}

class Child extends Parent {
    Child() {
        System.out.println("Child constructor");
    }
}

public class Main {
    public static void main(String[] args) {
        Child c = new Child();
    }
}
  • A. Child constructor
  • B. Parent constructor
  • C. Parent constructorChild constructor
  • D. Child constructorParent constructor
解答と解説

正解: C

サブクラスのコンストラクタの先頭には、明示的にsuper()this()を書かなければ、コンパイラが自動的にsuper()(親クラスの引数なしコンストラクタ呼び出し)を挿入します。

そのため、Childのコンストラクタは実質的に以下と同じです:

Child() {
    super(); // コンパイラが自動挿入
    System.out.println("Child constructor");
}

親クラスのコンストラクタが先に実行され、その後に子クラスのコンストラクタの残りが実行されます。


問題3 ⭐⭐(応用)

次のコードをコンパイル・実行すると、どうなりますか?

class Base {
    Base(int x) {
        System.out.println("Base: " + x);
    }
}

class Derived extends Base {
    Derived() {
        System.out.println("Derived");
    }
}

public class Main {
    public static void main(String[] args) {
        Derived d = new Derived();
    }
}
  • A. Base: 0Derived
  • B. Derived
  • C. コンパイルエラー
  • D. 実行時エラー
解答と解説

正解: C

Baseクラスには引数ありのコンストラクタBase(int x)のみが定義されているため、デフォルトコンストラクタ(引数なし)は自動生成されません。Derivedのコンストラクタには明示的なsuper()呼び出しがないため、コンパイラがsuper()(引数なし)を自動挿入しようとしますが、Baseに引数なしコンストラクタが存在しないためコンパイルエラーになります。

修正するには、Derived()内でsuper(何らかのint値)を明示的に呼び出す必要があります。


問題4 ⭐⭐⭐(チャレンジ)

次のコードをコンパイル・実行すると、どうなりますか?

class Parent {
    protected Number getValue() {
        return 42;
    }
}

class Child extends Parent {
    @Override
    protected Integer getValue() {
        return 100;
    }
}

public class Main {
    public static void main(String[] args) {
        Parent p = new Child();
        System.out.println(p.getValue());
    }
}
  • A. 42
  • B. 100
  • C. コンパイルエラー(戻り値の型が一致しない)
  • D. 実行時エラー
解答と解説

正解: B

Java 5以降、オーバーライドの戻り値に**共変戻り値型(covariant return type)**が認められています。IntegerNumberのサブクラスなので、戻り値の型をNumberからIntegerに変更することは正当なオーバーライドです。

ポリモーフィズムにより、実行時のオブジェクト型ChildgetValue()が呼ばれ、100が出力されます。

なお、共変戻り値型はプリミティブ型には適用されません。intからlongへの変更はオーバーライドとして認められません。


問題5 ⭐⭐(応用)

次のコードをコンパイル・実行すると、どうなりますか?

class Animal {
    String type = "Animal";

    String getType() {
        return type;
    }
}

class Cat extends Animal {
    String type = "Cat";

    String getType() {
        return type;
    }
}

public class Main {
    public static void main(String[] args) {
        Animal a = new Cat();
        System.out.println(a.type);
        System.out.println(a.getType());
    }
}
  • A. AnimalAnimal
  • B. CatCat
  • C. AnimalCat
  • D. CatAnimal
解答と解説

正解: C

フィールドとメソッドでは動作が異なります:

  • フィールドアクセス: コンパイル時の変数の型(宣言型)で決まる → a.typeAnimaltypeを参照 → "Animal"
  • メソッド呼び出し: 実行時のオブジェクトの型で決まる(動的束縛) → a.getType()CatgetType()が呼ばれる → "Cat"

フィールドにはポリモーフィズムが適用されません。フィールドの「隠蔽(hiding)」とメソッドの「オーバーライド(overriding)」は異なる概念です。


問題6 ⭐(基本)

次のコードをコンパイル・実行すると、どうなりますか?

class Vehicle {}
class Car extends Vehicle {}
class Truck extends Vehicle {}

public class Main {
    public static void main(String[] args) {
        Vehicle v = new Car();
        System.out.println(v instanceof Vehicle);
        System.out.println(v instanceof Car);
        System.out.println(v instanceof Truck);
    }
}
  • A. truetruetrue
  • B. truetruefalse
  • C. truefalsefalse
  • D. コンパイルエラー
解答と解説

正解: B

instanceofは実行時のオブジェクトの型を確認します。vの実際のオブジェクトはCarです。

  • v instanceof VehicleCarVehicleのサブクラスなのでtrue
  • v instanceof Car → オブジェクトはCarなのでtrue
  • v instanceof TruckCarTruckではないのでfalse

instanceofはオブジェクトが指定した型か、そのサブクラスのインスタンスである場合にtrueを返します。nullに対しては常にfalseを返します。


問題7 ⭐⭐⭐(チャレンジ)

次のコードをコンパイル・実行すると、どうなりますか?

class Base {
    void show() {
        System.out.println("Base");
    }
}

class Sub extends Base {
    @Override
    void show() {
        System.out.println("Sub");
    }
}

public class Main {
    public static void main(String[] args) {
        Base b = new Sub();
        b.show();

        Base b2 = (Base) b;
        b2.show();
    }
}
  • A. SubBase
  • B. SubSub
  • C. BaseBase
  • D. BaseSub
解答と解説

正解: B

キャストは参照の型を変えるだけであり、実際のオブジェクトは変わりません。(Base) bBase型にキャストしても、オブジェクト自体は依然としてSubのインスタンスです。

メソッド呼び出しは実行時のオブジェクトの型に基づいて解決されるため、どちらの場合もSubshow()が呼ばれます。

キャストはコンパイル時の型チェックに影響しますが、実行時のポリモーフィズムには影響しません。


問題8 ⭐⭐(応用)

次のコードをコンパイル・実行すると、どうなりますか?

class Parent {
    private void greet() {
        System.out.println("Hello from Parent");
    }

    void callGreet() {
        greet();
    }
}

class Child extends Parent {
    void greet() {
        System.out.println("Hello from Child");
    }
}

public class Main {
    public static void main(String[] args) {
        Child c = new Child();
        c.callGreet();
    }
}
  • A. Hello from Child
  • B. Hello from Parent
  • C. コンパイルエラー
  • D. 実行時エラー
解答と解説

正解: B

Parentgreet()privateなので、サブクラスChildからは見えません。Childgreet()はオーバーライドではなく、まったく別のメソッドです。

callGreet()Parentクラスで定義されており、Parentprivate greet()を呼び出します。privateメソッドはポリモーフィズムの対象外で、コンパイル時に静的に解決されます。

privateメソッド、staticメソッド、finalメソッドはオーバーライドできません。


問題9 ⭐⭐⭐(チャレンジ)

次のコードをコンパイル・実行すると、どうなりますか?

class Shape {
    @Override
    public String toString() {
        return "Shape";
    }

    @Override
    public boolean equals(Object obj) {
        return obj instanceof Shape;
    }

    @Override
    public int hashCode() {
        return 1;
    }
}

public class Main {
    public static void main(String[] args) {
        Shape s1 = new Shape();
        Shape s2 = new Shape();
        System.out.println(s1.toString());
        System.out.println(s1.equals(s2));
        System.out.println(s1.equals("hello"));
    }
}
  • A. Shapetruetrue
  • B. Shapetruefalse
  • C. Shapefalsefalse
  • D. コンパイルエラー
解答と解説

正解: B

Objectクラスの3つの重要なメソッドをオーバーライドしています。

  • s1.toString() → オーバーライドされたtoString()が呼ばれ"Shape"を返す
  • s1.equals(s2)s2Shapeのインスタンスなのでobj instanceof Shapetrue
  • s1.equals("hello")"hello"StringのインスタンスでありShapeではないのでobj instanceof Shapefalse

equals()をオーバーライドする場合はhashCode()も必ずオーバーライドすべきです(契約: equals()trueならhashCode()も同じ値を返す必要がある)。


問題10 ⭐⭐⭐(チャレンジ)

次のコードをコンパイル・実行すると、どうなりますか?

class A {
    void method() throws Exception {
        System.out.println("A");
    }
}

class B extends A {
    @Override
    void method() throws RuntimeException {
        System.out.println("B");
    }
}

class C extends A {
    @Override
    void method() {
        System.out.println("C");
    }
}

public class Main {
    public static void main(String[] args) throws Exception {
        A obj = new B();
        obj.method();
    }
}
  • A. A
  • B. B
  • C. C
  • D. コンパイルエラー
解答と解説

正解: B

オーバーライド時のthrows宣言ルール:

  • 同じ例外またはそのサブクラスをスローできる → RuntimeExceptionExceptionのサブクラスなのでOK
  • 例外をスローしないようにもできる → クラスCのようにthrows宣言を省略するのもOK
  • 親クラスより広い例外をスローすることはできない → throws Throwableにするとコンパイルエラー

Bmethod()RuntimeExceptionを宣言していますが、RuntimeExceptionExceptionのサブクラスなので正当なオーバーライドです。ポリモーフィズムによりBmethod()が呼ばれ、"B"が出力されます。


参考


@kotaro_ai_lab
AI活用や開発効率化について発信しています。フォローお気軽にどうぞ!

3
1
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
3
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?