jeudi 1 juin 2017

Sealed classes + Java reflection

I have the following sealed class in a Kotlin project:

sealed class Test {

    abstract val test: String

    @NoArg
    data class Test1(
            override val test: String
    ) : Test()

    @NoArg
    data class Test2(
            override val test: String
    ) : Test()
}

The @NoArg annotation is a marker and the compiler is configured to generate a no argument constructor for these classes.

My problem is that trying to instantiate Test1 and Test2 using Java reflection results in the following exception:

Exception in thread "main" java.lang.reflect.InvocationTargetException
    at sun.reflect.NativeConstructorAccessorImpl.newInstance0(Native Method)
    at sun.reflect.NativeConstructorAccessorImpl.newInstance(NativeConstructorAccessorImpl.java:62)
    at sun.reflect.DelegatingConstructorAccessorImpl.newInstance(DelegatingConstructorAccessorImpl.java:45)
    at java.lang.reflect.Constructor.newInstance(Constructor.java:423)
    at MainKt.main(Main.kt:27)
Caused by: java.lang.IllegalAccessError: tried to access method Test.<init>()V from class Test$Test1
    at Test$Test1.<init>(Main.kt)
    ... 5 more

I initially encountered this problem when trying to load instances of these classes from a Mongo database using Spring Data Mongo.

The code I used to instantiate them during this test is the following:

val javaConstructor = Test.Test1::class.java.getConstructor()
        ?: error("Did not find constructor")

val instance = javaConstructor.newInstance()

My current workaround is to just not use a sealed class which solves the Exception. Is there a way I can keep the sealed class and make Java reflection work correctly? I don't think this is quite the intended behaviour, but maybe there is some sort of reason to this? If so, I'd be interested to hear what it is.

I have an example of my problem here: http://ift.tt/2srtbyy





Aucun commentaire:

Enregistrer un commentaire