jeudi 17 mars 2022

Why java allows change blank final fields by reflection but not final fields?

I've created this code to show the unexpected behavior:

package reflection;

import java.lang.reflect.Field;
import java.lang.reflect.Modifier;
import java.util.Arrays;


public class App {
    public static void main(String[] args) throws NoSuchFieldException, IllegalAccessException {
        Person person = new Person();
        System.out.println("Fields of person object: ");
        Arrays.stream(person.getClass().getDeclaredFields()).forEach(System.out::println);

        final Field finalField = person.getClass().getDeclaredField("finalName");
        final Field blankFinalField = person.getClass().getDeclaredField("blankFinalName");

        System.out.println("\nOriginal modifiers and accessibility: ");
        printAccessibilityAndModifiers(person, finalField);
        printAccessibilityAndModifiers(person, blankFinalField);

        // Changes the access to reflected object to accesible.
        // You can see that it doesn't change modifiers.
        finalField.setAccessible(true);
        blankFinalField.setAccessible(true);
        System.out.println("\nAfter set accessible to true: " + person);
        printAccessibilityAndModifiers(person, finalField);
        printAccessibilityAndModifiers(person, blankFinalField);

        // Removing final modifier.
        removeModifier(finalField, Modifier.FINAL);
        removeModifier(blankFinalField, Modifier.FINAL);

        System.out.println("\nAfter remove final modifier: " + person);
        printAccessibilityAndModifiers(person, finalField);
        printAccessibilityAndModifiers(person, blankFinalField);

        finalField.set(person, "Dilson");
        blankFinalField.set(person, "Dilson");
        System.out.println("\nAfter change the value of field: " + person);

        printAccessibilityAndModifiers(person, finalField);
        printAccessibilityAndModifiers(person, blankFinalField);

        // For final field, even removing final modifier, the field keeps unchanged.
        System.out.println("\nThe value returned by getter of the instance: " + person);
        System.out.println(finalField.getName() + ": " + person.getFinalName());
        System.out.println(blankFinalField.getName() + ": " + person.getBlankFinalName());
    }

    private static void printAccessibilityAndModifiers(Object instance, Field field) {
        System.out.println(field.getName()
                + " - Value: " + getValue(instance, field)
                + " - Is Accessible: " + field.canAccess(instance)
                + " - Modifiers: " + Modifier.toString(field.getModifiers()));
    }

    private static Object getValue(Object instance, Field field) {
        try {
            return field.get(instance);
        } catch (IllegalAccessException e) {
            return "Can't access " + field.getName() + " value.";
        }

    }

    private static void removeModifier(Field field, int modifier) throws NoSuchFieldException, IllegalAccessException {
        Field modifiersField = Field.class.getDeclaredField("modifiers");
        modifiersField.setAccessible(true);
        modifiersField.setInt(field, field.getModifiers() & ~modifier);
    }
}
import lombok.Data;

@Data
public class Person {
    private final String finalName = "Leandro";
    private final String blankFinalName;

    public Person() {
        blankFinalName = "Leandro";
    }
}

I would like to understand deeply what makes Java behaves differently in these situations.





Aucun commentaire:

Enregistrer un commentaire