Kotlin - get name from lambda expression - kotlin

I want to get the name of a parameter passed into a lambda expression in Kotlin. In C# I would use an Expression<Func<T, ...>> to get the parameter name, but I'm not sure whether this is possible in Kotlin
import java.util.*
fun main(args: Array<String>) {
val foo = Foo<Model>()
foo.bar { it.age }
// Should print "age"
}
data class Model(val id: UUID, val name: String, val age: Int)
class Foo<T> {
fun bar(expression: (x: T) -> Any) {
println(/*The name of the parameter*/)
}
}
Is this possible in Kotlin?

If you want to pass around properties and print their names, you can do it by using KProperty:
fun main(args: Array<String>) {
val foo = Foo<Model>()
foo.bar(Model::age)
}
class Foo<T> {
fun bar(p: KProperty<*>) {
println(p.name)
}
}

Related

How to use Either monad and avoid nested flatMap

I'm in a situation where I'm trying to setup some data and then call a service. Each step can fail, so I'm trying to use Arrow's Either to manage this.
But I'm ending up with a lot of nested flatMaps.
The following code snippet illustrates what I'm trying to do:
import arrow.core.Either
import arrow.core.flatMap
typealias ErrorResponse = String
typealias SuccessResponse = String
data class Foo(val userId: Int, val orderId: Int, val otherField: String)
data class User(val userId: Int, val username: String)
data class Order(val orderId: Int, val otherField: String)
interface MyService {
fun doSomething(foo: Foo, user: User, order: Order): Either<ErrorResponse, SuccessResponse> {
return Either.Right("ok")
}
}
fun parseJson(raw: String): Either<ErrorResponse, Foo> = TODO()
fun lookupUser(userId: Int): Either<ErrorResponse, User> = TODO()
fun lookupOrder(orderId: Int): Either<ErrorResponse, Order> = TODO()
fun start(rawData: String, myService: MyService): Either<ErrorResponse, SuccessResponse> {
val foo = parseJson(rawData)
val user = foo.flatMap {
lookupUser(it.userId)
}
//I want to lookupOrder only when foo and lookupUser are successful
val order = user.flatMap {
foo.flatMap { lookupOrder(it.orderId) }
}
//Only when all 3 are successful, call the service
return foo.flatMap { f ->
user.flatMap { u ->
order.flatMap { o ->
myService.doSomething(f, u, o)
}
}
}
}
I'm sure there is a better way to do this. Can someone help me with an idiomatic approach?
You can use the either { } DSL, this is available in a suspend manner or in a non-suspend manner through the either.eager { } builder.
That way you can use suspend fun <E, A> Either<E, A>.bind(): A.
Rewriting your code example:
fun start(rawData: String, myService: MyService): Either<ErrorResponse, SuccessResponse> =
either.eager {
val foo = parseJson(rawData).bind()
val user = lookupUser(foo.userId).bind()
val order = lookupOrder(foo.orderId).bind()
myService.doSomething(foo, user, order).bind()
}
If you run into an Either.Left, then bind() will short-circuit the either.eager block and return with the encountered Either.Left value.

Deserialize generic object using Kotlin Serialization

I am trying to replace Gson library by kotlin serialization to handle JSON serialization/deserialization.
I am facing some issues to deserialize generic objects I have setup a simple example of what I am trying to achieve:
#Serializable
data class ContentMessageDto<T>(
val method: String,
val content: T
)
#Serializable
private data class DummyObjectNonNullProperties(
val value: Int,
#SerialName("aaa") val someProp: String,
val bbb: Boolean,
val ccc: Double
)
interface MyParser {
fun <T> parseContentMessage(
json: String
): ContentMessageDto<T>
}
class MyParserImpl(private val jsonSerialization: Json) : MyParser {
override fun <T> parseContentMessage(json: String): ContentMessageDto<T> {
return jsonSerialization.decodeFromString<ContentMessageDto<T>>(json)
}
}
fun main() {
println("start processing...")
val jsonToParse = """
{
"method":"get",
"content":{
"value":345,
"aaa": "some string",
"bbb": true,
"ccc": 23.4
}
}""".trimIndent()
val parser:MyParser = MyParserImpl(Json)
val result = parser.parseContentMessage<DummyObjectNonNullProperties>(jsonToParse)
println("result -> $result")
}
But when I run the main method, I get the following error:
Exception in thread "main" java.lang.IllegalStateException: Only KClass supported as classifier, got T
at kotlinx.serialization.internal.Platform_commonKt.kclass(Platform.common.kt:102)
at kotlinx.serialization.SerializersKt__SerializersKt.serializer(Serializers.kt:52)
at kotlinx.serialization.SerializersKt.serializer(Unknown Source)
at kotlinx.serialization.SerializersKt__SerializersKt.builtinSerializerOrNull$SerializersKt__SerializersKt(Serializers.kt:79)
at kotlinx.serialization.SerializersKt__SerializersKt.serializerByKTypeImpl$SerializersKt__SerializersKt(Serializers.kt:69)
at kotlinx.serialization.SerializersKt__SerializersKt.serializer(Serializers.kt:54)
at kotlinx.serialization.SerializersKt.serializer(Unknown Source)
But I am not sure why. Can someone provide me an explanation and if possible some tips on how I can implement this?
It would have worked if you've simply done:
val result = Json.decodeFromString<ContentMessageDto<DummyObjectNonNullProperties>>(jsonToParse)
But with all this wrapping, type information about T was lost. The problem is that you can't simply use reified generics here, cause inline functions can't be non-final.
Possible workarounds:
Define parseContentMessage as extension function so that it could have inline modifier (and T could be reified):
interface MyParser {
val jsonSerialization: Json
}
inline fun<reified T> MyParser.parseContentMessage(json: String): ContentMessageDto<T> {
return jsonSerialization.decodeFromString(json)
}
class MyParserImpl(override val jsonSerialization: Json) : MyParser
//Usage will be the same
Manually pass serializer for T into parseContentMessage:
interface MyParser {
fun <T> parseContentMessage(json: String, contentSerializer: KSerializer<T>): ContentMessageDto<T>
}
class MyParserImpl(private val jsonSerialization: Json) : MyParser {
override fun <T> parseContentMessage(json: String, contentSerializer: KSerializer<T>): ContentMessageDto<T> {
return jsonSerialization.decodeFromString(ContentMessageDto.serializer(contentSerializer), json)
}
}
//Usage:
val result = parser.parseContentMessage(jsonToParse, DummyObjectNonNullProperties.serializer())

How to create an instance of a class by passing the type

I want to be able to say make an instance of this class and give a type then the code can instantiate a new instance of that class.
fun maker(type: Class<Animal>): Animal {
if(type == Class<Dog>) {
return Dog()
}
else if (type == Class<Cat>) {}
...
}
What is a good way to do this?
If they all have zero-argument constructors, you can do:
fun maker(type: Class<Animal>): Animal {
return type.newInstance()
}
You can make it return the type that was passed in for a little more versatility:
fun <T: Animal> maker(type: Class<T>): T {
return type.newInstance()
}
Correct version following your example (not sure if best approach overall):
fun <T: Animal> maker(type: Class<T>): T? {
return when (type) {
Cat::class.java -> Cat() as T
Dog::class.java -> Dog() as T
else -> null
}
}
And then to create objects:
val a = maker(Cat::class.java)
val b = maker(Dog::class.java)
(Updated) I am not an expert in Kotlin but you can do something like this :
import kotlin.reflect.KClass
import kotlin.reflect.full.createInstance
class A {
fun greet() {
println("Hello A");
}
}
class B{
fun greet() {
println("Hello B");
}
}
fun <T : Any> maker(clazz: KClass<T>): T {
return clazz.createInstance();
}
val aObj = maker<A>(A::class);
aObj.greet();
val bObj = maker<B>(B::class);
bObj.greet();
Output:
Hello A
Hello B
I hope now it makes sense you just need to pass the class to the method and it returns an object.
As you will be using Animal as a parent class so you can replace Any => Animal
fun <T : Animal> maker(clazz: KClass<T>): T {
return clazz.createInstance();
}
If the function can be inline you can also use reified type
inline fun<reified T: Animal> make() = T::class.createInstance()
...
val dog = make<Dog>()
Please notice that to use createInstance() the class must have no-arg constructor or IllegalArgumentException will be thrown

kotlin: "unresolved reference" in sceondary constructor

i am very new to kotlin, and maybe this might be a silly question but why using a variable defined in a secondary parameter gives an error of "unresolved reference" when i try to print it while the same does not happen in case of primary constructor
fun main(args: Array<String>){
var stud= Student("Yash", 10)
}
class Student(name: String) {
init {
println("name is $name")
}
constructor(n: String, Id: Int): this(n) {
println("name is $n")
println("id is $id")
}
}
Parameter id is small letter,but you are try to print capital letter Id, change like this its working now
constructor(n: String, id: Int)
fun main(args: Array<String>){
var stud= Student("Yash", 10)
}
class Student(name: String) {
init {
println("name is $name")
}
constructor(n: String, id: Int): this(n) {
println("name is $n")
println("id is $id")
}
}

How to get names and values of method's parameters?

Is there possibility to get names of method's parameters inside method code? Can anyone show example of it?
I found only this solution at this time, but dislike it..:
class Greeter() {
fun greet(name: String) {
val c = Greeter::class;
for (m in c.memberFunctions) {
if (m.name == "greet") {
val p = m.parameters
println(p.toString())
}
}
println("Hello, ${name}");
}
}
fun main(args: Array<String>) {
Greeter().greet("UserName")
}
update: i found another solution, but now i have one more question:
How to get pairs of ParamName, ParamValue at function greet?
class Greeter() {
fun greet(name: String) {
val p = Greeter::greet.parameters
println(p.toString())
println("Hello, ${name}");
}
}
fun main(args: Array<String>) {
Greeter().greet("UserName")
}
Parameter names are available through the KParameter.name property:
class Greeter() {
fun greet(name: String) {
val p = Greeter::greet.parameters
println("Hello, ${p[0].name}")
}
}
Parameter values, on the other hand, cannot be obtained reflectively easily on JVM.