What's the difference between Foo::class.java and Foo::javaClass? - kotlin

To initialize my logger apparently I need:
val LOGGER : Logger = LoggerFactory.getLogger(Foo::class.java);
If I do:
val LOGGER : Logger = LoggerFactory.getLogger(Foo::javaClass);
It complains that the parameter type is not compatible with getLogger. However according to the API, both are Class<Foo>. How are they different?

The javaClass is an extension property that returns the runtime Java class of an instantiated object. In your case, it is being used as a property reference, which will give you a KProperty1<Foo, Class<Foo>> representing the extension function itself:
val T.javaClass: java.lang.Class<T>
You could use this in combination with a receiver, e.g. if Foo provided a default constructor you could say:
Foo::javaClass.get(Foo())
which may be simplified to:
Foo().javaClass
Using ::class.java on the other hand, gives you the Java Class<?> as described in "class references" directly. All three possibilities in a simple example:
val kProperty1: KProperty1<Foo, Class<Foo>> = Foo::javaClass
kProperty1.get(Foo()) //class de.swirtz.kotlin.misc.Foo
Foo::class.java //class de.swirtz.kotlin.misc.Foo
Foo().javaClass //class de.swirtz.kotlin.misc.Foo

javaClass is an extension property which returns the runtime Java class of an object.
/**
* Returns the runtime Java class of this object.
*/
public inline val <T: Any> T.javaClass : Class<T>
#Suppress("UsePropertyAccessSyntax")
get() = (this as java.lang.Object).getClass() as Class<T>
It can be called on an instance of a class, for example:
println(Foo().javaClass) //class Foo
However, Foo::javaClass give you a property reference of type KProperty1<Foo, Class<Foo>> instead of a Java class instance which can be used to get the class of an instance of Foo through reflection:
val p: KProperty1<Foo, Class<Foo>> = Foo::javaClass
println(p.get(Foo())) //p.get(Foo()) returns a Java class Foo
Therefore, it is wrong to pass a KProperty to LoggerFactory.getLogger() which accepts a Java class.

Foo::javaClass is a reference to a val defined as
inline val <T : Any> T.javaClass: Class<T>
So you'd have to call it on an instance of Foo like foo.javaClass.
Foo::class gives you the actual KClass of Foo and java is a property of KClass defined as
val <T> KClass<T>.java: Class<T>

Related

Instantiating classes from non-reified type parameters

I'm building an ORM for use with jasync-sql in Kotlin and there's a fundamental problem that I can't solve. I think it boils down to:
How can one instantiate an instance of a class of type T, given a
non-reified type parameter T?
The well known Spring Data project manages this and you can see it in their CrudRepository<T, ID> interface that is parameterised with a type parameter T and exposes methods that return instances of type T. I've had a look through the source without much success but somewhere it must be able to instantiate a class of type T at runtime, despite the fact that T is being erased.
When I look at my own AbstractRepository<T> abstract class, I can't work out how to get a reference to the constructor of T as it requires accessing T::class.constructors which understandably fails unless T is a reified type. Given that one can only used reified types in the parameters of inline functions, I'm a bit lost as to how this can work?
On the JVM, runtime types of objects are erased, but generic types on classes aren't. So if you're working with concrete specializations, you can use reflection to retrieve the type parameter:
import java.lang.reflect.*
​
abstract class AbstractRepository<T>
​
#Suppress("UNCHECKED_CAST")
fun <T> Class<out AbstractRepository<T>>.repositoryType(): Class<T> =
generateSequence<Type>(this) {
(it as? Class<*> ?: (it as? ParameterizedType)?.rawType as? Class<*>)
?.genericSuperclass
}
.filterIsInstance<ParameterizedType>()
.first { it.rawType == AbstractRepository::class.java }
.actualTypeArguments
.single() as Class<T>
​
class IntRepository : AbstractRepository<Int>()
class StringRepository : AbstractRepository<String>()
interface Foo
class FooRepository : AbstractRepository<Foo>()
class Bar
class BarRepository : AbstractRepository<Bar>()
​
fun main() {
println(IntRepository::class.java.repositoryType())
println(StringRepository::class.java.repositoryType())
println(FooRepository::class.java.repositoryType())
println(BarRepository::class.java.repositoryType())
}
class java.lang.Integer
class java.lang.String
interface Foo
class Bar
In your own CrudRepository you can add a companion object with an inline fun which is responsible to instantiate your repository by passing to it the corresponding class.
class MyCrudRepository<T> protected constructor(
private val type: Class<T>,
) {
companion object {
inline fun <reified T : Any> of() = MyCrudRepository(T::class.java)
}
fun createTypeInstance() = type::class.createInstance()
}

syntax for generic-parameterized variable/constant

I'm trying to create a Map that contains generic-parameterized types. For example:
abstract class Foo {
companion object {
val fooInjectors = HashMap<Class<T: Foo>, Injector<T: Foo>>()
}
}
The idea is to have fooInjectors (which would be static in Java or in a companion object in Kotlin) contain a cache of sub-classes of Foo and their corresponding Injector.
Unfortunately, I can't get this to compile. I'd very much appreciate it if someone would help me figure out the syntax for this!
As far as I know, you are trying to do something that is impossible in Kotlin. The companion object is a singleton and it doesn't make sense to generify a singleton as there will not be any further objects created hence generic types are irrelevant. So you can't generify the property you declared because it's in the companion object.
However, one way you could make this working is using a backing function. This backing function should annotate with declaration-site variance.
This simply means we tell the compiler that we only return a type T from the method (and don't consume). That allows us to use subtypes and the supertype of the T if required. This is called covariance.
You can look at the docs to understand it further - https://kotlinlang.org/docs/reference/generics.html#declaration-site-variance
Here's what I meant.
interface Injector<T>
class InjectorImpl<T> : Injector<T>
abstract class Foo {
companion object {
val fooInjectors = createMap<Foo>()
private fun <T> createMap(): HashMap<Class<out T>, Injector<out T>> {
return HashMap()
}
}
}
class Bar: Foo()
object Runner {
#JvmStatic
fun main(args: Array<String>) {
Foo.fooInjectors[Bar::class.java] = InjectorImpl<Bar>()
Foo.fooInjectors[Foo::class.java] = InjectorImpl<Bar>()
}
}

Kotlin - Get Class<T> from KProperty1<T, P>

Given the following class:
data class Foo(val bar: Int)
How would I obtain a Class<T> for Foo...
val prop = Foo::bar
...from this property expression?
val receiver = prop.parameters[0]
val receiverClass = receiver.type.jvmErasure.java
Note that you get KType and KClass on the way, which you may prefer to Class.
The above works because parameters documentation says
If this callable requires a this instance or an extension receiver parameter, they come first in the list in that order.
so it might be worth a comment in your code.
The receiver parameter can also be obtained more explicitly by
val receiver = (prop.instanceParameter ?: prop.extensionReceiverParameter)!!

Getting class of lateinit property in Kotlin

Is it somehow possible to get ::class.java from Kotlin lateinit property before it is initialized?
Logically it should work - I'm trying to obtain a class not a value, but in reality it fails with uninitialized property access exception.
Note that the property I'm trying to get class of is in generic class and its type is one of generic parameters:
abstract class MVIFragment<
out INTERACTOR : MVIInteractor<UINTERFACE>,
UINTERFACE : MVIUIInterface,
MODEL : MVIViewModel
>
: Fragment(), MVIUIInterface, KodeinAware {
lateinit var viewModel: MODEL
I need the class to create an instance of ViewModel
viewModel = ViewModelProviders.of(this).get(viewModel::class.java)
Of course I can't do:
viewModel = ViewModelProviders.of(this).get(MODEL::class.java)
Any solution for that?
Due to type erasure, generic types are not known at runtime. That's just how Java/JVM works, and Kotlin doesn't attempt to magically work around it. (Unlike Scala, which has implicit magic which works magically, except when it doesn't.)
You will have to pass it along from some context where the type is statically determined, e.g.
class Container<T : Any>(private val tClass: Class<T>) {
val t: T = tClass.newInstance()
}
Container(String::class.java)
You can use an inline function with reified types to hide this ugliness,
class Container<T : Any>(private val tClass: Class<T>) {
val t: T = tClass.newInstance()
companion object {
inline operator fun <reified T : Any> invoke() = Container(T::class.java)
}
}
Container<String>()
which really compiles to the same thing. (The <String> can be omitted if type inference can determine it from context.)
In your case, it won't be possible to do this trick in the base (abstract) class; it has to be done on the concrete types.

Converting Kotlin's KClass to regular Class in Java

I am trying call a regular Java method in a Java code as follows:
public <T> T proxy(KClass<T> kClass) {
// unfortunately nothing like getJavaClass() exists
return (T) proxy(kClass.getJavaClass());
}
public <T> T proxy(Class<T> jClass) {
return (T) context.getBean(jClass);
}
In Kotlin, you can call .java on each KClass. This is not the case here and I am unable to extract the Java Class object from KClass. Is there a way to do it?
EDIT: This is trivial in Kotlin, but I am looking for solution in Java code.
The functionality does exist, just not where it seems to, as java is an extension property.
Use the method JvmClassMappingKt.getJavaClass.
In Kotlin, extension methods (and property getters/setters) are implemented as static methods of their containing class. If you look at the source for .java (Ctrl+Q), you can see that it is implemented in JvmClassMapping.kt.
As the function is package-level and does not have a containing object, it is simply placed into the file [Filename]Kt which in this case is JvmClassMappingKt.
Here is the source of this extension property:
#Suppress("UPPER_BOUND_VIOLATED")
public val <T> KClass<T>.java: Class<T>
#JvmName("getJavaClass")
get() = (this as ClassBasedDeclarationContainer).jClass as Class<T>
As you can see, the method's name is renamed on the JVM to getJavaClass.
In your case, you can try:
public <T> T proxy(KClass<T> kClass) {
return (T) proxy(JvmClassMappingKt.getJavaClass(kClass));
}
You can try to use javaObjectType on your KClass
The explanation:
Returns a Java [Class] instance corresponding to the given [KClass] instance.
In case of primitive types it returns corresponding wrapper classes.
E.g.
Boolean::class.javaObjectType