mardi 22 novembre 2022

Java 17 reflection: assign the "this" reference of a lambda object

This code runs fine in java 11.

package test;

import org.junit.Assert;
import org.junit.Test;

import java.lang.reflect.Field;
import java.util.function.Consumer;

public class LambdaArg1ReflectionTest {
    @Test
    public void test() throws IllegalAccessException {
        // Create a lambda with reference to parent class (this)
        Consumer<String> lambda = s -> this.parseInt(s, 7);

        // Get the value from the lambdas "this" reference
        Field lambda_arg1_field = lambda.getClass().getDeclaredFields()[0];
        lambda_arg1_field.setAccessible(true);
        Object lambda_arg1_value = lambda_arg1_field.get(lambda);
        // Assert that the value is as we expected
        Assert.assertEquals(this, lambda_arg1_value);

        // Attempt to assign the same value
        lambda_arg1_field.set(lambda, lambda_arg1_value);
    }

    private int parseInt(String s, int i) {
        return Integer.parseInt(s + i);
    }
}

But, in java 17 it throws an exception:

    java.lang.IllegalAccessException: Can not set final test.LambdaArg1ReflectionTest field test.LambdaArg1ReflectionTest$$Lambda$40/0x0000000800c191a8.arg$1 to test.LambdaArg1ReflectionTest

    at java.base/jdk.internal.reflect.UnsafeFieldAccessorImpl.throwFinalFieldIllegalAccessException(UnsafeFieldAccessorImpl.java:76)
    at java.base/jdk.internal.reflect.UnsafeFieldAccessorImpl.throwFinalFieldIllegalAccessException(UnsafeFieldAccessorImpl.java:80)
    at java.base/jdk.internal.reflect.UnsafeQualifiedObjectFieldAccessorImpl.set(UnsafeQualifiedObjectFieldAccessorImpl.java:79)
    at java.base/java.lang.reflect.Field.set(Field.java:799)
    at test.LambdaArg1ReflectionTest.test(LambdaArg1ReflectionTest.java:23)
  • Why?
  • Is it not supposed to work in java 17?
  • Is there any way to work around this?

Obviously this is an example. What the real code attempts to do is to replace the lambdas this reference with a spy object, invoke the lambda and then capture the arguments given to this.parseInt. Ultimately what it does is to serialize the first method invocation of a lambda.





Aucun commentaire:

Enregistrer un commentaire