A new type of Java class (Interface) - sealed class

Posted by clint on Fri, 12 Nov 2021 04:50:25 +0100

Sealed class is a new feature officially supported by Java 17. It allows class inheritance in Java to be controlled more finely. Today, let's take a look at this new feature.

Sealing class

In the previous Java class inheritance, the inheritance control of Java class is very limited, and class inheritance can only be controlled through final keyword and access control character. For example, the final class cannot be integrated; Package private classes can only inherit under the package.

This is obviously not enough. If a function is only allowed to appear on Phone and Pad, it is not allowed to appear on Computer. If the inheritance implementation of this function is not limited, developers will easily abuse the implementation class of this function and reuse some code by mistake. This is why sealed classes arise.

Declaration of sealed class

❝ a sealed class can be not only a class, but also an interface. The sealed classes in this article are collectively referred to as

Sealed classes (interfaces) can specify which classes and interfaces can be extended or implemented. You can use the sealed modifier to indicate that a class is a sealed class. However, the following is an incorrect sealed class declaration:

/**
 * This is a false demonstration
 */
public sealed interface SealedService {
   
    void doSomething();
}

The scope of inheritability (Implementation) must be specified when declaring a sealed class (Interface), so the above writing is wrong. You must specify a class that allows you to extend a sealed class with the permissions clause, and the permissions keyword is after extensions or implements.

❝ in short, sealed classes specify which other classes (or interfaces) can extend them.

Here is the correct way to write:

/**
 * This is a correct demonstration, which makes it clear that the inheritable subclass is {@ link SealedServiceImpl}
 * The sealed class interface implements {@ link SuperService}
 */
public sealed interface SealedService extends SuperService permits SealedServiceImpl {
    void doSomething();
}

/**
 * Sealed subclass
 */
public final class SealedServiceImpl implements SealedService {
    @Override
    public void doSomething() {
        System.out.println("This is a subclass of a sealed class");
    }
}

Type of subclass of sealed class

In the above example, the implementation class of the sealed class (Interface) is marked with the final keyword. Of course, the implementation class of the sealed class can also be a sealed class:

/**
 * Sealed subclass
 */
public sealed class SealedServiceImpl implements SealedService permits SonService {
    @Override
    public void doSomething() {
        System.out.println("This is a subclass of a sealed class");
    }
}


public final class SonService extends SealedServiceImpl {
}

So can the subclass of a sealed class (Interface) only be a final class or a sealed class, so it can no longer be extended? The answer is No. you only need to use the keyword non sealed to explicitly declare that the inheritance of the sealed class is implemented as an unsealed class, and you can continue to expand.

public non-sealed class SealedServiceImpl implements SealedService {
    @Override
    public void doSomething() {

    }

    /**
     * Declare the unsealed class with {@ code non sealed} to continue the extension
     */
    static class NonSealedExtend extends SealedServiceImpl {

    }

}

To sum up, the subclass of the sealed Class is either final Class; Or sealed Class; Or non sealed Class.

The class declared by permissions must be a direct subclass

The subclass declared by the permissions keyword of the sealed class must be a direct implementation class. To prove this, we write as follows:

/**
 * Wrong demonstration
 */
public sealed interface SealedService extends SuperService permits SealedServiceImpl, SonService {
    void doSomething();
}

public sealed class SealedServiceImpl implements SealedService permits SonService {
    @Override
    public void doSomething() {
        System.out.println("This is a subclass of a sealed class");
    }
}

public final class SonService extends SealedServiceImpl {
}

I used SonService to indirectly implement SealedService, and the result reported an error. The error information must be a direct inheritance relationship.

Bad sealed class inheritance implementation

As can be seen from the above figure, SonService does not directly implement SealedService, which will break the rules of sealing classes, so it cannot be compiled.

❝ the subclass declared by the permits keyword in the sealed class must be a direct subclass and cannot be implemented indirectly.

Sealed classes do not support anonymous classes and functional interfaces

Since a sealed class must explicitly inherit implementation relationships, it does not support anonymous classes.

/**
 * Sealed classes cannot use anonymous classes
 *
 * @return the sealed service
 */
public SealedService sealedService(){
    // Prompt Anonymous classes must not extend sealed classes
    return new SealedService() {
        @Override
        public void doSomething() {
            
        }
    };
}

Functional interfaces are also not supported:

/**
 * Wrong demonstration
 */
@FunctionalInterface
public sealed interface SealedService permits SealedServiceImpl {
    void doSomething();
}

summary

The sealed class has been officially confirmed in Java 17, which is also one of the very important features of Java 17. It is very useful for scenarios that require fine-grained control of inheritance relationships.