Given a domain class with a parameterless constructor, how do we get a reference to that constructor through the Reflection API?
Consider for example a Student data class, such as:
data class Student(var nr: Int = 0, var name: String? = null)
Notice, we can confirm the presence of the parameterless constructor through javap that shows:
public pt.isel.Student(int, java.lang.String);
descriptor: (ILjava/lang/String;)V
public pt.isel.Student(int, java.lang.String, int, kotlin.jvm.internal.DefaultConstructorMarker);
descriptor: (ILjava/lang/String;ILkotlin/jvm/internal/DefaultConstructorMarker;)V
public pt.isel.Student();
descriptor: ()V
Yet, none of the following approaches returns the parameterless constructor:
Approach 1 - primaryConstructor:
val constructor = Student::class.primaryConstructor
Approach 2 - All constructors declared in the class:
// java.util.NoSuchElementException: Collection contains no element matching the predicate
val constructor = Student::class.constructors.first { it.parameters.isEmpty() }
Alternatively, we can proceed via Java Reflection that works fine, but it should not be necessary such detour:
val constructor = Student::class.java.getDeclaredConstructor()
Second, why do we need that? Because we want to instantiate a domain class at runtime. Yes, we know that createInstance() of KClass do that job. But it throws IllegalArgumentException: Class should have a single no-arg constructor if there is no parameterless constructor.
Thus, we would like to check before-hand if we could call the createInstance() with no exceptions.
The parameterless constructor here only exists in the compiled Java class, and not in your Kotlin code. As far as Kotlin code is concerned, your Student class has one single constructor, with 2 optional parameters.
The Kotlin reflection API is designed to be platform-independent, so you have to use Java reflection to get the parameter constructor.
If you just want to see if you can call createInstance safely, you can just check if the class has a single constructor whose parameters are all optional. This is documented:
Creates a new instance of the class, calling a constructor which either has no parameters or all parameters of which are optional. If there are no or many such constructors, an exception is thrown.
val isSafe = someClass.constructors.singleOrNull {
it.parameters.all(KParameter::isOptional)
} != null
This is similar to how createInstance is implemented to throw the exception.
Related
I'm trying to inherit the JFrame class. It says "This type has a constructor, and thus must be initialized here" so I can't write:
class MainFrame : JFrame
but:
class MainFrame() : JFrame()
Since I'm forced to declare a primary constructor I can't do this:
constructor(title: String) : super(title)
And I have to do this:
constructor(title: String) : this(title)
So I have to declare the primary constructor this way:
class MainFrame(title: String) : JFrame(title)
The problem is that this way every secondary constructor needs to call the primary constructor and then the supertype constructor of choice. If I have multiple supertype constructors I'm forced to delegate the process to that single supertype constructor that can be inconvenient if the supertype class has many constructors for many purposes.
There is some way to make a class with multiple constructors that call different supertype constructors?
Edit:
I can't remove the supertype constructor like this:
class MainFrame : JFrame
If I do I get this error:
This type has a constructor, and thus must be initialized here
Solution:
A constructor was missing but it can be a secondary constructor, so the error:
This type has a constructor, and thus must be initialized here
Can be solved by adding only a secondary constructor.
It is not necessary to use a primary constructor in your class definition. You can simply omit it, and then all your "secondary" constructors do not have to call a primary constructor, and can call the relevant super constructor instead.
class MainFrame: JFrame {
constructor() : super() {
}
constructor(title: String) : super(title) {
}
}
JFrame, does however follow the pattern of having an unofficial primary constructor in Java, so you could call through to this as your primary constructor and use the same defaults it uses:
class MainFrame(title: String = "", gc: GraphicsConfiguration? = null): JFrame(title, gc) {
}
It means that, by the time of the base class constructor execution, the properties declared or overridden in the derived class are not yet initialized. If any of those properties are used in the base class initialization logic (either directly or indirectly, through another overridden open member implementation), it may lead to incorrect behavior or a runtime failure. When designing a base class, you should therefore avoid using open members in the constructors, property initializers, and init blocks.
I was studying Inheritence from Kotlin docs, and I got stuck here. There was another post which asked a question about this, but the answers were just what the docs said in a different way.
To be clear, I understood the data flow between constructors and inheritence. What I couldn't understand was how we can use an overridden property in a base class initialization. It says
It could happen directly or indirectly
What does this actually mean? How can the base class can somehow access to the overridden property in the derived class?
Also, it said
You should therefore avoid using open members in the constructors,
property initializers and init blocks.
So how can we properly use open properties?
EDIT FOR THE COMMENT:
fun main ()
{
val d = Derived("Test2")
}
open class Base()
{
open val something:String = "Test1"
init
{
println(something) //prints null
}
}
class Derived(override val something: String): Base()
What does this actually mean? How can the base class can somehow access to the overridden property in the derived class?
This is one direct way:
abstract class Base {
abstract val something: String
init {
println(something)
}
}
class Child(override val something: String): Base()
fun main() {
Child("Test") // prints null! because the property is not initialized yet
}
This prints null, which is pretty bad for a non-nullable String property.
You should therefore avoid using open members in the constructors, property initializers and init blocks.
So how can we properly use open properties?
You can use these properties in regular methods on the base class (or in custom property getters):
abstract class Base {
abstract val something: String
fun printSomething() {
println(something)
}
}
class Child(override val something: String): Base()
fun main() {
Child("Test").printSomething() // correctly prints "Test"
}
EDIT: Here are some clarifications regarding the follow-up questions in the comments.
I couldn't quite get why the code in the init block went for the parameter in the child class constructor
I think you might be confused by Kotlin's compact syntax for the primary constructors in general, which probably makes the debugger's flow hard to understand. In the Child declaration, we actually declare many things:
the argument something passed to the Child's primary constructor
the property something on the Child class, which overrides the parent's one
the call to the parent constructor (Base())
When Child() is called, it immediately calls the Base() no-arg constructor, which runs the init block.
We didn't even delegate the base constructor with a parameter or anything, but it still went for the parameter who did the overriding
You might be mixing declarations and runtime here. Although we declare things in the Base class and in the Child class, there is only 1 instance at runtime (an instance of Child) in this example code.
So, in fact, there is only 1 property called something here (only one place in memory). If the init block accesses this property, it can only be the property of the child instance. We don't need to pass anything to the Base constructor because the init block is effectively executed with the data/fields of the Child instance.
Maybe you would be less confused if you saw the Java equivalent of this. It's obvious if you think of the abstract something as a declaration of a getter getSomething(). The child class overrides this getSomething() method and declares a private something field, the getter returns the current value of the field something. But that field is only initialized after the constructor of the parent (and the init block) finished executing.
Using ByteBuddy how can I create enum with constructors such as this one :
public enum EnumConstructorSample {
STATE1(10),
STATE2(15);
public int count;
EnumConstructorSample(int count){
this.count = count;
}
}
I tried this code and it gives me error.
Class enumClass = new ByteBuddy().makeEnumeration("STATE1", "STATE2")
.name("DynamicEnum")
.defineConstructor(Visibility.PACKAGE_PRIVATE)
.withParameters(int.class)
.intercept(FixedValue.value(1))
.make()
.load(EnumWithConstructor.class.getClassLoader(), ClassLoadingStrategy.Default.WRAPPER)
.getLoaded();
System.out.println(enumClass.getDeclaredConstructors()[0]);
This is the Error and it is happening in enumClass.getDeclaredConstructors()
Exception in thread "main" java.lang.VerifyError: Constructor must call super() or this() before return
Exception Details:
Location:
DynamicEnum.<init>(I)V #2: return
Reason:
Error exists in the bytecode
Bytecode:
0x0000000: 0457 b1
at java.lang.Class.getDeclaredConstructors0(Native Method)
at java.lang.Class.privateGetDeclaredConstructors(Class.java:2671)
at java.lang.Class.getDeclaredConstructors(Class.java:2020)
at EnumWithConstructor.main(EnumWithConstructor.java:19)
For constructors, it is required to invoke the super method within the constructor. For enumerations, you'd need to invoke the Enum(String, int) constructor. You can implement this using MethodCall.invoke(...).onSuper().
If you wanted to achieve this, I'd recommend you to subclass Enum manually since you'd otherwise define multiple constructors for the enum you are creating where Byte Buddy would invoke its own enum constructor and the fields would all have its default value.
Rather, implement the method and return the value based on its name. You can for example use a MethodDelegation and then use a #This Enum<?> val injection where you switch over the name to return the correct value as if it was stored in a field.
I'm creating a builder (for Java compat), where context is both a private property and public method.
private lateinit var context: Context
fun context(appContext: Context) = apply {
context = appContext
}
fun build(): MySdk {
// this::context fails to compile because it cannot differentiate between the
// method `context()` vs property `context`
require(this::context.isInitialized) {
"context == null"
}
But I get a compilation issue for ::context.isInitialized, because it cannot differentiate between the method context() vs property context
Does Kotlin have a workaround for this? or am I forced to use unique property/method names?
This is a case of overload resolution ambiguity and the kotlin compiler is unable to identify whether you are using the property or the method.
This is because of callable references (::) . Internally when you are using the callable references it calls a method.
Callable references : References to functions, properties, and
constructors, apart from introspecting the program structure, can also
be called or used as instances of function types.
The common supertype for all callable references is KCallable, where R is the return value type, which is the property type for properties, and the constructed type for constructors.
KCallable<out R> // supertype for all callable references
So, for function the type is KFunction and for properties the type is KProperty
interface KFunction<out R> : KCallable<R>, Function<R> (source)
interface KProperty<out R> : KCallable<R> (source)
When you use a function like :
fun context(appContext: Context) = apply {
context = appContext
}
It can be used as a Function reference
::context // This is a Function reference i.e. KFunction
When you use a property reference, like
private lateinit var context: Context
fun something(){
::context // this is a property reference, KProperty
}
A property reference can be used where a function with one parameter is expected:
val strs = listOf("a", "bc", "def")
println(strs.map(String::length))
So, its not that Kotlin forces you to use different property and function names("although it is not recommended"). Its just that its unable to differentiate in this case as
Both are KCallable and have the same name
A property reference can be used where a function with one parameter is expected
You can resolve the ambiguity between the property and the method by giving the expected type:
val prop: kotlin.reflect.KProperty0<*> = this::context
Alas, prop.isInitialized then gives a compilation error:
This declaration can only be called on a property literal (e.g. 'Foo::bar')
So this doesn't appear to be possible currently. OTOH, since the error shows isInitialized is already handled specially by the compiler, it's likely possible to fix; I suggest reporting it on http://youtrack.jetbrains.com/ (after searching for duplicates).
open class Super {
open var name : String = "Name1"
init {
println("INIT block fired with : $name")
name = name.toUpperCase()
println(name)
}
}
class SubClass(newName : String) : Super() {
override var name : String = "Mr. $newName"
}
fun main(args: Array<String>) {
var obj = SubClass("John")
println(obj.name)
}
The above Kotlin code results in the following TypeCastException :
INIT block fired with : null
Exception in thread "main" kotlin.TypeCastException: null cannot be cast to non-null type java.lang.String
at Super.<init>(index.kt:7)
at SubClass.<init>(index.kt:13)
at IndexKt.main(index.kt:21)
As my understanding goes while inheriting from a class in Kotlin, first the primary constructors and init blocks and secondary constructors of super classes are called with passed parameters. After which the subclass can override such properties with its own version.
Then why does the above code results in the exception as described ... What am I doing wrong ... Why does the init block in super class is fired with null ...??? At first my speculation was that the init block might get fired before the actual property initialization as it is executed as a part of primary constructor but initializing the name property in the primary constructor as below gives the same error and the IDE would have warned me if so.
open class Super(open var name : String = "Name1") {
init {
println("INIT block fired with : $name")
name = name.toUpperCase()
println(name)
}
}
class SubClass(newName : String) : Super() {
override var name : String = "Mr. $newName"
}
fun main(args: Array<String>) {
var obj = SubClass("John")
println(obj.name)
}
Console :
INIT block fired with : null
Exception in thread "main" kotlin.TypeCastException: null cannot be cast to non-null type java.lang.String
at Super.<init>(index.kt:5)
at Super.<init>(index.kt:1)
at SubClass.<init>(index.kt:11)
at IndexKt.main(index.kt:19)
Am I doing something wrong here or is this a language bug...??? What can I do to avoid the error and to make the init blocks fire with the actual passed value and not null ... ??? Elaborate what is happening behind the scene. At this time I have several situations with classes like this in my actual codebase where I want to inherit from another classes, I want to maintain property names as they are...
Essentially, because you tell Kotlin that your subclass is going to be defining name now, it is not defined when the init block in Super is executed. You are deferring definition of that until the SubClass is initialized.
This behavior is documented on the Kotlin website under "Derived class initialization order":
During construction of a new instance of a derived class, the base class initialization is done as the first step (preceded only by evaluation of the arguments for the base class constructor) and thus happens before the initialization logic of the derived class is run.
...
It means that, by the time of the base class constructor execution, the properties declared or overridden in the derived class are not yet initialized. If any of those properties are used in the base class initialization logic (either directly or indirectly, through another overridden open member implementation), it may lead to incorrect behavior or a runtime failure. Designing a base class, you should therefore avoid using open members in the constructors, property initializers, and init blocks. [emphasis mine]
FWIW, this is similar to the reason that some Java code analysis tools will complain if you refer to non-final methods in a constructor. The way around this in Kotlin is to not refer to open properties in your init blocks in the superclass.
Have the same trouble, a disgusting issue with kotlin, when subclass constructor is ignored or initialized after super class calls internal method, this is not a safe thing, if not worst i found in kotlin.