How can I get the inner type of a generic class in Kotlin? - kotlin

I know with Kotlin (on the JVM) I can get the type of a class with foo::class. And I know I can generic information, even at runtime, with typeOf<>() (as long as my function is inline). But I can't figure out how to get Int from a List<Int> for example.
Here's as far as I can get:
import kotlin.reflect.KType
import kotlin.reflect.typeOf
fun main() {
TypeTest.get<Int>()
TypeTest.get<List<Int>>()
TypeTest.get<Map<String, List<Int>>>()
}
class TypeTest {
companion object {
#OptIn(ExperimentalStdlibApi::class)
inline fun <reified OUT : Any> get() {
generateReturnType(typeOf<OUT>())
}
fun generateReturnType(type: KType) {
val classifier = type.classifier!!
println("class $classifier has parameters ${classifier::class.typeParameters}")
}
}
}
This prints:
class class kotlin.Int has parameters [T]
class class kotlin.collections.List has parameters [T]
class class kotlin.collections.Map has parameters [T]
Given a KClassifier, how can I get its inner type (ideally recursively, in the case of a List<List<Int>>)? Or is there another way to do this?

Related

How to reference parameterized constructor in Kotlin?

So I have this definition:
sealed interface ParseResult<out R> {
data class Success<R>(val value: R) : ParseResult<R>
data class Failure(val original: String, val error: Throwable) : ParseResult<Nothing>
}
I want to wrap certain elements in a Success. And I know I can do it like this...
list.map{ParseResult.Success(it)}
But is there a way to use a constructor reference?
list.map(ParseResult::Success) //this won't compile
You can use a constructor reference if you add an import:
import somepackage.ParseResult.Success
Or even a typealias:
typealias Success<R> = ParseResult.Success<R>
Then you can do:
list.map(::Success)
The idea here is to make ParseResult.Success referrable by a simple name.
Not being able to do ParseResult::Success does look like a bug to me though. Compare:
class Outer {
class Nested
}
class OuterGeneric<T> {
class Nested
}
fun main() {
val x = Outer::Nested // works
val y = OuterGeneric::Nested // error
}

Default value for generic member

I'm trying this:
class Foo<T> {
var member: T = T()
}
...but the Kotlin compiler gives me an error: Type parameter T cannot be called as function.
How do I default-construct a generic member variable?
Well, to access the type information, we need to use the reified keyword on the type, but this is only applicable in inlined functions. So instead of relying on direct construction, a workaround can be to use a generator function wrapped in the companion object that immediately sets the member right after construction
// Test class to verify the implementation
class Yolo {
override fun toString() = "Yolo swag"
}
class Foo<T : Any> {
lateinit var member: T
companion object {
inline fun <reified T : Any> newInstance() =
T::class.java.newInstance().let { memberInstance ->
Foo<T>().apply { member = memberInstance}
}
}
}
fun main() {
// generate a Foo<Yolo>
val foo = Foo.newInstance<Yolo>()
println(foo.member) // displays "Yolo swag"
}
It's implied that T has a public no-arg constructor, but in general case it may not be true. This code uses reflection to bypass compiler complains about it (which may end up with runtime error if you dissapoint the JVM expectations and indeed pass T without public no-arg constructor).
//Reified generics at class level are not yet supported in Kotlin (KT-33213),
// so you have to pass instance of `KClass` manually as a consructor parameter
class Foo<T : Any>(clazz: KClass<T>) {
var member: T = clazz.createInstance()
}

How can I enforce that a class has a companion object?

What I want is this:
interface base {
abstract static fun foo()
}
class impl : base {
override static fun foo()
}
Normally, Kotlin solves problems using companion objects rather than static functions. But an interface can't define a requirement for a companion object with function. So how can I accomplish this? The code that uses this would look like
fun <T : base> bar() {
T.foo()
}
Any other way to get this behavior? Namely, that I can execute a function of a derivative of T, without knowing the specific type, and not assuming the derivative has a default constructor?
Edit
I was able to get this to do what I want by using value parameters of types that can be set on the companion objects of the classes I want to work with. An illustrative example of what I want to use this technique for.
import kotlin.reflect.full.*
interface DynamicBuilder {
fun build(sides: Int): Shape?
}
interface Shape {
companion object : DynamicBuilder {
override fun build(sides: Int) = null
}
}
abstract class Shape2D : Shape {
companion object : DynamicBuilder {
override fun build(sides: Int) = if(sides > 0) Square() else Circle()
}
}
abstract class Shape3D : Shape {
companion object : DynamicBuilder {
override fun build(sides: Int) = if(sides > 0) Cube() else Sphere()
}
}
class Square : Shape2D()
class Circle : Shape2D()
class Sphere : Shape3D()
class Cube : Shape3D()
fun Build(sides: Int, builder: DynamicBuilder): Shape? {
return builder.build(sides)
}
inline fun <reified T : Shape> Build(sides: Int): Shape? {
return Build(sides, T::class.companionObjectInstance as DynamicBuilder)
}
fun main() {
println(Build(0, Shape2D))
println(Build(4, Shape2D))
println(Build<Shape3D>(0))
println(Build<Shape3D>(6))
}
The goal is that I can create a new entire class of Shape, and have all the logic related to how it builds the concrete object contained in that file, rather than having some monolithic shared switch statement.
An interface can define a requirement for some object with function, and you can suggest it to be the companion object even if you can't force it to be.
interface BaseCompanion {
fun foo(): Unit
}
interface Base {
companion object : BaseCompanion {
fun foo() { println("in Base") }
}
fun companion(): BaseCompanion = Base
}
interface Derived : Base {
companion object : BaseCompanion {
fun foo() { println("in Derived") }
}
override fun companion() = Derived
}
// value parameter, not type parameter
fun bar(companion: BaseCompanion) {
companion.foo()
}
bar(Base)
bar(Derived)
The companion() function isn't actually used in this case, it's for when you want to access the companion from a Base instance:
fun baz(x: Base) {
x.companion().foo()
}
Another (unsafe) option is to define companion() using reflection.
fun companion() = this::class.companionObjectInstance as BaseCompanion
Plus: no need to explicitly override it in Derived; minuses: 1. will crash at runtime if you forget to create the companion or to extend BaseCompanion; 2. slower than non-reflection definition.
TL;TR:
How can I enforce that a class has a companion object?
You cannot.
Kotlin has no static methods. Even if it had them, they wouldn't be overridable, as they are not in Java. The same holds for companion objects. Kotlin code is eventually compiled to Java byte code, so what is not possible in Java won't be possible in Kotlin either.
Edit:
It's interesting to see what the compiler has to say about it. Consider the following snippet:
open class Base {
companion object {
fun test() {}
}
}
inline fun <reified T : Base> staticCall() {
T.test() // <-- ERROR
}
The error message:
Type parameter 'T' cannot have or inherit a companion object, so it cannot be on the left hand side of dot
Based on your updated question, it seems like what you want is usually achieved using the factory pattern. Alternatively you could also use dependency injection. There are many options without the usage of reflection.
Why shouldn't you use reflection?
There are a few reasons here and here and you can find more if you google it. Generally reflection was created for a specific purpose, to discover the functionality of a class that was unknown at compile time. You do not use it for this purpose, since your implementation requires you to know the class, in order to pass it as a reified generic parameter. If you do require to discover classes that you don't know at compile time, you can use dependency injection.
The simpler solution for your version is a factory pattern:
interface Shape
class Square : Shape
class Circle : Shape
class Sphere : Shape
class Cube : Shape
object ShapeFactory {
fun build2DShape(sides: Int): Shape {
if(sides > 0) Square() else Circle()
}
fun build3DShape(sides: Int): Shape {
if(sides > 0) Cube() else Sphere()
}
}
fun main() {
println(ShapeFactory.build2DShape(0))
println(ShapeFactory.build3DShape(0))
}
In short, Build<Shape3D>(0) is replaced by ShapeFactory.build3DShape(0). The caller still has to know that there are 3DShapes and where they are. The only thing that changed is that you do not require Reflection.
This requires the person calling the function to know of the existence of 2D and 3D shapes. Same as in your implementation with reflection. This way you can have all the logic how to create the shapes in the same file as the shapes. You could even make the factory call some functions in the companion object of the shape if you wish to do so. Your factory knows of the existence of those subclasses. But since you can put the factory in the same file as the subclasses, that doesn't split the logic to somewhere else.
If you want to delegate the deciding whether it is a 2D or a 3D shape to a subclass you can do the following:
interface Shape
class Square : Shape
class Circle : Shape
class Sphere : Shape
class Cube : Shape
object ShapeFactory {
fun build2DShape(sides: Int): Shape {
return if(sides > 0) Square() else Circle()
}
fun build3DShape(sides: Int): Shape {
return if(sides > 0) Cube() else Sphere()
}
}
fun getBuilder(dimensions: Int): (sides: Int) -> Shape {
if (dimensions == 2)
return ShapeFactory::build2DShape
else
return ShapeFactory::build3DShape
}
fun main() {
print (getBuilder(2)(3))
}

Type inference only works for extension function

The following code works fine and the call to the foo.get() extension function returns the correct type BarImpl.
open class Bar
class BarImpl: Bar()
class Foo<T : Bar>
inline fun <reified T : Bar> Foo<T>.get(): T {
return SomeMap(this).get(T::class)
}
class Activity {
lateinit var foo: Foo<BarImpl>
val barImpl = foo.get()
}
But when I try to move Foo<T>.get() into the class the type inference fails
class Foo<T : Bar> {
inline fun <reified T : Bar> get(): T {
return SomeMap(this).get(T::class)
}
}
class Activity {
lateinit var foo: Foo<BarImpl>
val barImpl = foo.get()
}
error: type inference failed: Not enough information to infer parameter T in inline fun get(): T
Please specify it explicitly.
val vm = foo.get()
^
How can I move the function into the class?
The extension function returns the result of the Foo type parameter. So the result type can be inferred from the receiver type.
And the member function result type has nothing in common with Foo type parameter except the name, which means nothing for a compiler. You can see that T in method and T in class are different types by writing and compiling the following code:
Foo<BarImpl>().get<BarImpl2>()
If you want to make get to be a member function which returns the result of Foo type parameter, you should remove type parameter from function and inject class instance via the constructor:
class Foo<T : Bar>(private val clazz: KClass<T>) {
fun get(): T {
return SomeMap(this).get(clazz)
}
companion object {
inline operator fun <reified T : Bar> invoke() = Foo(T::class)
}
}

How to define functional interface with generics in Kotlin?

I'm learning Kotlin and I have some trouble with functions.
I'm trying to create something like a functional interface with a generic parameter.
In Java I would create something like this:
#FunctionalInterface
public interface Foo<T extends Bar> {
String something(T arg);
}
Then I can use this somewhere else like this (given that Person extends Bar:
Foo<Person> f = p -> p.toString();
How do you write this with Kotlin?
The first thing I tried was to use type-aliases like this:
typealias Foo<T> = (T) -> String
However, it stopped working when I added the bound to the type parameter:
typealias Foo<T: Bar> = (T) -> String // Error: Bounds are not allowed on type alias parameters
The second approach was to write an interface that extends the function type:
interface Foo<T: Bar> : (T) -> String
However, now I don't know how to instantiate a lambda function from with this. It works when I create class from it like this:
class Something: Foo<Person> {
override fun invoke(p: Person): String {
return p.toString()
}
}
val f = Something()
But this is a big overhead and I'm sure there has to be a better solution.
So how can I define a function signature that can be reused by many functions that supports generic parameters with bounds in kotlin?
Most of the time (always?) it is sufficient to define the type of the lambda in the parameter of the function that receives it.
For example:
open class Bar
class Person: Bar()
var f = { p: Person -> p.toString() }
fun <T : Bar> withFoo(block: (T) -> String) { }
fun <T : Bar> otherFoo(block: (T) -> String) { }
fun main() {
withFoo(f)
otherFoo(f)
}
The same way the Kotlin documentation states:
"since Kotlin has proper function types, automatic conversion of functions into implementations of Kotlin interfaces is unnecessary and therefore unsupported."
See https://kotlinlang.org/docs/reference/java-interop.html#sam-conversions