169. Closing the electrical panel after JDK 17
Do you remember our electrical panel model introduced earlier in Problems X and Y? In Problem Y, we closed this model as much as possible by using the Java capabilities available before JDK 17. Now, we can reiterate that model (Problem Y) and close it completely via JDK 17 Sealed Classes.We start with the ElectricComponent interface which is declared as follows:
public interface ElectricComponent {}
At this moment, this interface is not closed. It can be extended/implemented from any other point of the application. But, we can close it by transforming it into a sealed interface with the proper permits clause as follows:
public sealed interface ElectricComponent
permits ElectricCircuit, ElectricBreaker,
Capacitor, Resistor, Transistor {}
Next, let’s focus on the semi-closed ElectricCircuit class. This is an abstract class that uses a package-private constructor to block any extension from outside its package. However, it can still be extended from inside the package. We can close it completely by transforming it into a sealed class with the proper permits clause (the package-private constructor can be safely removed):
public sealed abstract class ElectricCircuit
implements ElectricComponent
permits ParallelCircuit, SeriesCircuit, ShortCircuit {}
The ParallelCircuit, SeriesCircuit, and ShortCircuit were declared as final, so they remain unchanged. We don’t want to permit any extension of these classes.Next, let’s focus on the Capacitor, Transistor, and Resistor classes. These classes are also abstract and use package-private constructors to avoid any extension attempts coming from outside of their packages. So, we can remove these constructors as transform them into sealed classes exactly as we did with ElectricCircuit:
public sealed abstract class Capacitor
implements ElectricComponent
permits CeramicCapacitor, ElectrolyticCapacitor {}
public sealed abstract class Transistor
implements ElectricComponent
permits FieldEffectTransistor, BipolarTransistor {}
public sealed abstract class Resistor
implements ElectricComponent
permits MetalResistor, CarbonResistor {}
Check out the Resistor class. It permits only MetalResistor and CarbonResistor classes. Next, the MetalResistor class needs special attention. So far, this class is public and can be extended from any other point of the application:
public class MetalResistor extends Resistor {}
Closing this class can be done by sealing it as follows:
public sealed class MetalResistor extends Resistor
permits MetalFilmResistor, MetalOxideResistor {}
The MetalFilmResistor and MetalOxideResistor classes are final and remain unchanged:
public final class MetalFilmResistor extends MetalResistor {}
public final class MetalOxideResistor extends MetalResistor {}
The same statement applies to the CeramicCapacitor, ElectrolyticCapacitor, BipolarTransistor, and FieldEffectTransistor classes.Next, let’s focus on the ElectricBreaker interface. This interface lives in the modern.circuit.panel package and it was implemented only by ElectricPanel, so it was declared package-private (it cannot be extended/implemented from outside the package):
interface ElectricBreaker extends ElectricComponent {}
In order to completely close this interface we transform it into a sealed interface as follows:
public sealed interface ElectricBreaker
extends ElectricComponent permits ElectricPanel {}
Notice that we added the public modifier as well. This is needed because ElectricBreaker must occur in the permits list of ElectricComponent interface, so it has to be available outside its package.Finally, the ElectricPanel remains unchanged (a final class implementing ElectricBreaker):
public final class ElectricPanel implements ElectricBreaker {}
Mission accomplished! The electric panel hierarchical model is completely closed to extension. We put everything in a named module (since we have sealed artifacts that interact across different packages) and we are done.