Java Silver 継承とインタフェースの使用 問題

ゆめさくが作った練習問題です。本試験の問題ではありません。

継承とインタフェースの使用の問題を2⁠4問、小分野ごとにまとめました。選ぶと、その場で正誤と解説が出ます。

ログインなしで解けます。答えはこのブラウザの中だけに残り、どこにも送りません。

この分野の小分野

  • 継承、抽象クラス、オーバーライド、キャスト(12問)
  • インタフェースとシール・クラス(12問)

継承とインタフェースの使用の問題

24 問

選ぶと、すぐに正誤と解説が出ます。選んだ答えは変えられません。

  1. 継承、抽象クラス、オーバーライド、キャスト

    問 1Java のクラスの継承について正しい説明はどれですか。

  2. 問 2次のコードをコンパイルして実行したときの結果として正しいものはどれですか。

    class A {
        A() { System.out.print("a"); }
        A(int n) { System.out.print("A" + n); }
    }
    class B extends A {
        B() { this(2); System.out.print("b"); }
        B(int n) { super(n); System.out.print("B"); }
    }
    class C extends B {
        C() { System.out.print("C"); }
    }
    public class Main {
        public static void main(String[] args) {
            new C();
            System.out.println();
        }
    }
  3. 問 3次のコードをコンパイルして実行したときの結果として正しいものはどれですか。

    class A {
        String name() { return "A"; }
    }
    class B extends A {
        String name() { return "B" + super.name(); }
    }
    class C extends B {
        String name() { return "C" + super.name(); }
    }
    public class Main {
        public static void main(String[] args) {
            A a = new C();
            System.out.println(a.name());
        }
    }
  4. 問 4次のコードをコンパイルして実行したときの結果として正しいものはどれですか。

    abstract class Shape {
        abstract double area();
    }
    public class Main {
        public static void main(String[] args) {
            Shape[] shapes = new Shape[3];   // (A)
            System.out.print(shapes.length); // (B)
            Shape s = new Shape();           // (C)
            System.out.print(s.area());
        }
    }
  5. 問 5次のコードをコンパイルして実行したときの結果として正しいものはどれですか。

    abstract class Animal {
        abstract String sound();
        abstract String name();
    }
    abstract class Pet extends Animal {     // (A)
        String name() { return "pet"; }
    }
    class Dog extends Pet {                 // (B)
        String name() { return "dog"; }
    }
    public class Main {
        public static void main(String[] args) {
            Animal a = new Dog();           // (C)
            System.out.println(a.name());
        }
    }
  6. 問 6次のコードで、コンパイルエラーになる行をすべて挙げたものはどれですか。

    class Box {
        Number value() { return 1; }
        long count() { return 1L; }
        protected void open() { }
    }
    class SmallBox extends Box {
        Integer value() { return 2; }   // (A)
        int count() { return 2; }       // (B)
        public void open() { }          // (C)
    }
    public class Main {
        public static void main(String[] args) {
            Box b = new SmallBox();
            System.out.println(b.value());
        }
    }
  7. 問 7スーパークラスに void load() throws IOException というメソッドがあります。サブクラスでこのメソッドをオーバーライドする宣言として、コンパイルできるものはどれですか。

  8. 問 8次のコードをコンパイルして実行したときの結果として正しいものはどれですか。

    public class Main {
        private final int id;
        Main(int id) { this.id = id; }
    
        @Override                                // (A)
        public boolean equals(Main other) {      // (B)
            return other != null && id == other.id;
        }
    
        public static void main(String[] args) {
            Object a = new Main(1);
            Object b = new Main(1);
            System.out.println(a.equals(b));     // (C)
        }
    }
  9. 問 9次のコードをコンパイルして実行したときの結果として正しいものはどれですか。

    class Parent {
        String tag = "p";
        static String kind() { return "P"; }
        String show() { return tag + kind(); }
    }
    class Child extends Parent {
        String tag = "c";
        static String kind() { return "C"; }
    }
    public class Main {
        public static void main(String[] args) {
            Child c = new Child();
            System.out.println(c.tag + c.kind() + c.show());
        }
    }
  10. 問 10次のコードをコンパイルして実行したときの結果として正しいものはどれですか。

    class Base {
        Base() { print(); }
        void print() { System.out.print("Base/"); }
    }
    class Derived extends Base {
        private String msg = "hello";
        Derived() { print(); }
        @Override
        void print() { System.out.print(msg + "/"); }
    }
    public class Main {
        public static void main(String[] args) {
            new Derived();
            System.out.println();
        }
    }
  11. 問 11次のコードをコンパイルしたときの結果として正しいものはどれですか。

    interface Printable { }
    class Paper { }
    final class Stone { }
    
    public class Main {
        public static void main(String[] args) {
            Paper paper = new Paper();
            Stone stone = new Stone();
            Object o = stone;
            Printable a = (Printable) paper;   // (A)
            Printable b = (Printable) o;       // (B)
            Printable c = (Printable) stone;   // (C)
            String d = (String) o;             // (D)
        }
    }
  12. 問 12次のコードをコンパイルして実行したときの結果として正しいものはどれですか。

    class Account {
        final String close() { return "closed"; }
    }
    class Savings extends Account {
        String close(int code) { return "saved" + code; }   // (A)
    }
    public class Main {
        public static void main(String[] args) {
            Savings s = new Savings();
            System.out.println(s.close() + " " + s.close(1));
        }
    }
  13. インタフェースとシール・クラス

    問 13インタフェースの中に書いたフィールドについて正しい説明はどれですか。

  14. 問 14次のコードをコンパイルして実行したときの結果として正しいものはどれですか。

    interface Task {
        void run();
    }
    class Base {
        void run() { System.out.print("base"); }
    }
    class Job extends Base implements Task { }   // (A)
    
    public class Main {
        public static void main(String[] args) {
            Task t = new Job();                  // (B)
            t.run();
        }
    }
  15. 問 15次のコードをコンパイルして実行したときの結果として正しいものはどれですか。

    interface Named { String name(); }
    interface Aged { int age(); }
    interface Person extends Named, Aged {
        default String label() { return name() + age(); }
    }
    class Student implements Person {
        public String name() { return "cat"; }
        public int age() { return 3; }
    }
    public class Main {
        public static void main(String[] args) {
            Named n = new Student();
            Aged a = (Aged) n;
            System.out.println(((Person) a).label());
        }
    }
  16. 問 16次のコードをコンパイルして実行したときの結果として正しいものはどれですか。

    interface Left {
        default String side() { return "L"; }
    }
    interface Right {
        default String side() { return "R"; }
    }
    class Both implements Left, Right {
        public String side() {
            return Right.super.side() + Left.super.side();
        }
    }
    public class Main {
        public static void main(String[] args) {
            System.out.println(new Both().side());
        }
    }
  17. 問 17次のコードをコンパイルして実行したときの結果として正しいものはどれですか。

    interface Greeter {
        default String greet() { return "I"; }
    }
    interface Polite extends Greeter {
        default String greet() { return "P"; }
    }
    class Base {
        public String greet() { return "B"; }
    }
    class A implements Greeter, Polite { }
    class B extends Base implements Polite { }
    
    public class Main {
        public static void main(String[] args) {
            System.out.println(new A().greet() + new B().greet());
        }
    }
  18. 問 18次のコードをコンパイルして実行したときの結果として正しいものはどれですか。

    interface Formatter {
        static String twice(String s) {
            return bracket(s) + bracket(s);      // (A)
        }
        default String wrap(String s) {
            return bracket(s) + count();         // (B)
        }
        private String bracket(String s) {       // (C)
            return "[" + s + "]";
        }
        private static int count() {
            return 1;
        }
    }
    public class Main implements Formatter {
        public static void main(String[] args) {
            System.out.println(new Main().wrap("x"));
        }
    }
  19. 問 19インタフェースのデフォルト・メソッドとして宣言したとき、コンパイルエラーになるものはどれですか。

  20. 問 20抽象クラスではできるが、インタフェースではできないことはどれですか。

  21. 問 21sealed を付けたクラスで permits を省いたとき、許可されるサブクラスについて正しい説明はどれですか。

  22. 問 22sealed クラス Shape が permits Square で Square を許可しています。Square の宣言のうち、コンパイルエラーになるものはどれですか。

  23. 問 23次のコードをコンパイルして実行したときの結果として正しいものはどれですか。

    sealed abstract class Pay permits Cash, Card { }   // (A)
    final class Cash extends Pay { }                    // (B)
    final class Card { }                                // (C)
    
    public class Main {
        public static void main(String[] args) {
            Pay p = new Cash();
            System.out.println(p instanceof Cash);
        }
    }
  24. 問 24次のコードをコンパイルして実行したときの結果として正しいものはどれですか。

    sealed interface Animal permits Dog, Wild {
        default String sound() { return "?"; }
    }
    final class Dog implements Animal {
        public String sound() { return "wan"; }
    }
    non-sealed interface Wild extends Animal { }
    class Wolf implements Wild { }
    
    public class Main {
        public static void main(String[] args) {
            Animal[] list = { new Dog(), new Wolf() };
            for (Animal a : list) {
                System.out.print(a.sound());
            }
            System.out.println();
        }
    }

24 問中 0 問に答えました

解説の一覧

解いたあとに読み返せるよう、全問の解説を並べています。問いを開くと解説が出ます。

継承、抽象クラス、オーバーライド、キャスト

問1Java のクラスの継承について正しい説明はどれですか。

正解は「final を付けたクラスは、ほかのクラスの extends の後ろに書けない」です。final を付けたクラスは「これで完成で、サブクラスは作らせない」という宣言です。言語仕様は、extends の後ろに final クラスを書くとコンパイルエラーになると定めています。たとえば final class Base を作り、class Sub extends Base と書くと、Sub の行でコンパイルエラーになります。ほかの選択肢が違う理由は次の通りです。extends の後ろに書けるクラスは 1 つだけです (単一継承)。カンマで複数並べられるのは、インタフェースを書く implements のほうです。extends を書かないクラスも、java.lang.Object を直接のスーパークラスとして継承しています。abstract クラスはサブクラスに継承されることを前提にしたクラスなので、extends の後ろに書けます。出典 https://docs.oracle.com/javase/specs/jls/se1⁠7/html/jls-⁠8.html#jls-⁠8.1.4

問2次のコードをコンパイルして実行したときの結果として正しいものはどれですか。
class A {
    A() { System.out.print("a"); }
    A(int n) { System.out.print("A" + n); }
}
class B extends A {
    B() { this(2); System.out.print("b"); }
    B(int n) { super(n); System.out.print("B"); }
}
class C extends B {
    C() { System.out.print("C"); }
}
public class Main {
    public static void main(String[] args) {
        new C();
        System.out.println();
    }
}

正解は「A2BbC」です。言語仕様では、コンストラクタの最初の文が this(...) か super(...) でないときだけ、先頭に暗黙の super() が入ります。最初の文が this(...) なら、そのコンストラクタには暗黙の super() は入りません。new C() では、C() の先頭に暗黙の super() が入り、B の引数なしのコンストラクタ B() が呼ばれます。B() の最初の文は this(2) なので、B(int) へ進みます。B(int) は super(2) で A(int) を呼び、A2 が出ます。そのあと B(int) の残りで B、B() の残りで b、最後に C() の本体で C が出て、A2BbC と出力されます。aBbC と aA2BbC は、B() にも暗黙の super() が入り A() が動くと考えた答えです。A2BC は、this(2) で呼んだ先から戻ったあとの B() の残りを見落とした答えです。出典 https://docs.oracle.com/javase/specs/jls/se1⁠7/html/jls-⁠8.html#jls-⁠8.8.7

問3次のコードをコンパイルして実行したときの結果として正しいものはどれですか。
class A {
    String name() { return "A"; }
}
class B extends A {
    String name() { return "B" + super.name(); }
}
class C extends B {
    String name() { return "C" + super.name(); }
}
public class Main {
    public static void main(String[] args) {
        A a = new C();
        System.out.println(a.name());
    }
}

正解は「CBA」です。変数 a の型は A ですが、実体は C なので、a.name() はオーバーライドした C の name() が動きます。C の name() は "C" に super.name() の結果をつなぎます。言語仕様では super.メソッド名 の形の呼び出しは、その式を書いたクラスの直接のスーパークラスからメソッドを探すので、C の中の super.name() は B の name() を呼びます。B の name() はさらに super.name() で A の name() を呼び、"A" が返ります。よって B は "BA"、C は "CBA" を返し、CBA と出力されます。C だけになるのは super の呼び出しを見落とした読み方です。CA は super が一番上の A へ飛ぶと考えた場合で、super は直接のスーパークラスを指すので違います。ABC は並びが逆です。出典 https://docs.oracle.com/javase/specs/jls/se1⁠7/html/jls-⁠1⁠5.html#jls-⁠1⁠5.1⁠2.1

問4次のコードをコンパイルして実行したときの結果として正しいものはどれですか。
abstract class Shape {
    abstract double area();
}
public class Main {
    public static void main(String[] args) {
        Shape[] shapes = new Shape[3];   // (A)
        System.out.print(shapes.length); // (B)
        Shape s = new Shape();           // (C)
        System.out.print(s.area());
    }
}

正解は「(C) の行でコンパイルエラーになる」です。abstract クラスは中身が完成していないクラスなので、new でインスタンスを作れません。言語仕様は、abstract クラスを new で作ろうとするとコンパイルエラーになると定めています。よって new Shape() と書いた (C) の行でコンパイルエラーになります。ほかの選択肢が違う理由は次の通りです。(A) の new Shape[3] は Shape のインスタンスではなく、Shape 型の参照を 3 つ入れる配列を作っているだけなので、abstract クラスでも作れます。中身はすべて null です。コンパイルの段階で (C) が止めるので、実行には進みません。3 と出力されることも、実行時に例外が出ることもありません。Shape を使うには、area() を実装した具象クラスを作り、そのインスタンスを Shape 型の変数に入れます。出典 https://docs.oracle.com/javase/specs/jls/se1⁠7/html/jls-⁠8.html#jls-⁠8.1.1.1

問5次のコードをコンパイルして実行したときの結果として正しいものはどれですか。
abstract class Animal {
    abstract String sound();
    abstract String name();
}
abstract class Pet extends Animal {     // (A)
    String name() { return "pet"; }
}
class Dog extends Pet {                 // (B)
    String name() { return "dog"; }
}
public class Main {
    public static void main(String[] args) {
        Animal a = new Dog();           // (C)
        System.out.println(a.name());
    }
}

正解は「(B) の行でコンパイルエラーになる」です。abstract の付いていない具象クラスが、宣言したものか継承したものかを問わず abstract メソッドを 1 つでも持っていると、コンパイルエラーになります。Dog は Pet を通して Animal の sound() と name() を受け継ぎます。name() は Dog 自身が実装していますが、sound() はどこにも実装がないまま残ります。Dog は abstract ではないので、Dog を宣言した (B) の行でコンパイルエラーになります。(A) の Pet は abstract クラスなので、sound() を実装しないままでも問題ありません。(C) は Animal 型の変数に Dog を入れるアップキャストで、Dog が正しく作れていれば通る文です。コンパイルが通らないので dog も出力されません。直すには Dog に sound() を実装するか、Dog に abstract を付けます。出典 https://docs.oracle.com/javase/specs/jls/se1⁠7/html/jls-⁠8.html#jls-⁠8.1.1.1

問6次のコードで、コンパイルエラーになる行をすべて挙げたものはどれですか。
class Box {
    Number value() { return 1; }
    long count() { return 1L; }
    protected void open() { }
}
class SmallBox extends Box {
    Integer value() { return 2; }   // (A)
    int count() { return 2; }       // (B)
    public void open() { }          // (C)
}
public class Main {
    public static void main(String[] args) {
        Box b = new SmallBox();
        System.out.println(b.value());
    }
}

正解は「(B) の行だけ」です。オーバーライドでは、戻り値の型の決まりが参照型と基本データ型で違います。(A) の Integer は Number のサブクラスです。戻り値が参照型なら、サブクラスの型に狭めて返すこと (共変の戻り値) が許されるので、(A) はコンパイルできます。(B) の戻り値は基本データ型です。言語仕様は、基本データ型の戻り値は元のメソッドとまったく同じ型でなければならないと定めています。int は long に代入できる型ですが、同じ型ではないので (B) はコンパイルエラーになります。(C) は protected を public に広げています。オーバーライドではアクセスを狭めることはできませんが、広げることはできるので正しい宣言です。よってエラーは (B) の行だけです。(A) を含む 2 つは共変の戻り値を、(C) を含む選択肢はアクセスを広げる規則を見落とした答えです。出典 https://docs.oracle.com/javase/specs/jls/se1⁠7/html/jls-⁠8.html#jls-⁠8.4.5

問7スーパークラスに void load() throws IOException というメソッドがあります。サブクラスでこのメソッドをオーバーライドする宣言として、コンパイルできるものはどれですか。

正解は「void load() throws FileNotFoundException, IllegalStateException」です。オーバーライドするメソッドは、元のメソッドより多くのチェック例外を throws に書けません。書けるのは、元の throws にある例外か、そのサブクラスだけです。FileNotFoundException は IOException のサブクラスなので書けます。IllegalStateException は RuntimeException のサブクラス、つまり非チェック例外で、この規則の対象外なので自由に書けます。ほかの選択肢が違う理由は次の通りです。InterruptedException は IOException のサブクラスではないチェック例外なので、足すとコンパイルエラーです。Exception は IOException のスーパークラスで、より広い例外を投げられることになるのでエラーです。java.sql.SQLException は IOException と関係のないチェック例外なので、IOException と並べてもエラーです。出典 https://docs.oracle.com/javase/specs/jls/se1⁠7/html/jls-⁠8.html#jls-⁠8.4.8.3

問8次のコードをコンパイルして実行したときの結果として正しいものはどれですか。
public class Main {
    private final int id;
    Main(int id) { this.id = id; }

    @Override                                // (A)
    public boolean equals(Main other) {      // (B)
        return other != null && id == other.id;
    }

    public static void main(String[] args) {
        Object a = new Main(1);
        Object b = new Main(1);
        System.out.println(a.equals(b));     // (C)
    }
}

正解は「(A) の行でコンパイルエラーになる」です。@Override は「このメソッドはスーパークラスかインタフェースのメソッドをオーバーライドしている」とコンパイラに確かめさせる注釈です。オーバーライドになっていなければ、言語仕様によりコンパイルエラーになります。Object の equals の引数は Object 型です。(B) の equals は引数が Main 型なので、シグネチャが違い、オーバーライドではなくオーバーロードになります。よって @Override を付けた (A) の行でコンパイルエラーになります。(B) の宣言そのものはオーバーロードとして正しく、(C) も Object の equals を呼ぶ正しい文です。@Override が無ければコンパイルでき、a の型が Object なので Object の equals(Object) が選ばれ、別のインスタンスどうしの比べとして false が出ます。今のコードはコンパイルが通らないので、true も false も出力されません。出典 https://docs.oracle.com/javase/specs/jls/se1⁠7/html/jls-⁠9.html#jls-⁠9.6.4.4

問9次のコードをコンパイルして実行したときの結果として正しいものはどれですか。
class Parent {
    String tag = "p";
    static String kind() { return "P"; }
    String show() { return tag + kind(); }
}
class Child extends Parent {
    String tag = "c";
    static String kind() { return "C"; }
}
public class Main {
    public static void main(String[] args) {
        Child c = new Child();
        System.out.println(c.tag + c.kind() + c.show());
    }
}

正解は「cCpP」です。フィールドと static メソッドはオーバーライドされず、サブクラスの同じ名前の物は親の物を隠す (隠ぺい) だけです。どちらを使うかは実体の型ではなく、コンパイル時に決まる式の型や書いた場所で決まります。c.tag と c.kind() は Child 型の変数 c を通しているので、Child の "c" と "C" になります。c.show() は Parent に書かれたメソッドで、その中の tag と kind() は Parent のクラスの中の名前として決まります。Child の tag も kind() も Parent の中からは見えないので、"p" と "P" になり、show() は "pP" を返します。よって cCpP と出力されます。cCcC は show() の中でも Child の物が使われると考えた答え、cCcP はフィールドだけ実体の型で決まると考えた答え、pPpP は c の型を Parent と取り違えた答えです。出典 https://docs.oracle.com/javase/specs/jls/se1⁠7/html/jls-⁠8.html#jls-⁠8.4.8.2

問10次のコードをコンパイルして実行したときの結果として正しいものはどれですか。
class Base {
    Base() { print(); }
    void print() { System.out.print("Base/"); }
}
class Derived extends Base {
    private String msg = "hello";
    Derived() { print(); }
    @Override
    void print() { System.out.print(msg + "/"); }
}
public class Main {
    public static void main(String[] args) {
        new Derived();
        System.out.println();
    }
}

正解は「null/hello/」です。new Derived() では、まず Derived のコンストラクタの先頭で暗黙の super() が動き、Base のコンストラクタが実行されます。Derived のフィールド msg に "hello" が入るのは、その super() が終わったあとです。Base のコンストラクタの中の print() はインスタンス・メソッドの呼び出しなので、実体の型 Derived でオーバーライドした print() が動きます。この時点で msg はまだ初期値の null なので null/ が出ます。次に msg に "hello" が入り、Derived のコンストラクタの print() が hello/ を出します。よって null/hello/ と出力されます。Base/ で始まる 2 つは、親のコンストラクタの中では親の print() が動くと考えた答えです。hello/hello/ は、フィールドの初期化が親のコンストラクタより先に済むと考えた答えです。出典 https://docs.oracle.com/javase/specs/jls/se1⁠7/html/jls-⁠1⁠2.html#jls-⁠1⁠2.5

問11次のコードをコンパイルしたときの結果として正しいものはどれですか。
interface Printable { }
class Paper { }
final class Stone { }

public class Main {
    public static void main(String[] args) {
        Paper paper = new Paper();
        Stone stone = new Stone();
        Object o = stone;
        Printable a = (Printable) paper;   // (A)
        Printable b = (Printable) o;       // (B)
        Printable c = (Printable) stone;   // (C)
        String d = (String) o;             // (D)
    }
}

正解は「(C) の行でコンパイルエラーになる」です。参照型のキャストは、実体がその型である見込みが少しでもあればコンパイルが通り、実際に違えば実行時に ClassCastException になります。見込みがまったく無いとコンパイラが分かるときだけ、コンパイルエラーになります。(C) の Stone は final クラスでサブクラスを持てず、Printable も実装していません。Stone 型の値が Printable である見込みは無いので、言語仕様の規則によりコンパイルエラーになります。(A) の Paper は final ではないので、Printable を実装したサブクラスの実体が入っているかもしれません。だからコンパイルは通ります (実行すれば ClassCastException になります)。(B) と (D) は Object 型からのキャストで、どの型へもキャストを書けます。これらも実行時に例外になり得ますが、コンパイルは通ります。出典 https://docs.oracle.com/javase/specs/jls/se1⁠7/html/jls-⁠5.html#jls-⁠5.1.6.1

問12次のコードをコンパイルして実行したときの結果として正しいものはどれですか。
class Account {
    final String close() { return "closed"; }
}
class Savings extends Account {
    String close(int code) { return "saved" + code; }   // (A)
}
public class Main {
    public static void main(String[] args) {
        Savings s = new Savings();
        System.out.println(s.close() + " " + s.close(1));
    }
}

正解は「closed saved1」です。final を付けたメソッドは、サブクラスでオーバーライドすることも隠すこともできません。ただし禁じられるのは同じシグネチャのメソッドを宣言することだけです。(A) の close(int code) は引数の並びが close() と違うので、オーバーライドではなく別のメソッド (オーバーロード) です。よってコンパイルできます。また final メソッドもふつうに継承されるので、Savings のインスタンスから close() を呼べます。s.close() は Account の close() で closed、s.close(1) は Savings の close(int) で saved1 を返し、closed saved1 と出力されます。(A) でエラーになるという答えは、名前が同じなら final に触れると考えた思い違いです。saved1 saved1 は close() がオーバーライドされたと考えた答え、closed closed は引数付きの呼び出しまで親に行くと考えた答えです。出典 https://docs.oracle.com/javase/specs/jls/se1⁠7/html/jls-⁠8.html#jls-⁠8.4.3.3

インタフェースとシール・クラス

問13インタフェースの中に書いたフィールドについて正しい説明はどれですか。

正解は「宣言と同時に値を入れなければ、コンパイルエラーになる」です。インタフェースのフィールドは、何も書かなくても暗黙に public static final になります。つまり、どこからでも見える 1 つだけの定数です。final なので後から値を入れる場所が無く、言語仕様は、インタフェースのフィールドの宣言には必ず初期化子 (= 値) が要り、無ければコンパイルエラーになると定めています。たとえば int MAX; とだけ書くとその行でエラーです。ほかの選択肢が違う理由は次の通りです。修飾子を書かなくても暗黙に public なので、パッケージの外からも参照できます。暗黙に static final なので、実装クラスのコンストラクタで値を入れることはできません。static を書かなくても暗黙に static なので、インスタンスごとに別の値を持つことはありません。出典 https://docs.oracle.com/javase/specs/jls/se1⁠7/html/jls-⁠9.html#jls-⁠9.3.1

問14次のコードをコンパイルして実行したときの結果として正しいものはどれですか。
interface Task {
    void run();
}
class Base {
    void run() { System.out.print("base"); }
}
class Job extends Base implements Task { }   // (A)

public class Main {
    public static void main(String[] args) {
        Task t = new Job();                  // (B)
        t.run();
    }
}

正解は「(A) の行でコンパイルエラーになる」です。インタフェースのメソッドは、修飾子を書かなくても暗黙に public abstract です。Task の run() も public です。Job は自分では run() を書いていませんが、Base から run() を継承し、これが Task の run() の実装として扱われます。ところが Base の run() は修飾子なし (パッケージ・アクセス) で、public より狭いアクセスです。言語仕様は、オーバーライドや実装をするメソッドが元のメソッドよりアクセスを狭めるとコンパイルエラーになると定めています。継承してきたメソッドでも同じなので、Job を宣言した (A) の行でコンパイルエラーになります。(B) は Job が正しく宣言できていれば通る文です。コンパイルが通らないので、base の出力も実行時の例外もありません。直すには Base の run() に public を付けます。出典 https://docs.oracle.com/javase/specs/jls/se1⁠7/html/jls-⁠8.html#jls-⁠8.4.8.3

問15次のコードをコンパイルして実行したときの結果として正しいものはどれですか。
interface Named { String name(); }
interface Aged { int age(); }
interface Person extends Named, Aged {
    default String label() { return name() + age(); }
}
class Student implements Person {
    public String name() { return "cat"; }
    public int age() { return 3; }
}
public class Main {
    public static void main(String[] args) {
        Named n = new Student();
        Aged a = (Aged) n;
        System.out.println(((Person) a).label());
    }
}

正解は「cat3」です。インタフェースは extends の後ろにカンマで区切って、複数のインタフェースを継承できます。Person は Named と Aged の両方を継承しているので、Person を実装した Student は Named、Aged、Person のどの型としても扱えます。Named 型の n の実体は Student です。Aged へのキャストも Person へのキャストも、実体の Student がその型を実装しているので成功します。label() は Person のデフォルト・メソッドで、name() と age() を呼んで "cat" と 3 をつなぎます。よって cat3 と出力されます。インタフェースが複数のインタフェースを継承するのは正しい書き方なので、コンパイルエラーにはなりません。実体がどのインタフェースも実装しているので ClassCastException も出ません。cat は age() の結果を見落とした答えです。出典 https://docs.oracle.com/javase/specs/jls/se1⁠7/html/jls-⁠9.html#jls-⁠9.1.3

問16次のコードをコンパイルして実行したときの結果として正しいものはどれですか。
interface Left {
    default String side() { return "L"; }
}
interface Right {
    default String side() { return "R"; }
}
class Both implements Left, Right {
    public String side() {
        return Right.super.side() + Left.super.side();
    }
}
public class Main {
    public static void main(String[] args) {
        System.out.println(new Both().side());
    }
}

正解は「RL」です。Left と Right は同じシグネチャのデフォルト・メソッド side() を持っています。両方を実装するクラスは、どちらを使うか決められないので、自分で side() をオーバーライドしなければなりません。Both はオーバーライドしているので、コンパイルできます。オーバーライドの中で、直接実装しているインタフェースのデフォルト・メソッドを呼ぶには インタフェース名.super.メソッド名() と書きます。Right.super.side() は "R"、Left.super.side() は "L" を返すので、RL と出力されます。LR は implements に書いた順に並ぶと考えた答えで、実際は式に書いた順です。L は最初に書いたインタフェースが勝つと考えた答えです。衝突をオーバーライドで解決しているので、コンパイルエラーにもなりません。出典 https://docs.oracle.com/javase/specs/jls/se1⁠7/html/jls-⁠1⁠5.html#jls-⁠1⁠5.1⁠2.1

問17次のコードをコンパイルして実行したときの結果として正しいものはどれですか。
interface Greeter {
    default String greet() { return "I"; }
}
interface Polite extends Greeter {
    default String greet() { return "P"; }
}
class Base {
    public String greet() { return "B"; }
}
class A implements Greeter, Polite { }
class B extends Base implements Polite { }

public class Main {
    public static void main(String[] args) {
        System.out.println(new A().greet() + new B().greet());
    }
}

正解は「PB」です。デフォルト・メソッドが重なったときの決まりは 2 つあります。1 つは、より具体的なインタフェース (サブインタフェース) のメソッドが勝つこと、もう 1 つは、スーパークラスから継承した具象メソッドがインタフェースのデフォルト・メソッドに勝つことです。A は Greeter と Polite を実装していますが、Polite は Greeter を継承して greet() をオーバーライドしています。Greeter の greet() は継承されず、Polite の "P" が使われます。B は Base から public な具象メソッド greet() を継承しているので、Polite のデフォルト・メソッドは継承されず、Base の "B" が使われます。よって PB と出力されます。IB は A で Greeter が勝つと考えた答え、PP は B でインタフェースが勝つと考えた答えです。どちらも 1 つに決まるので、コンパイルエラーにはなりません。出典 https://docs.oracle.com/javase/specs/jls/se1⁠7/html/jls-⁠8.html#jls-⁠8.4.8

問18次のコードをコンパイルして実行したときの結果として正しいものはどれですか。
interface Formatter {
    static String twice(String s) {
        return bracket(s) + bracket(s);      // (A)
    }
    default String wrap(String s) {
        return bracket(s) + count();         // (B)
    }
    private String bracket(String s) {       // (C)
        return "[" + s + "]";
    }
    private static int count() {
        return 1;
    }
}
public class Main implements Formatter {
    public static void main(String[] args) {
        System.out.println(new Main().wrap("x"));
    }
}

正解は「(A) の行でコンパイルエラーになる」です。Java 9 からインタフェースに private メソッドを書けます。static の付かない private メソッドはインスタンス・メソッドで、private static メソッドはインスタンスなしで呼べるメソッドです。(A) は static メソッド twice の中から、インスタンス・メソッドの bracket を呼んでいます。static メソッドには this にあたるインスタンスが無いので、言語仕様によりコンパイルエラーになります。(B) のデフォルト・メソッドはインスタンス・メソッドなので、private の bracket も private static の count も呼べます。(C) のように private メソッドに本体を書くのは正しい書き方です。(A) が無ければ wrap("x") は [x]1 を返しますが、今のコードはコンパイルが通らないので何も出力されません。出典 https://docs.oracle.com/javase/specs/jls/se1⁠7/html/jls-⁠1⁠5.html#jls-⁠1⁠5.1⁠2.3

問19インタフェースのデフォルト・メソッドとして宣言したとき、コンパイルエラーになるものはどれですか。

正解は「default String toString() { return "x"; }」です。言語仕様は、デフォルト・メソッドが Object クラスの private でないメソッドとオーバーライド等価 (名前と引数の並びが同じ) になるとコンパイルエラーになると定めています。インタフェースを実装するクラスは必ず Object を継承しており、toString() はクラス側の物が常に優先されるので、デフォルト・メソッドで用意しても使われることがないからです。toString() は引数なしで Object の toString() と同じシグネチャなので、エラーになります。ほかの選択肢が違う理由は次の通りです。toText() は Object に無い名前なので問題ありません。equals(String s) は引数が String で、Object の equals(Object) とはシグネチャが違うオーバーロードなので書けます。hashCode(int seed) も引数があるので Object の hashCode() とは別のメソッドで、書けます。出典 https://docs.oracle.com/javase/specs/jls/se1⁠7/html/jls-⁠9.html#jls-⁠9.4.1.2

問20抽象クラスではできるが、インタフェースではできないことはどれですか。

正解は「protected のメソッドを宣言する」です。言語仕様は、インタフェースのメソッドに付けられるアクセス修飾子は public か private だけで、何も書かなければ暗黙に public になると定めています。protected を付けるとコンパイルエラーです。抽象クラスのメソッドには、ふつうのクラスと同じく protected を付けられます。ほかの選択肢が違う理由は次の通りです。abstract メソッドは、抽象クラスでもインタフェースでも宣言できます。インタフェースでは修飾子を書かなくても暗黙に abstract です。本体のあるメソッドは、インタフェースでもデフォルト・メソッド、static メソッド、private メソッドとして書けます。private メソッドも Java 9 からインタフェースに書けるようになりました。デフォルト・メソッドどうしで共通の処理をまとめるのに使います。出典 https://docs.oracle.com/javase/specs/jls/se1⁠7/html/jls-⁠9.html#jls-⁠9.4

問21sealed を付けたクラスで permits を省いたとき、許可されるサブクラスについて正しい説明はどれですか。

正解は「同じソース・ファイルに書かれ、直接継承するクラスが許可される」です。sealed クラスは、継承してよいサブクラスを permits の後ろに並べて限定します。言語仕様では、permits を省いた場合、同じコンパイル単位 (同じソース・ファイル) に書かれ、そのクラスを直接のスーパークラスとするクラスが、許可されたサブクラスとみなされます。たとえば 1 つのファイルに sealed class Shape { } と final class Circle extends Shape { } を書けば、permits が無くてもコンパイルできます。ほかの選択肢が違う理由は次の通りです。permits を省いても sealed であることは変わらず、どのクラスからでも継承できるようにはなりません。同じパッケージでも別のファイルのクラスは許可されません。permits は省けるので、省いただけではエラーになりません。ただし同じファイルに直接のサブクラスが 1 つも無いとエラーになります。出典 https://docs.oracle.com/javase/specs/jls/se1⁠7/html/jls-⁠8.html#jls-⁠8.1.6

問22sealed クラス Shape が permits Square で Square を許可しています。Square の宣言のうち、コンパイルエラーになるものはどれですか。

正解は「class Square extends Shape { }」です。sealed クラスを直接継承するクラスは、final、sealed、non-⁠sealed のどれかを必ず付けなければなりません。言語仕様は、どれも付けないとコンパイルエラーになると定めています。よって何も付けない class Square extends Shape { } がエラーです。これは、継承の広がりを sealed で絞ったのに、サブクラスの段で知らないうちに誰でも継承できる状態に戻ってしまうのを防ぐための決まりです。ほかの宣言は正しく書けます。final は Square で継承を止めます。non-⁠sealed は Square から先を誰でも継承できるように開きます。abstract non-⁠sealed は、abstract と non-⁠sealed を一緒に付けた形で、Square を抽象クラスにしたうえで先を開きます。abstract はどの 3 つとも別の修飾子なので、一緒に付けてかまいません。出典 https://docs.oracle.com/javase/specs/jls/se1⁠7/html/jls-⁠8.html#jls-⁠8.1.1.2

問23次のコードをコンパイルして実行したときの結果として正しいものはどれですか。
sealed abstract class Pay permits Cash, Card { }   // (A)
final class Cash extends Pay { }                    // (B)
final class Card { }                                // (C)

public class Main {
    public static void main(String[] args) {
        Pay p = new Cash();
        System.out.println(p instanceof Cash);
    }
}

正解は「permits に書いた Card が Pay を継承していないのでコンパイルエラーになる」です。permits の後ろに書いたクラスは、すべてその sealed クラスを直接継承していなければなりません。言語仕様は、そうでないクラスを permits に書くとコンパイルエラーになると定めています。Card は permits に書かれていますが、extends Pay を書いておらず、Pay のサブクラスではありません。javac はこのエラーを permits を書いた Pay の宣言、(A) の行で知らせます。Cash は permits に書かれ、final を付けて Pay を直接継承しているので正しい宣言で、sealed クラスのサブクラスに final を付けるのはむしろ決まりの通りです。sealed は abstract と一緒に付けられ、抽象クラスの継承先を絞る使い方ができます。コンパイルが通らないので true も出力されません。直すには Card を final class Card extends Pay { } にするか、permits から Card を外します。出典 https://docs.oracle.com/javase/specs/jls/se1⁠7/html/jls-⁠8.html#jls-⁠8.1.6

問24次のコードをコンパイルして実行したときの結果として正しいものはどれですか。
sealed interface Animal permits Dog, Wild {
    default String sound() { return "?"; }
}
final class Dog implements Animal {
    public String sound() { return "wan"; }
}
non-sealed interface Wild extends Animal { }
class Wolf implements Wild { }

public class Main {
    public static void main(String[] args) {
        Animal[] list = { new Dog(), new Wolf() };
        for (Animal a : list) {
            System.out.print(a.sound());
        }
        System.out.println();
    }
}

正解は「wan?」です。インタフェースにも sealed を付け、permits で実装クラスやサブインタフェースを限定できます。Animal が許しているのは Dog と Wild です。Dog は final を付けたクラスなので正しく、Wild は non-⁠sealed を付けたサブインタフェースなので正しい宣言です。non-⁠sealed にした Wild は、その先を誰でも実装できるように開いています。Wolf は Animal の permits に名前がありませんが、Animal を直接実装しているのではなく Wild を実装しているので、コンパイルできます。実行すると、Dog はオーバーライドした sound() で wan を、Wolf は Animal のデフォルト・メソッドで ? を返すので、wan? と出力されます。wanwan は Wolf も Dog の物を使うと考えた答え、wan は Wolf が配列に入らないと考えた答えです。出典 https://docs.oracle.com/javase/specs/jls/se1⁠7/html/jls-⁠9.html#jls-⁠9.1.1.4

ほかの分野

Java Silverの頁に、6分野の入口と試験の案内があります。

続きはゆめさくで

試験の知識を仕事につなげる一歩を、ここにまとめています。