I have a Java method that accepts Class parameter. I need to pass Integer.class to it, but from Kotlin code. I tried Int::class.java, however this does not work, because int.class is passed to the function. My question is, how do I access Integer.class from Kotlin.
Java
void foo(Class clazz);
Kotlin
foo(Int::class.java) // does not work, int.class gets passed to foo
You must use the KClass#javaObjectType to get the wrapper type class in Kotlin:
Returns a Java Class instance corresponding to the given KClass instance. In case of primitive types it returns corresponding wrapper classes.
For example:
// v--- java.lang.Integer
println(Int::class.javaObjectType)
// v--- int
println(Int::class.java)
Related
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.
I am trying to understand concept of inline classes - they are a simple object wrapper of single property that is being inlined during runtime.
That means, that the actual initialization of the class is not happening at runtime
I was trying to write simple test which directly will show my above explanation during JUnit test as below:
companion object {
private const val NAME = "JACK"
}
inline class NameInlineClass(val value: String)
#Test
fun unwrapping() {
val nameInlineClass = NameInlineClass(NAME)
val name = nameInlineClass
assertEquals(name, NAME)
}
This test fails unfortunately which leads me to the question why during assertEquals() the actual unwrapped String value is not being compared, but the actual inline class (which should be unwrapped during runtime)?
What you probably wanted to do was val name = nameInlineClass.value, but I'll try to explain the error.
See Representation from docs (includes code sample):
In generated code, the Kotlin compiler keeps a wrapper for each inline
class. Inline class instances can be represented at runtime either as
wrappers or as the underlying type. This is similar to how Int can be
represented either as a primitive int or as the wrapper Integer.
That means as long as you don't reference the wrapping object or its type explicitly, value will not be boxed. We can check it by inspecting bytecode (decompiled back to Java for readability):
// kotlin source
fun unwrapping() {
val nameInlineClass = NameInlineClass(NAME)
val name = nameInlineClass // this line gets dropped by compiler by the way
assertEquals(name, NAME)
}
// java representation of bytecode
public final void unwrapping() {
String nameInlineClass = NameInlineClass.constructor-impl("JACK");
Assert.assertEquals(NameInlineClass.box-impl(nameInlineClass), "JACK");
}
I won't paste entire generated NameInlineClass body, but constructor-impl is static method that only checks for null of value, and box-impl creates the wrapper object.
You can see nameInlineClass is indeed a String - that means inline works and no extra object was allocated.
Only when you reference nameInlineClass instead of nameInlineClass.value compiler determines that this object needs representation and "boxes" the value with wrapper NameInlineClass class.
kotlin file
interface Test {
fun test(message: String, delay: Int =100)
}
class A: Test
{
override fun test(message: String, delay: Int) {
}
}
I find i can't use #JvmOverloads in interface nor class.
if i add a #JvmOverloads in interface,the error is #JvmOverloads annotation cannot be used on interface method,if i add #JvmOverloads in class,the error is platform declaration clash....
However, I seem able to use defaults paramters in kotlin files,like this.
var a=A()
a.test("1234")
But when I use it in a java file, it seems that the method is not overloaded。
A a=new A();
a.test("123");//Compile error
The following version without interface can work
class A
{
#JvmOverloads
fun test(message: String, delay: Int=100) {
}
}
Then I can use it normally in java file
A a=new A();
a.test("123");
But how to maintain the same functionality after add the interface?
This is not a bug. #JvmOverloads annotation simply does not work with abstract methods.
From Kotlin docs:
Normally, if you write a Kotlin function with default parameter values, it will be visible in Java only as a full signature, with all parameters present. If you wish to expose multiple overloads to Java callers, you can use the #JvmOverloads annotation.
The annotation also works for constructors, static methods etc. It can't be used on abstract methods, including methods defined in interfaces.
source: https://kotlinlang.org/docs/reference/java-to-kotlin-interop.html#overloads-generation
Why?
Because as You can learn from the doc I mentioned, #JvmOverloads instructs compiler to generate bunch of Java overloaded methods, omitting each of the parameters one by one, starting from the last one.
As far as I understand, each overloaded method calls internally method with one more parameter, and this additional parameter has default value. Edit: see comment by #hotkey here
This won't work with abstract methods, because they don't have any body.
Also new Java interface would have more methods, and its implementations would have to implement all of them. Kotlin interface had only one method.
To get to the same result you can make a LegacySupport class in Kotlin that will actually call the function with the default parameter and then you can expose only the return of the function to the java class from this class.
For example, FastUtil's IntArrayList has a push method that accepts both int (primitive) and Integer (boxed), but Kotlin sees these both as the same function push(Int), therefore I cannot use the function at all as the function is ambiguous.
What should I do when a Java library has overloads for both the primitive and boxed type?
(p.s. I am aware that I can use the add(int) method. I am in search of what to do if I come across such a problem in the future.)
Consider these methods in Java:
void f(int x) { }
void f(Integer y) { }
In Kotlin, they are seen as
f(x: Int)
f(x: Int!)
The second method has a parameter of platform type, meaning it might be nullable, corresponding to Integer.
First one can be simply called with Kotlin Int passed:
f(5) // calls f(int x)
To call the second one, you can cast the argument to nullable Int?, thus selecting the overload:
f(5 as Int?) // calls f(Integer y)
Have you tried writing a Java class to serve as an intermediary? That is, write your own java class with the method you want Kotlin to see, then call that method from your Kotlin code.
I'm trying to translate a class from java to kotlin which uses a lot of raw types and accesses the Class of objects.
How can i get this to work:
val item: Any = items[position]
item::class.java // compiler complains about "Unresolved reference: item"
You'll want to call item.javaClass. The reason is that you're not calling on a class literal, you're calling on an actual object instance.