How are anonymous classes related to the Liskov substitution principle?
Imagine that you are doing an audit of a third-party desktop Java application that interacts with users’ input and has the following code:
var c = new Control();
c.registerHandler(
new Handler<Event>() {
@Override
void handle(Event e) {
System.out.println("Event occurred: " + e);
}
}
);
Based on the provided code fragment, which statement is true about the Handler type? Choose one.
A. It must be an interface.
B. It must be an abstract class.
C. It can be a concrete class.
D. It can be either a class or an interface.
E. It can be a class, an interface, or an enum.
F. It can be a class, an interface, an enum, or a record.
Answer. In this era in which Java has lambda expressions, it’s not unusual to hear the idea that anonymous classes are irrelevant. However, anonymous inner classes provide capabilities that are not possible with lambda expressions. How is that?
In general, an anonymous class can extend a class, either a concrete or an abstract class, or it can implement an interface. If an abstract type is specialized, all the abstract methods in that type must be implemented. However, an anonymous class can declare only a single parent type, regardless of whether it’s an interface or a class. This limitation derives largely from the syntax, which provides only a single place in the code at which to define the parent type.
The declaration/instantiation of an anonymous class includes a parameter list. If the parent type is a class (either abstract or concrete), that parameter list is passed to the parent class’s constructor and, of course, there must be a matching constructor for that delegation. If the parent type is an interface, the parameter list must be empty.
An anonymous class can override methods of the parent type, implement abstract methods, and define arbitrary new methods and fields, even static ones, though that’s not likely to be useful.
From the description above, you can conclude that an anonymous class is not constrained to either implement an interface or extend an abstract class. Therefore, options A and B are incorrect.
You also know that, in general, an anonymous class can be derived from a concrete or abstract class or from an interface. This seems to make options C and D both look good, though the question requires a single answer. So, you have perhaps guessed there’s a bit more to this question, which we’ll get to in a moment.
Enum types place strict limits on their subtypes: Specifically, any such subtype must itself be an anonymous class declared inside the enum, and any such subtype is implicitly final. An enum that does not declare any anonymous subtypes is itself implicitly final. This means an anonymous class declared in the form shown in the question cannot possibly be a subtype of an enum. Therefore, you can reject both options E and F as incorrect.
Further, record types are always final, which makes option F impossible.
So, how can you choose between options C and D? Turn your attention to interfaces. Any method declared in an interface will default to being public if no explicit modifier is given, and most methods in an interface can be declared explicitly public. Static and concrete instance methods can also be declared as private. Notably, however, no interface method can have any intermediate accessibility—that is, no interface method can have package level, or protected, accessibility.
In addition to the restrictions on interface methods, Java seeks to impose the Liskov substitution principle on overriding or implementing methods. This principle broadly says that if a method substitutes for a method in a parent type, it should not cause any surprises. Putting it another way, the child’s method should be consistent with the declaration of the parent’s method.
Java seeks to enforce this guidance in several ways, and one of them is to prevent an overriding or implementing method from being less accessible than the method it replaces. This means that any method that claims to override an interface method must be public. (Note that you can’t use @Override to override a private method in any situation.)
In this case, however, the method handle(Event e) in the anonymous class has package accessibility. This can be valid only if the method being overridden also has package accessibility, and that tells you that the parent type must be a class and not an interface. That parent class could be either abstract or concrete, but the only option that’s valid is option C, which says the parent can be a concrete class.
So, you can conclude that option C is correct, and option D is incorrect.
Conclusion. The correct answer is option C.
Source: oracle.com
0 comments:
Post a Comment