Tuesday, October 11, 2022

Quiz yourself: Java’s sealed classes and instanceof checks

Java’s Sealed Classes, Core Java, Oracle Java Prep, Oracle Java Certification, Oracle Java Learning, Oracle Java Prep, Oracle Java Tutorial and Materials

What does the Java compiler do when it encounters an instanceof expression?


Given the full code of an interface and three classes

public interface Loggable {
    static boolean isLoggable(Process p) {
        return p instanceof Loggable;
    }
}

sealed class Process permits SlowProcess, FastProcess {
}

final class SlowProcess extends Process {
}

non-sealed class FastProcess extends Process {
}

Which statement is true? Choose one.

A. The code compiles successfully as it is.

B. In order to make the code compilable, the final modifier from SlowProcess class must be removed.

C. In order to make the code compilable, the Process class must implement the Loggable interface.

D. In order to make the code compilable, the non-sealed modifier from FastProcess class must be replaced with the final modifier.

Answer. This question explores limitations added by sealed classes on instanceof checks.

The instanceof expression tests a value against a type. When the compiler comes across an instanceof expression, the compiler first determines if the expression can be shown to be definitely false by static analysis. In such a case, the expression causes a compilation error. A readily understood example of this kind of impossible instanceof is illustrated by the following code:

void compilerError(String s) {
  // compiler rejects this instanceof test
  boolean impossible = s instanceof StringBuilder;

The analysis is a little more subtle where interfaces are concerned, and more examples can help clarify the issue. First, the following example compiles successfully:

interface SomeInterface {
  void doStuff();
}
// and later the test:
void test(BufferedWriter x) {
  boolean test = x instanceof SomeInterface;

The reason this compiles is that even though SomeInterface is clearly a new interface and BufferedWriter does not implement it, the BufferedWriter class is not final. That means it’s possible that elsewhere, or in the future, a subclass of BufferedWriter might exist that implements SomeInterface. Because the compiler doesn’t know, it doesn’t signal a compilation error.

The next example shows another use of instanceof that fails to compile.

void compilerError(String x) {
  boolean test = x instanceof Runnable;

In this case, Runnable is an interface and String does not implement Runnable. At this point, the code seems very similar to the previous example. However, this example fails because String is a final class, and because it’s final, there is no chance of a subclass existing elsewhere. Therefore, there is no chance that a subclass exists that implements Runnable.

This type of logic is also applied to the situation in this exam question.

The instanceof expression in the question tests a reference of type Process to see if the object referred to by that reference implements the Loggable interface. Now, in the typical situation, all types that can ever be assignment-compatible with a sealed class will be known to the compiler, and if none implements the interface in question, the compiler could reject the code outright.

However, in this case, the FastProcess class is marked non-sealed, and because of this, it’s possible for this class to have subclasses that are not known to the compiler at this time, and those subclasses might possibly implement the Loggable interface. Because of this possibility, the compiler accepts the code and compiles the test as written. Therefore, the code will compile successfully as it is, and option A is correct.

The subclasses of a sealed class must explicitly list one of these modifiers: sealed, non-sealed, or final. Removing the final keyword would cause an error, and from this you can determine that option B is incorrect.

Option C proposes a solution to fix a compilation error, but as you have already seen, there’s nothing wrong with the code. Implementing the Loggable interface by the Process class would be entirely valid but is not required to fix the code. Because of this, you can determine that option C is incorrect.

Option D suggests replacing non-sealed with the final modifier. However, this would guarantee that no subtype of Process could exist that would pass the instanceof test—and therefore the change will actually result in compilation failure. This tells you that option D is incorrect.

Conclusion. The correct answer is option A.

Source: oracle.com

Related Posts

0 comments:

Post a Comment