Kotlin/Java generics: when to expect an IllegalAccessException during instantiation?

Viewed 162

Please consider the following classes

open class BaseClass

class MyClass private constructor(string: String): BaseClass()

And a generic function that creates an instance:

inline fun <reified T : BaseClass, reified A : Any> create(arg: A): T = T::class.java.getConstructor(arg::class.java).newInstance(arg)

The following test fails:

@Test(expected = IllegalAccessException::class)
fun `should throw an IllegalAccessException`() {
    val test: MyClass = create("test")
}

because a java.lang.NoSuchMethodException is actually thrown. The JavaDoc for Constructor.newInstance() states:

@exception IllegalAccessException if this {@code Constructor} object is enforcing Java language access control and the underlying constructor is inaccessible.

A private constructor matches my expectation of an "inaccessible constructor". Why does this example throw a NoSuchMethodException instead and under which circumstances can it throw an IllegalAccessException?

1 Answers

The getConstructor method will try to pick a constructor from a list of public constructors only, private constructors won't be considered. Since it can't find a public match it throws NoSuchMethodException.

IllegalAccessException, on the other hand, will be thrown by newInstance method if you use getDeclaredConstructor instead because this particular method picks the constructor from all available constructors, not only the public ones, so the private constructor in your example would be retrieved, though not accessible.

The below will throw IllegalAccessException:

T::class.java.getDeclaredConstructor(arg::class.java).newInstance(arg)

If for some reason, you would like to overcome that, you could use something like:

val ct = T::class.java.getDeclaredConstructor(arg::class.java)
ct.trySetAccessible()
return ct.newInstance(arg)
Related