Showing posts with label limitation. Show all posts
Showing posts with label limitation. Show all posts

Tuesday, February 16, 2021

Restrictions with record classes

In a previous post, we saw how a record class gets converted to a regular class definition during compilation. Though that conceptually shows how a record class behaves, it doesn't represent the conversion accurately. 

Lets see how exactly a record class is getting converted during compilation. We will use the same simple, single line Cat record definition for this analysis

record Cat(String color) {}

Lets compile this record class using the below command

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

This will generate the Cat.class file in the same folder. We will analyze this class using reflection. For this purpose, we will use the below reflect utility - which prints out the basic details about the class to the console.

import java.lang.reflect.Modifier;

public class ReflectClass {

    public static void reflect(Class<?> ref) {
        log("Name",ref.getName());
        log("Module",ref.getModule().getName());
        log("PackageName",ref.getPackageName());
        log("Superclass",ref.getSuperclass().getName());
        log("Modifiers",Modifier.toString(ref.getModifiers()));
        log("DeclaredConstructors", ref.getDeclaredConstructors());
        log("DeclaredFields", ref.getDeclaredFields());
        log("DeclaredMethods", ref.getDeclaredMethods());
        
    }

    static void log(String name, Object[] values) {
        log(String.format("****%s****", name));
        int i = 0;
        for (Object val:values) {
            log(name+"-"+i++, val.toString());
        }
    }

    static void log(String name, String value) {
        log(String.format("%-25s: %s", name, value));
    }

    static void log(String data) {
        System.out.println(data);;
    }
    
    public static void main(String args[]) throws ClassNotFoundException {
        reflect(Class.forName(args[0]));
    }

}

Now lets try to analyze the Cat class using this utility by running the below command 

java --enable-preview ReflectClass Cat

This generates the output shown below

Name                     : Cat
Module                   : null
PackageName              :
Superclass               : java.lang.Record
Modifiers                : final
****DeclaredConstructors****
DeclaredConstructors-0   : Cat(java.lang.String)
****DeclaredFields****
DeclaredFields-0         : private final java.lang.String Cat.color
****DeclaredMethods****
DeclaredMethods-0        : public final boolean Cat.equals(java.lang.Object)
DeclaredMethods-1        : public final java.lang.String Cat.toString()
DeclaredMethods-2        : public final int Cat.hashCode()
DeclaredMethods-3        : public java.lang.String Cat.color()

Key details from the output highlighted in yellow. Inferences from this output are

1. The generated Cat class extends java.lang.Record. All record classes extend this class. It implies that a record class cannot extend any other class

2. Also we see the class generated is final - indicating that this record class cannot be extended by any other class

3. The record components gets converted to private final instance variables and there are not setter methods. The record components are immutable and cannot be changed after initialization. Note however that this immutability is shallow and reference attributes within a component may be mutable

4. A significant difference to note when working records is the deviation from the Java convention that the public accessor method is named color() and not getColor(). The generated accessor method does not follow the getter convention

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


Friday, February 12, 2021

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