Kotlin method generic type verification - kotlin

I'm trying to write a method that takes a KProperty1 and a object of R like so
inline fun <T: Any, R: Any> List<T>.test(prop1: KProperty1<T, R>, prop2: R): List<T>
except I'm not getting type checking on the prop2. Is there any way to ensure that prop2 is of type R?
Here is a more complete example
class Foo
class Bar(val foo: Foo)
fun main(args: Array<String>): Unit {
val list = listOf(Bar(Foo()))
list.test(Bar::foo, Foo()) // This should work
list.test(Bar::foo, "") // I want this to be a type error since a string is not a Foo
}
inline fun <T: Any, R: Any> List<T>.test(prop1: KProperty1<T, R>, prop2: R): List<T> {
println(prop1.invoke(this.first())::class == prop2::class)
return listOf()
}

If you want to restrict R to children of Foo then provide upper bound constraint:
inline fun <T: Any, R: Foo> List<T>.test(prop1: KProperty1<T, R>, prop2: R): List<T> {
println(prop1.invoke(this.first())::class == prop2::class)
return listOf()
}

Related

How to write generic functions in Kotlin interfaces's implementations

I am trying to implement a generic HttpClient like this one:
interface HttpClient {
fun <T: Any> get(url: String): T?
}
implemented by a class like this:
class HttpClientImpl #Inject constructor(...) : HttpClient {
override fun <T : Any> get(url: String): T? = execute(url)
private inline fun <reified T: Any> execute(url: String): T? {
val request = Request.Builder().url(url).get().build()
client.newCall(request).execute().use {
return it.body?.parseBodySuccess()
}
}
private inline fun <reified T: Any> ResponseBody?.parseBody(): T? {
val type = objectMapper.typeFactory.constructType(T::class.java)
return this?.let { objectMapper.readValue(it.string(), type) }
}
}
Now, I would like to be able to call such GET method in this way:
data class MyEntity(...)
class MyService #Inject constructor(private val client: HttpClient) {
fun performGet(url: String): MyEntity? = client.get<MyEntity>(url)
}
However this is not allowed and the compiler throws an error referring to the line of code
override fun <T : Any> get(endpoint: String): T? = execute(endpoint)
flagging that : Cannot use 'T' as reified type parameter. Use a class instead.
I have been trying to re-write the line as
override inline fun <reified T : Any> get(endpoint: String): T? = execute(endpoint)
however, despite having to make the other two inline functions "non private" the compiler still won't compile because in this last way of writing the overriding function, it says:
Override by a function with reified type parameter
How can I achieve such generic function?
I ended up doing something like this:
interface HttpClient {
fun <T: Any> get(url: String, type: Class<T>): T?
}
implemented as:
class HttpClientImpl #Inject constructor(...) : HttpClient {
override fun <T : Any> get(url: String, type: Class<T>): T? = execute(url, type)
private fun <T: Any> execute(url: String, type: Class<T>): T? {
val request = Request.Builder().url(url).get().build()
client.newCall(request).execute().use {
return it.body?.parseBody(type)
}
}
private fun <T: Any> ResponseBody?.parseBody(type: Class<T>): T? {
val dataType = objectMapper.typeFactory.constructType(type)
return this?.let { objectMapper.readValue(it.string(), dataType) }
}
}
that I can call in this way:
data class MyEntity(...)
class MyService #Inject constructor(private val client: HttpClient) {
fun performGet(url: String): MyEntity? = client.get(url, MyEntity::class.java)
}
I would have preferred to pass the Type directly as an actual type like
client.get<MyEntity>(url)
rather than passing the Class as a parameter, however, just for now it works...
If anyone can suggest a better way of doing this, please let me know.
Updated
As suggested by Pawel, I have created an extra inline extension function to the HttpClient interface
inline fun <reified T:Any> HttpClient.get (url: String) = get(url, T::class.java)
And I'm now able to call the function the way I wanted.

Chaining property references and maintaining both the ability to read and write

Using a KMutableProperty1 to access a classes property works both as a getter and setter.
class BaseClass(
var baseInt: Int = 0,
var baseInnerClass: InnerClass = InnerClass()
)
class InnerClass(
var innerInt: Int = 0,
)
val target = BaseClass()
val kMutableProperty1b = (BaseClass::baseInt)
kMutableProperty1b.set(target, 4)
val baseInt = kMutableProperty1b.get(target)
To be able to access nested properties like
BaseClass::innerClass -> InnerClass:innerInt
I tried to up chain two kMutableProperty1 with
fun <A, B, C> ((A) -> B).chained(getter : (B) -> C) : (A) -> C = { getter(this(it)) }
With that, the inner properties can be read, but not set:
val chainedKMutableProperty = baseMutableProperty.chained(InnerClass::innerInt)
val innerInt = chainedKMutableProperty(target)
chainedKMutableProperty.set(target, 5) // Not available
In Swift something similar can be achieved using KeyPaths
let target = BaseClass()
let aKeyPath = \BaseClass.baseInt
target[keyPath: aKeyPath] = 4
let baseInt = target[keyPath: aKeyPath]
let bKeyPath = \BaseClass.baseInnerClass
let chainedKeyPath = bKeyPath.appending(path: \InnerClass.innerInt)
let innerInt = target[keyPath: chainedKeyPath]
target[keyPath: chainedKeyPath] = 5
How can I do the same in Kotlin - chaining property accessors and maintaining both the ability to read and write?
I don't think there is something like this already in Kotlin or Java stdlib. We can easily create it by ourselves, although I don’t think it is a good idea to stick to KProperty. This interface isn’t just a generic accessor interface. It is a very specific thing: a property of a class. And we don't deal with class properties here.
Instead, I suggest to create our own interfaces. Below is a simple POC:
fun main() {
val target = BaseClass()
val chainedProp = BaseClass::baseInnerClass chain InnerClass::innerInt
println(chainedProp.get(target))
chainedProp.set(target, 5)
// or
println(target[chainedProp])
target[chainedProp] = 12
}
operator fun <T, V> T.get(key: MyProperty<T, V>): V = key.get(this)
operator fun <T, V> T.set(key: MyMutableProperty<T, V>, value: V) = key.set(this, value)
infix fun <T, V, V2> KProperty1<T, V>.chain(next: KMutableProperty1<V, V2>): MyMutableProperty<T, V2> = asMyProperty() chain next.asMyProperty()
infix fun <T, V, V2> MyProperty<T, V>.chain(next: MyMutableProperty<V, V2>): MyMutableProperty<T, V2> = object : MyMutableProperty<T, V2> {
override fun get(receiver: T): V2 {
return next.get(this#chain.get(receiver))
}
override fun set(receiver: T, value: V2) {
next.set(this#chain.get(receiver), value)
}
}
fun <T, V> KProperty1<T, V>.asMyProperty(): MyProperty<T, V> = object : MyProperty<T, V> {
override fun get(receiver: T): V {
return this#asMyProperty.get(receiver)
}
}
fun <T, V> KMutableProperty1<T, V>.asMyProperty(): MyMutableProperty<T, V> = object : MyMutableProperty<T, V> {
override fun get(receiver: T): V {
return this#asMyProperty.get(receiver)
}
override fun set(receiver: T, value: V) {
this#asMyProperty.set(receiver, value)
}
}
interface MyProperty<in T, out V> {
fun get(receiver: T): V
}
interface MyMutableProperty<in T, V> : MyProperty<T, V> {
fun set(receiver: T, value: V)
}

extension function on List for elements compliant to multiple interfaces

The following code creates an extension function for List that contain elements that are implementing the InterfaceCustomFirebaseData.
fun List<InterfaceCustomFirebaseData>.toFirebaseDate(): MutableMap<String, Any> {
val firebaseData: MutableMap<String, Any> = mutableMapOf()
this.forEach { firebaseData[it.id] = it.toFirebaseData() }
return firebaseData
}
But I need the List elements to be compliant to 2 interfaces (InterfaceCustomFirebaseData and also InterfaceIdentifiable), but this does not work:
fun List<InterfaceCustomFirebaseData, InterfaceIdentifiable>.toFirebaseDate(): MutableMap<String, Any> { ... }
The compiler gives error "One type argument expected for interface List"
How should this be written in Kotlin?
Found the answer myself:
fun <T> List<T>.toFirebaseDate(): MutableMap<String, Any> where T : InterfaceCustomFirebaseData, T : InterfaceIdentifiable {
val firebaseData: MutableMap<String, Any> = mutableMapOf()
this.forEach { firebaseData[it.id] = it.toFirebaseData() }
return firebaseData
}

How to use function receiver type with SAM interfaces in Kotlin

I'm coming from Java and am new to Kotlin. I try to understand how to use receiver type with lambdas specified as functional SAM interfaces.
Let the code speak for itself.
fun interface Delegator <T> {
fun delegate(receiver: T)
}
fun <T> invokeWithDynamicReceiver(receiver: T, fn: T.() -> Unit) = receiver.fn()
fun <T> invokeWithSamInterface(receiver: T, fn: Delegator<T>) = fn.delegate(receiver)
fun dynamicReceiver() {
invokeWithDynamicReceiver("Foo") { length } // Dynamic receiver
invokeWithSamInterface("Foo") { it.length } // Can't bind receiver as "this"
}
How do I need to change the code to use the Delegator lambda with dynamic receiver?
You can define the functions inside the Delegator as extension function, this way the receiver is passed as this to the lambda.
fun interface ExtensionDelegator <T, R> {
fun T.delegate(): R
}
fun <T, R> invokeWithExtensionSamInterface(receiver: T, fn: ExtensionDelegator<T, R>): R =
with(fn) { receiver.delegate() }
Alternatively, you can simply define the dynamic receiver with a typealias to achieve the same result.
typealias AliasDelegator<T, R> = T.() -> R
fun <T, R> invokeWithAliasSamInterface(receiver: T, fn: AliasDelegator<T, R>): R = fn.invoke(receiver)
On the use site, both approaches look the same.
fun main() {
val result = invokeWithExtensionSamInterface("Foo") { length }
println(result)
val otherResult = invokeWithAliasSamInterface("Fizz") { length }
println(otherResult)
}

How to nest multiple property delegates in Kotlin

I've come accross a case where I want to "chain" mutliple delegates (piping the output of one into the other).
This seems to be possible:
private val errorLogList by listSO(listOf<StateObject<Luxeption>>(), SODest.NONE, publicSOAccessRights())
val errorLog: StateObject<List<StateObject<Luxeption>>> by addToSet(errorLogList)
However, this does not look too well :). I'd like to do it in one line like this:
val errorLog: StateObject<List<StateObject<Luxeption>>> by addToSet(
listSO(listOf<StateObject<Luxeption>>(), SODest.NONE, publicSOAccessRights())
)
My question: Is this type of creating properties through delegates possible in Kotlin?
Here are both implementations of my delegates:
addToSet:
open class ChildSOReturner {
val set: Set<StateObject<*>> = setOf()
inline fun <reified T> addToSet(so: T) = object: ReadOnlyProperty<Any?, T> {
override operator fun getValue(thisRef: Any?, property: KProperty<*>): T {
if (thisRef is T) {
set.plus(so)
return so
} else throw IllegalArgumentException()
}
}
}
listSo:
fun <O> listSO(
initialState: List<StateObject<O>>,
soDest: SODest,
soAccessRights: SOAccessRights
) = object : ReadOnlyProperty<Any?, StateObject<List<StateObject<O>>>> {
override operator fun getValue(thisRef: Any?, property: KProperty<*>): StateObject<List<StateObject<O>>> {
val meta = SOMeta(SOId(property.name), soDest, soAccessRights)
return StateObjectList(initialState, meta)
}
}
It turned out to be quite tricky, but possible (unless I am missing something, and it isn't tested but the idea should work):
fun <T, U, V> composeProperties(prop: ReadOnlyProperty<T, U>, f: (U) -> ReadOnlyProperty<T, V>) : ReadOnlyProperty<T, V> {
var props = mutableMapOf<Pair<T, KProperty<*>>, ReadOnlyProperty<T, V>>()
return object : ReadOnlyProperty<T, V> {
override operator fun getValue(thisRef: T, property: KProperty<*>): V {
val prop1 = props.getOrPut(Pair(thisRef, property)) {
f(prop.getValue(thisRef, property))
}
return prop1.getValue(thisRef, property)
}
}
}
And then to use
val errorLog: ... by composeProperties(listSO(...)) { addToSet(it) }