JavaRush /Java Blog /Random EN /Coffee break #143. Sealed classes in Java 17. 4 ways to i...

Coffee break #143. Sealed classes in Java 17. 4 ways to implement Singleton

Published in the Random EN group

Sealed classes in Java 17

Source: Codippa In this post we will look at sealed classes, a new feature introduced in Java 17, and how to declare and use them with examples. Coffee break #143.  Sealed classes in Java 17. 4 ways to implement Singleton - 1Sealed classes first appeared in Java 15 as a preview feature, and later in Java 16 with the same status. This feature became fully functional with the release of Java 17 ( JEP 409 ).

What are sealed classes?

A sealed class allows you to restrict or select subclasses. A class cannot extend a private class unless it is in the parent class's list of allowed child classes. The class is sealed using the sealed keyword . The sealed class must be followed by the permits keyword, along with a list of classes that can extend it. Here's an example:
public sealed class Device permits Computer, Mobile {
}
This declaration means that Device can only be extended by the Computer and Mobile classes . If any other class tries to extend it, a compiler error will be thrown. A class that extends a sealed class must have the final , sealed , or non-sealed keyword in its declaration . Thus we have a fixed class hierarchy. How does this relate to creating a child class?
  1. final means it cannot be further subclassed.

  2. sealed means that we need to declare child classes with permits .

  3. non-sealed means that here we end the parent-child hierarchy .

For example, Computer permits the classes Laptop and Desktop as long as Laptop itself remains non-sealed . This means that Laptop can be extended by classes such as Apple , Dell , HP and so on.

The main goals of introducing sealed classes are:

  1. Until now, you could only limit the extension of a class using the final keyword . A sealed class controls which classes can extend it by including them in the allowed list.

  2. It also allows the class to control which of them will be its child classes.

Rules

A few rules to remember when using sealed classes:
  1. A sealed class must define classes that can extend it using permits . This is not required if the child classes are defined within the parent class as an inner class.

  2. The child class must be either final , sealed , or non-sealed .

  3. A permitted child class must extend its parent sealed class.

    That is, if sealed class A allows class B, then B must extend A.

  4. If the sealed class is in a module, then the child classes must also be in the same module, or in the same package if the parent sealed class is in an unnamed module.

  5. Only directly permitted classes can extend a sealed class. That is, if A is a sealed class that allows B to extend it, then B is also a sealed class that allows C.

    Then C can only extend B, but cannot directly extend A.

Sealed Interfaces

Like sealed classes, interfaces can also be sealed. Such an interface may allow the selection of its child interfaces or classes, which can extend it using permits . Here's a good example:
public sealed interface Device permits Electronic, Physical,
DeviceImpl {
}
Here, the Device interface allows the Electronic and Physical interfaces to extend it and the DeviceImpl class for subsequent implementation.

Sealed Records

Sealed classes can be used with entries introduced in Java 16. An entry cannot extend a regular class, so it can only implement a private interface. In addition, the notation implies final . Therefore, an entry cannot use the permits keyword because it cannot be subclassed. That is, there is only a single-level hierarchy with records. Here's an example:
public sealed interface Device permits Laptop {
}
public record Laptop(String brand) implement Device {
}

Reflection support

Java Reflection provides support for sealed classes. The following two methods have been added to java.lang.Class :

1. getPermittedSubclasses()

This returns an array java.lang.Class containing all the classes allowed by this class object. Example:
Device c = new Device();
Class<? extends Device> cz = c.getClass();
Class<?>[] permittedSubclasses = cz.getPermittedSubclasses();
for (Class<?> sc : permittedSubclasses){
  System.out.println(sc.getName());
}
Conclusion:
Computer Mobile

2.isSealed()

This returns true if the class or interface in which it is called is sealed. That's all for now about the sealed classes added in Java 17. I hope this article was informative.

4 Ways to Implement Singleton

Source: Medium Today you will learn several ways to implement the Singleton design pattern. Singleton design pattern is widely used in Java projects. It provides access control to resources, such as a socket or database connection. I was once asked to implement a singleton during an interview for a web developer position at a large chip company. This was my first time interviewing for a web position, and I didn't do much preparation, so I chose the most difficult solution: Lazy instantiation. My code was only 90% correct and not efficient enough, I ended up losing in the final round... So I hope my article will be useful to you.

Early Instantiation

class Singleton {
    private Singleton() {}
    private static Singleton instance = new Singleton();

    public static Singleton getInstance() {
        return instance;
    }
}
Since the object is already created upon initialization, there is no thread safety issue here, but it does waste memory resources if no one is using it.

Lazy implementation

class Singleton {
    private static Singleton instance = null;
    private Singleton() {}
    public static Singleton getInstance() {
        if (instance == null) {
            instance = new Singleton();
        }
        return instance;
    }
}
When using a lazy initialization pattern, the object is created on demand. However, this method has a thread safety problem: if two threads are started on line 5 at the same time, they will create two Singleton instances. To avoid this, we need to add a lock:
class Singleton {
    private Singleton() {}
    private static Singleton instance = null;

    public static Singleton getInstance() {
        if (instance == null) {
            synchronized(Singleton.class) {
                if (instance == null) {
                    instance = new Singleton();
                }
            }
        }
        return instance;
    }
}
Double-Checked Locking (DCL): There is no locking on line 6, so this line will work very quickly if the object has already been created. Why do we need to double-check instance == null ? Because perhaps there are two threads introduced on line 7: the first one initiated the object, the second one is waiting for the Singleton.class lock . If there is no check, then the second thread will again recreate the singleton object. However, this method is still dangerous for threads. Line 9 can be divided into three lines of byte code:
  1. Allocate memory.
  2. Init object.
  3. Assign object to instance reference.
Because the JVM can run out of order, the virtual machine may assign an object to an instance reference before initialization. Another thread already sees the != null instance , it will start using it and cause a problem. So we need to add volatile to the instance, then the code becomes like this:
class Singleton {
    private Singleton() {}
    private volatile static Singleton instance = null;

    public static Singleton getInstance() {
        if (instance == null) {
            synchronized(Singleton.class) {
                if (instance == null) {
                    instance = new Singleton();
                }
            }
        }
        return instance;
    }
}

Using a static inner class

public class Singleton {
  private Singleton() {}
  private static class SingletonHolder {
    private static final Singleton INSTANCE = new Singleton();
  }
  public static final Singleton getInstance() {
    return SingletonHolder.INSTANCE;
  }
}
SingletonHolder is a static inner class, it is only initialized when the getInstance method is called . The init class in the JVM will run <clinit> cmd , then the JVM itself will make sure that only one thread can call <clinit> on the target class, other threads will wait.

Enum as a singleton

public enum EnumSingleton {
    INSTANCE;
    int value;
    public int getValue() {
        return value;
    }
    public int setValue(int v) {
        this.value = v;
    }
}
By default, an enum instance is thread-safe, so there's no need to worry about double-checked locking, and it's fairly easy to write.
Comments
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION