Sealed Classes & Interfaces practice questions

From OCP Java SE 25 (1Z0-831) · 14 questions on this topic

Sealed Classes & Interfaces practice questions from OCP Java SE 25 (1Z0-831). This pack has 14 questions tagged Sealed Classes & Interfaces, drawn from its timed mock exams. 8 of them are worked through in full below — the question, every option, why each is right or wrong, and the explanation.

Worked examples for Sealed Classes & Interfaces

  1. Question 1

    What does this print? ```java public class Main { abstract sealed static class Expr permits Num, Add {} static final class Num extends Expr { final int v; Num(int v){this.v=v;} } static final class Add extends Expr { final Expr l, r; Add(Expr l, Expr r){this.l=l;this.r=r;} } static int eval(Expr e) { return switch (e) { case Num n -> n.v; case Add a -> eval(a.l) + eval(a.r); }; } public static void main(String[] args) { System.out.println(eval(new Add(new Num(3), new Add(new Num(4), new Num(5))))); } } ```

    1. A. Compilation fails: a class cannot be both abstract and sealed

      abstract and sealed compose freely; an abstract sealed class is the standard shape for an expression hierarchy.

    2. B. Compilation fails: the switch needs a default branch

      The two final permitted subtypes cover the whole sealed hierarchy, so the switch is exhaustive and needs no default.

    3. C. 12.0

      eval is declared to return int, not double, so the result prints as 12, not 12.0.

    4. D. 12Correct answer

      abstract sealed is a legal combination; the two final permitted subtypes make the switch exhaustive, and recursive evaluation of 3 + (4 + 5) yields the int 12 (JEP 409).

    Explanation

    `abstract` and `sealed` combine legally: `sealed` restricts who may extend the type while `abstract` forbids instantiating it directly, which is the classic algebraic-data-type shape. With `final` permitted subtypes fully covering the hierarchy, the switch is exhaustive without a `default`. The recursive evaluation returns `int`, so the summed value prints as a bare integer rather than a decimal.

  2. Question 2

    What is the result of compiling and running this code? ```java public class Main { sealed static class Shape permits Circle {} static class Circle extends Shape {} public static void main(String[] args) { System.out.println("ok"); } } ```

    1. A. It compiles and prints ok

      It does not compile: the permitted subclass carries no seal-continuation modifier, which is a compile-time error, so nothing is printed.

    2. B. Compilation fails: a sealed class must be declared abstract

      A sealed class need not be abstract; sealed on its own is legal, and abstract is an independent, optional choice.

    3. C. Compilation fails: Circle must be declared final, sealed, or non-sealedCorrect answer

      Every direct subclass of a sealed class must declare exactly one of final, sealed, or non-sealed; this subclass carries none, so javac rejects it (JLS 25 §8.1.1.2).

    4. D. Compilation fails: a permits clause must list at least two subclasses

      A permits clause may list a single subtype; one permitted subclass is perfectly legal, so the count is not the problem.

    Explanation

    Every direct subclass of a sealed type must state how the seal continues by declaring exactly one of `final`, `sealed`, or `non-sealed`. A bare subclass declaration with none of those modifiers is rejected at compile time (`error: sealed, non-sealed or final modifiers expected`), so the program never runs. This bare permitted-subclass form is the single most common sealed-classes trap and never compiles.

  3. Question 3

    What does this print? ```java public class Main { sealed interface Node {} record Leaf(int v) implements Node {} record Branch(Node l, Node r) implements Node {} public static void main(String[] args) { System.out.println(Node.class.isSealed() + ":" + Node.class.getPermittedSubclasses().length); } } ```

    1. A. true:2Correct answer

      The permits clause is inferred from the same-file implementors, so the interface stays sealed (isSealed() returns true) with two permitted subtypes in its permitted set.

    2. B. false:2

      Omitting permits does not remove the seal; the interface is still sealed, so isSealed() is true, not false.

    3. C. true:0

      The two same-file implementors are inferred into the permitted set, so getPermittedSubclasses() has length 2, not 0.

    4. D. Compilation fails: a sealed interface must have an explicit permits clause

      A permits clause is optional when every permitted subtype lives in the same compilation unit, so it is not mandatory here.

    Explanation

    When a sealed type and all of its permitted subtypes are declared in the same source file, the `permits` clause may be omitted and the compiler infers the permitted set from that file. Omitting `permits` does not remove the seal: the type remains sealed, and reflection reports it as sealed with the inferred subtypes counted in its permitted set. Note the contrast — subtypes scattered across different files with no `permits` clause is a compile error, but every subtype in one file compiles.

  4. Question 4

    What does this print? ```java public class Main { sealed static class Animal permits Dog {} non-sealed static class Dog extends Animal {} static class Puppy extends Dog {} public static void main(String[] args) { Animal a = new Puppy(); System.out.println(a.getClass().getSimpleName()); } } ```

    1. A. Compilation fails: Puppy cannot extend a permitted subclass

      Dog is declared non-sealed, so it can be extended freely by unlisted classes; there is no compile error even though Puppy appears in no permits clause.

    2. B. PuppyCorrect answer

      non-sealed re-opens Dog's branch so an unlisted class may extend it, and getClass().getSimpleName() reflects the actual runtime type, which is Puppy.

    3. C. Dog

      Dog would print only if the reference pointed at a Dog instance, but getClass() reflects the actual runtime type, not the declared reference type or the parent type.

    4. D. Compilation fails: Dog must be declared final

      A permitted subclass may be final, sealed, or non-sealed; non-sealed is a valid choice and does not force final.

    Explanation

    The `non-sealed` modifier is the escape hatch of a sealed hierarchy: it re-opens one branch so that arbitrary, unlisted classes may extend it, while the sealed parent still restricts its own direct subtypes. A class that extends a non-sealed permitted subclass is therefore legal even though it appears in no permits clause. At runtime `getClass()` reports the actual instantiated type rather than the declared reference type or any ancestor.

  5. Question 5

    A sealed class `Payment` is compiled and deployed on the class path (the unnamed module). Where may the subclasses listed in its permits clause be declared?

    1. A. In any package, provided each permitted subclass is public

      Accessibility does not relax locality; a public permitted subclass in a different package is still rejected on the class path.

    2. B. In any module that requires Payment's module

      The same-module rule applies only when the sealed class is in a named module; on the class path (unnamed module) the stricter same-package rule applies, and merely reading a module is never sufficient.

    3. C. Only in the same source file as Payment

      Same source file is required only when the permits clause is omitted and inferred; with an explicit permits clause, same package (class path) or same module (named module) suffices.

    4. D. In the same package as PaymentCorrect answer

      A sealed class and its permitted subclasses must be co-located; for a sealed class in the unnamed module (the class path), the JLS requires every permitted subclass to be in the same package (JLS 25 §8.1.1.2).

    Explanation

    Sealed-type locality is two-tier: a sealed class in a named module requires its permitted subclasses to be in the same module, while a sealed class in the unnamed module (ordinary class-path code) requires them to be in the same package. Because this class is deployed on the class path, the stricter same-package rule governs where its permitted subclasses may be declared. Accessibility does not relax this locality, and the same-source-file requirement applies only when the permits clause is inferred rather than written.

  6. Question 6

    `Shape` is sealed and permits exactly `Circle` and `Square`, and the switch in `kind` has a case for each of them, with no `default`. What is the result of compiling and running this program? ```java public class Main { sealed interface Shape permits Circle, Square {} record Circle(double r) implements Shape {} record Square(double side) implements Shape {} static String kind(Shape sh) { return switch (sh) { case Circle c -> "circle"; case Square s when s.side() > 0 -> "square"; }; } public static void main(String[] args) { System.out.println(kind(new Circle(2.0))); } } ```

    1. A. Compilation fails: a guard is not permitted on a case label in a switch over a sealed type

      Invents a rule: a guard is perfectly legal on a case label here; adding an unguarded `case Square s` after the guarded case, or a `default`, makes this same switch compile.

    2. B. circle

      Assumes the program compiles because every permitted subtype is mentioned somewhere in the switch; being mentioned is not the same as being covered, and the guard is exactly what breaks the link.

    3. C. An exception is thrown at runtime: java.lang.MatchException

      Assumes the coverage gap is discovered at run time when a value slips past every case; the compiler will not let a non-exhaustive switch expression get that far, so there is no run time.

    4. D. Compilation fails: the switch expression does not cover all possible input valuesCorrect answer

      A case carrying a `when` guard is a conditional match, so the compiler refuses to count it for coverage; coverage of Shape then rests on `case Circle c` alone, which does not cover Square, so javac rejects the switch expression as not exhaustive (JLS 25 14.11.1.1).

    Explanation

    Trace: a case label carrying a `when` guard is a *conditional* match, so the compiler refuses to count it when it computes coverage — it cannot prove `s.side() > 0` holds. Coverage of `Shape` therefore rests on `case Circle c` alone, which does not cover `Square`, and the switch expression is not exhaustive. javac rejects it with `error: the switch expression does not cover all possible input values`. The `Circle` argument in `main` is irrelevant: exhaustiveness is a compile-time property of the switch, not of the value that reaches it. Why the others are wrong: `circle` assumes the program compiles because every permitted subtype is mentioned somewhere in the switch. Being mentioned is not the same as being covered — the guard is exactly what breaks the link. `Compilation fails: a guard is not permitted on a case label in a switch over a sealed type` invents a rule. Guards are perfectly legal here; adding `case Square s -> "square";` after the guarded case, or a `default`, makes this same switch compile. `An exception is thrown at runtime: java.lang.MatchException` assumes the gap is discovered at run time when a value slips past every case. The compiler will not let a non-exhaustive switch *expression* get that far, so there is no run time. Exam tip: a guarded case never contributes to exhaustiveness. The idiom is to guard a case and then follow it with the same unguarded pattern as a fallback (`case Square s when s.side() > 0 -> ...; case Square s -> ...;`). Reverse trap: this bites only switch *expressions* and pattern switch *statements*; the compiler's message points at the `switch` keyword, not at the guard, so students hunt for a missing subtype that is right there on the screen.

  7. Question 7

    What is the result of compiling and running this code? ```java public class Main { sealed interface Shape permits Circle, Square, Triangle {} record Circle() implements Shape {} record Square() implements Shape {} record Triangle() implements Shape {} static String name(Shape s) { return switch (s) { case Circle c -> "circle"; case Square sq -> "square"; }; } public static void main(String[] args) { System.out.println(name(new Circle())); } } ```

    1. A. circle

      It never runs; an uncovered permitted subtype makes the switch non-exhaustive, which is a compile-time error.

    2. B. Compilation fails: the switch expression does not cover all possible input valuesCorrect answer

      The sealed interface permits three subtypes but the switch expression covers only two and has no default, so it is not exhaustive and javac rejects it (JEP 441).

    3. C. Compilation fails: a sealed interface cannot permit a record

      A sealed interface may permit records; that is a common, legal pattern, not the error here.

    4. D. It compiles but throws an exception at runtime

      The failure is at compile time, not runtime; exhaustiveness is verified statically before any code runs.

    Explanation

    A switch expression over a sealed type must be exhaustive, and the compiler verifies this statically against the full permitted set. When a permitted subtype is left uncovered and there is no `default` branch, the switch is non-exhaustive and compilation fails before any code runs. This is the feature at work: adding a new permitted subtype turns every previously-exhaustive `default`-less switch into a compile error, pointing you at every switch that must now handle the new case.

  8. Question 8

    Which two statements about sealed types are correct? (Choose two.)

    1. A. A record can serve as a permitted subtype of a sealed interface with no explicit modifierCorrect answer

      A record is implicitly final, and final is one of the three legal states for a permitted subtype, so a record can implement a sealed interface with no extra modifier at all.

    2. B. The non-sealed modifier may be applied to a class that has no sealed direct supertype

      non-sealed on a type with no sealed direct supertype is a compile-time error (javac: non-sealed modifier not allowed here ... does not have any sealed supertypes); there must be a seal to un-seal.

    3. C. An exhaustive switch over a sealed type's permitted subtypes compiles without a default branchCorrect answer

      When a switch covers every permitted subtype of a sealed type, the compiler proves exhaustiveness from the closed hierarchy, so the switch compiles with no default branch.

    4. D. A sealed class must declare at least two permitted subtypes

      A sealed class may permit as few as one subtype (or several); there is no rule requiring at least two.

    Explanation

    Implicitly final types such as records slot into a sealed hierarchy with no extra modifier, because final is one of the three legal states for a permitted subtype. And a switch that covers every permitted subtype of a closed hierarchy is provably exhaustive, so it compiles without a `default`. By contrast, `non-sealed` requires an actual sealed supertype to relax, and a sealed type is free to permit just one subtype — 'at least two permitted subtypes' is a made-up rule.

Practise all 14 Sealed Classes & Interfaces questions

OCP Java SE 25 has the full set, inside timed mock exams that mirror real exam conditions — every question with a worked explanation.

Open OCP Java SE 25

Other topics in this pack