Showing posts with label Java15. Show all posts
Showing posts with label Java15. Show all posts

Friday, February 26, 2021

Binding variable for instanceof operator

We know that instanceof operator tests a variable for a particular type. This is generally used when we have a variable of a super type and we want to check if its of a specific subtype before casting and invoking specific methods or attributes of that subtype

Here is the sample of instanceof usage

    public int getSizeOfString(Object o) {
        if (o instanceof String) {
            String s = (String) o;
            return s.length();
        } else {
            return 1;
        }
    }

We check if o is of type String. If so, we cast it to String, assign it to the variable s and then proceed to return the length of the string. Otherwise we return 1 as the default size.

Binding variable for instanceof operator introduced in Java 15, makes this casting and assignment to the local variable much more concise and less error prone. 

Below is this code using binding variable with instanceof operator

    public int getSizeOfString(Object o) {
        if (o instanceof String s) {
            return s.length();
        } else {
            return 1;
        }
    }

In the above code, s is the binding variable used with the instanceof operator. If Object o is of type String, it is cast to String and assigned to s as part of the single line statement. We can proceed to use s as a String variable holding the object assigned inside of the if block. 

As you can see, not only does this make the code concise, it also eliminates the possibility for errors due to wrong manual casting.

We will explore more scenarios of using binding variable with instanceof operator in the next few posts. 

A complete sample for binding variable usage

Scoping of binding variables

Binding variable scope with NOT (!) condition

Using binding variable in the expression of if statement


Sample code used in this post can be downloaded from https://github.com/ashokkumarta/awesomely-java/tree/main/2021/02/Language-Features/Instanceof-Binding-Variable/Usage

Friday, February 12, 2021

Sealed classes and reflection

Two new methods are added to java.lang.Class to examine if a Class or an Interface is a sealed class and if so, to find what are its supported subtypes. 

These two methods are 

1. isSealed - which returns a boolean indicating if the class is sealed or  not

2. permittedSubclasses- which returns an array of ClassDesc representing the supported sub classes

Below sample code demonstrates this

import java.lang.constant.ClassDesc;

public sealed interface Pet {
    
}

final class Cat implements Pet {

}

final class Dog implements Pet {
    
}

class Main {
    public static void main(String[] args) {
        System.out.println("Pet is a sealed class: "+Pet.class.isSealed());
        System.out.print("Its supported subtypes are: ");
        ClassDesc[] cDescArr = Pet.class.permittedSubclasses();
        for (ClassDesc cDesc : cDescArr) {
            System.out.print(cDesc.displayName()+" ");
        }
    }
}

The output of the above program is

Pet is a sealed class: true
Its supported subtypes are: Cat Dog

We will move on to the another new feature in Java 15 - Records in the next post

Sample code used in this post can be downloaded from https://github.com/ashokkumarta/awesomely-java/tree/main/2021/02/Language-Features/Sealed-classes/Reflection

Module and package constraint for permitted sub types

In sealed classes, this constraint apply.

That when you are using modules, the sealed class and its permitted sub types must be defined within the same module. 

If you are not using modules, the sealed class and its permitted sub types must be defined within the same package. 

The below code will not compile unless they are included as part of the same module

package samples.j15.sealed;

import samples.j15.sealed.impl.*;

public sealed interface Pet permits Cat {

}
package samples.j15.sealed.impl;

import samples.j15.sealed.Pet; 

public non-sealed class Cat implements Pet {

}

This is not allowed because the sealed class Pet and its permitted sub type Cat are in different packages - samples.j15.sealed and samples.j15.sealed.impl respectively. 

This code will compile only if these 2 packages are part of the same module. 

To include the above to packages, just create a module-info.java file at the root folder, with only the module name as in the following declaration 

module sealed.samples {
    
}

This will make the two packages part of the sealed.samples module. Now this code will compile because they are now part of the same module.

Sample code used in this post can be downloaded from https://github.com/ashokkumarta/awesomely-java/tree/main/2021/02/Language-Features/Sealed-classes/Module-and-package-constraint

Defining the sealed class and all its permitted types in a single file

Look at the below example in which the sealed interface and all the class that implements it are defined in the same source file

import java.util.Random;

public sealed interface Pet permits Cat, Dog {

    final Random random = new Random();
    
    default Pet myLuckyPet() {
        if(random.nextInt(2) == 0) {
            return new Cat();
        }
        return new Dog();
    }
}

final class Cat implements Pet {

}

final class Dog implements Pet {

}

Here Pet interface is a sealed type and permits only Cat and Dog to inherit from it. 

We define the implementing Cat and Dog classes in the same file. 

We also have a default method in Pet interface which returns a Pet instance (Cat or Dog) randomly. 

Nothing unusual about it. Restrictions of Java applies here as well, that Cat and Dog are package private and can be used only within the same package. If we want the Cat & Dog to be public, they would have to be defined in their own files.

But one advantage of defining a sealed type and its permitted types in the same file is that we can omit the permits clause in the Pet declaration. Java would infer it and include all the implementing types that are defined in the same source file as the set for permits types. 

public sealed interface Pet {

    final Random random = new Random();
    
    default Pet myLuckyPet() {
        if(random.nextInt(2) == 0) {
            return new Cat();
        }
        return new Dog();
    }
}

final class Cat implements Pet {

}

final class Dog implements Pet {

}

This code snippet behaves the same was as the 1st one. When defined in the same file, Java can infer the permitted types and we don't need to provide the permits clause with the list of allowed subtypes.

But when Permits is provided, full list of all permitted types must be specified include types defined in the same file and elsewhere.

Note however that when defining a sealed class without the permits clause, at least one subtype definition should be provided in the same file.  

Sample code used in this post can be downloaded from https://github.com/ashokkumarta/awesomely-java/tree/main/2021/02/Language-Features/Sealed-classes/Single-file

Thursday, February 11, 2021

Sealed Classes

In the next set of posts, lets see some of the features of Java language. 

We will start with sealed classes. This is introduced as a preview feature in Java15. 

So what is a sealed class? 

In java language we can have a final class or a non-final class (class not declared as final). A final class cannot be sub-classed. Non-final class can be extended by any class as long as the base class is visible to it. What if we want to define the entire class hierarchy and not leave it open for any class to extend our class hierarchy in the future?

Lets take a simple example. Lets say we run a pet shop and exclusively deal only with cats & dogs. We want to model this and define a class hierarchy for our case. We will have a base Pet interface, Cat & Dog class both implement the Pet interface. While we want to allow any no. of sub classes for Cat, we want to restrict Dog class to be extended only by the two types of dog we deal with. The hierarchy we want is as shown below




Now we don't want any other class or interface to implement or extend from Pet Interface. Similarly we don't want any other classes other than GermanShepard & LabradorRetriever to extend from Dog class. How can we control this? Sealed classes helps here.

Lets understand some keywords first

sealed: A class can be declared as sealed. It means that this class can be extended only by the set of classes mentioned in the permits clause of its definition

permits: Allows to specify a comma separated set of classes that are permitted to extend from the sealed class

non-sealed: When a class extends from a sealed class, it has to be declared as one of 

  1. final - it cannot be extended any further
  2. sealed - it itself is also sealed, with the set of classes that can extend from it declared in its own permits clause
  3. non-sealed - Specifies that the class hierarchy at this point is no longer sealed and any no. of classes can extend from it

Sample code that implements the type hierarchy shown in the above diagram is given below

public sealed interface Pet permits Cat, Dog {

}

public abstract non-sealed class Cat implements Pet {

}

public abstract sealed class Dog implements Pet permits GermanShepard, LabradorRetriever {

}

public final class GermanShepard extends Dog {

}

public final class LabradorRetriever extends Dog {

}

Here Pet is the interface which allows only Cat & Dog types to implement or extend it.

Cat & Dog are abstract classes. While Cat is non-sealed allowing any class to extend from it, Dog is sealed and allows only GermanShepard & LabradorRetriever classes to extend from it.

GermanShepard & LabradorRetriever are final classes and would prevent any other class to extend from it.

Sealed classes helps impose tight control over type hierarchy that we want to have. Though this feature is commonly referred to as sealed classes, it applies to interfaces as well.

Note: This is a preview feature in Java 15. To compile the above code, use the command with --enable-preview flag as shown below

javac --enable-preview --release 15 *.java

And to run code that uses preview features, run the application with --enable-preview option like

java --enable-preview Main

If you are using Eclipse, enable preview feature by selecting this option


We will get into detailed usage of sealed classes next

Sample code used in this post can be downloaded from https://github.com/ashokkumarta/awesomely-java/tree/main/2021/02/Language-Features/Sealed-classes/Sealed-classes