0 Comments

168. Introducing the permits clause

In the previous problem, you saw how to write a closed hierarchical model in a single source file. Next, let’s use the Fuel.java source file to rewrite this model by using separate sources and separate packages.

Working with sealed classes in separate sources (same package)

Let’s consider the sealed Fuel interface from Fuel.java in package com.refinery.fuel:

public sealed interface Fuel {}      // Fuel.java

We know that this interface is extended by three other interfaces SolidFuel, LiquidFuel, and SolidFuel. Let’s define SolidFuel in SolidFuel.java source (same package) as follows:

public sealed interface SolidFuel {} // SolidFuel.java

As you’ll see, this code will not compile (is like the compiler is asking: hey, what’s the point of a sealed interface without any implementation/extension?). This time, we have to explicitly nominate the interfaces that can extend/implement the Fuel interface. For this, we use the permits keyword. Since Fuel is implemented by three interfaces, we just list their names via permits as follows:

public sealed interface Fuel
  permits SolidFuel, LiquidFuel, GaseousFuel {}

The list provided via permits is exhaustive. The SolidFuel is also a sealed interface, so it has to define its permits as well:

public sealed interface SolidFuel extends Fuel
  permits Coke, Charcoal {}

LiquidFuel and GaseousFuel are in the same scenario as SolidFuel:

// LiquidFuel.java
public sealed interface LiquidFuel extends Fuel
  permits Petroleum {}
// GaseousFuel.java
public sealed interface GaseousFuel extends Fuel
  permits NaturalGas, Propane {}

The Coke (Coke.java) and Charcoal (Charcoal.java) are final implementations of SolidFuel, so they don’t use the permits keyword:

public final class Coke implements SolidFuel {}
public final class Charcoal implements SolidFuel {}

The Petroleum class (Petroleum.java) is sealed and it allows three extensions:

public sealed class Petroleum implements LiquidFuel
  permits Diesel, Gasoline, Ethanol {}

The Diesel (Diesel.java), Gasoline (Gasoline.java), and Ethanol (Ethanol.java) classes are final:

public final class Diesel extends Petroleum {}
public final class Gasoline extends Petroleum {}
public final class Ethanol extends Petroleum {}

The NaturalGas interface (NaturalGas.java) is a sealed extension of GaseousFuel, while Propane (Propane.java) is a final implementation of GaseousFuel:

public sealed interface NaturalGas extends GaseousFuel
  permits Hydrogen, Methane {}
public final class Propane implements GaseousFuel {}

As you can see, the NaturalGas interface permits two extensions. The Hydrogen class is a final extension, while Methane is a sealed class:

public final class Hydrogen implements NaturalGas {}
public sealed class Methane implements NaturalGas
  permits Chloromethane, Dichloromethane {}

The Chloromethane class is final, and Dichloromethane is sealed:

public final class Chloromethane extends Methane {}
public sealed class Dichloromethane extends Methane
  permits Trichloromethane {}

Finally, we have the Trichloromethane class. This is a final class:

public final class Trichloromethane extends Dichloromethane {}

Done! The hierarchical model is closed and complete. Any attempt to extend/implement any member of this hierarchy will lead to an exception. If we want to add a new extension/implementation to a sealed class/interface then we have to add it to the permits list as well.

Working with sealed classes in separate packages

In the previous example, we expressed the classes/interfaces in separate sources but in the same package, com.refinery.fuel. Next, let’s consider that we spread these classes and interfaces in different packages as in the following figure:

Figure 8.5 – Sealed hierarchy in different packages

As long as the related sealed classes/interfaces live in the same package, we can use the JDK 9 unnamed special module (no explicit module). Otherwise, we have to use a named module. For instance, if we express our model as in Figure 8.5 then we have to add everything in a module via module-info.java:

module P168_SealedHierarchySeparatePackages {}

Without a named module, the code will not compile. In the bundled code, you can find both examples from this problem.


Leave a Reply

Your email address will not be published. Required fields are marked *