What does single variable name in lambda closure? - kotlin

I had method that get data from database to list using Anko library. I can't figure out what mean single variable name after lambda (i.e dataList in the end of the function). How it's translate to Java?
Code:
fun gerData() : ArrayList<DataModelKotlin> = context.database.use {
val dataList = ArrayList<DataModelKotlin>()
select("TipList", "task", "id")
.parseList(object: MapRowParser<List<DataModelKotlin>>{
override fun parseRow(columns: Map<String, Any?>): List<DataModelKotlin> {
val task = columns.getValue("task")
val id = columns.getValue("id")
val dataModel = DataModelKotlin(text = task.toString(), id = id.toString().toInt())
dataList.add(dataModel)
return dataList
}
})
dataList //???
}

It is the same with
return dataList;
In Java

Last expression in lambda is its return value: https://kotlinlang.org/docs/reference/lambdas.html#returning-a-value-from-a-lambda-expression

Related

Kotlin reflection on Anonymous object using kotlin-reflect

I am receiving a json object in which there is a property I don't know the name of at compile time.
The name of the property is stored in a variable.
Since the name of the property may vary the JSON is parsed as an Anonymous object.
Is it possible to read the value of the property using reflection using the name stored in the variable ?
I tried with code resembling this:
jsonResponse::class.memberProperties.find { it.name == variableName }
with no success.
val decodedToken = JWT(jwtString)
decodedToken.getClaim("useful_claim").asObject(Any::class.java)?.let {
// Get the property that matches the variable name
val reflectProp = res::class.memberProperties.find { it.name == BuildConfig.VARIABLE_NAME }
// Check that the property was found and exists
if (reflectProp is KMutableProperty<*>) {
(reflectProp.getter.call(res, BuildConfig.VARIABLE_NAME) as? List<*>)?.let {
// Return it as a list of existing MyClass
return it.filterIsInstance<MyClass>()
}
}
}
After the comments made by #Joffrey and #broot I tried without the JWT library.
Here is the code:
// Parcelable classes
#Parcelize
#JsonClass(generateAdapter = true)
data class JwtCustomResponse(
// This maps the variable name from the buildconfig on a known field name that can be used later
#field:Json(name = BuildConfig.VARIABLE_NAME) val appResourceAccess: MyCustomClass? = MyCustomClass()
) : Parcelable
#Parcelize
#JsonClass(generateAdapter = true)
data class JWTBody(
#field:Json(name = "resource_access") val resourceAccess: JwtCustomResponse? = JwtCustomResponse(),
) : Parcelable
// Custom JWT deserializer
object JWTUtils {
#Throws(Exception::class)
fun decoded(JWTEncoded: String): JWTBody {
val split = JWTEncoded.trim().split(".")
val mAdapt = moshi.adapter(JWTBody::class.java)
return mAdapt.fromJson(getJson(split[1])) ?: JWTBody()
}
#Throws(UnsupportedEncodingException::class)
private fun getJson(strEncoded: String): String {
val decodedBytes: ByteArray = Base64.decode(strEncoded, Base64.URL_SAFE)
return String(decodedBytes, charset("UTF-8"))
}
}
// JWT passed to the Utils
val decoded = JWTUtils.decoded(jwtString)

Why 'add' method doesn't work for mutableListOf()?

I have my own converter from Strings to List
object TypeConverter {
fun stringToListLong(text: String): List<Long> {
val listLong = mutableListOf<Long>()
val listString = text.split(",").map { it.trim() }
listString.forEach {
listLong.add(it.toLong())
}
return listLong
}
}
Then when I try to use it like below it shows the error(Unresolved reference: add)
val someString = "something"
var ids = TypeConverter.stringToListLong(someString)
ids.add(some long value)
Why?
You're returning a List<>, so ids is a List<>, therefore it does not have mutation operations. Make stringToListLong return MutableList<Long>.

Use Kotlin's data class in service-proxy of Vert.x

I'm trying to pass data class to the service-proxy of Vert.x like this:
data class Entity(val field: String)
#ProxyGen
#VertxGen
public interface DatabaseService {
DatabaseService createEntity(Entity entity, Handler<AsyncResult<Void>> resultHandler);
}
However, the service-proxy requires a DataObject as the parameter type.
Below are what I've tried so far.
First, I rewrite the data class as:
#DataObject
data class Entity(val field: String) {
constructor(json: JsonObject) : this(
json.getString("field")
)
fun toJson(): JsonObject = JsonObject.mapFrom(this)
}
Although this works, the code is redundant, so I tried the kapt with the following generator:
override fun process(annotations: Set<TypeElement>, roundEnv: RoundEnvironment): Boolean {
roundEnv.getElementsAnnotatedWith(ProxyDataObject::class.java).forEach { el ->
val className = el.simpleName.toString()
val pack = processingEnv.elementUtils.getPackageOf(el).toString()
val filename = "Proxy$className"
val classBuilder = TypeSpec.classBuilder(filename)
val primaryConstructorBuilder = FunSpec.constructorBuilder()
val secondaryConstructorBuilder = FunSpec.constructorBuilder().addParameter("json", JsonObject::class)
val secondaryConstructorCodeBlocks = mutableListOf<CodeBlock>()
el.enclosedElements.forEach {
if (it.kind == ElementKind.FIELD) {
val name = it.simpleName.toString()
val kClass = getClass(it) // get the corresponding Kotlin class
val jsonTypeName = getJsonTypeName(it) // get the corresponding type name in methods of JsonObject
classBuilder.addProperty(PropertySpec.builder(name, kClass).initializer(name).build())
primaryConstructorBuilder.addParameter(name, kClass)
secondaryConstructorCodeBlocks.add(CodeBlock.of("json.get$jsonTypeName(\"$name\")"))
}
}
secondaryConstructorBuilder.callThisConstructor(secondaryConstructorCodeBlocks)
classBuilder
.addAnnotation(DataObject::class)
.addModifiers(KModifier.DATA)
.primaryConstructor(primaryConstructorBuilder.build())
.addFunction(secondaryConstructorBuilder.build())
.addFunction(
FunSpec.builder("toJson").returns(JsonObject::class).addStatement("return JsonObject.mapFrom(this)").build()
)
val generatedFile = FileSpec.builder(pack, filename).addType(classBuilder.build()).build()
generatedFile.writeTo(processingEnv.filer)
}
return true
}
Then I can get the correct generated file by simply writing the original data class, but when I execute the building after cleaning, I still get the following error:
Could not generate model for DatabaseService#createEntity(ProxyEntity,io.vertx.core.Handler<io.vertx.core.AsyncResult<java.lang.Void>>): type ProxyEntity is not legal for use for a parameter in proxy
It seems that the generated annotation #DataObject is not processed.
So what should I do? Is there a better solution?

Map Key Values to Dataclass in Kotlin

how can I set properties of a dataclass by its name. For example, I have a raw HTTP GET response
propA=valueA
propB=valueB
and a data class in Kotlin
data class Test(var propA: String = "", var propB: String = ""){}
in my code i have an function that splits the response to a key value array
val test: Test = Test()
rawResp?.split('\n')?.forEach { item: String ->
run {
val keyValue = item.split('=')
TODO
}
}
In JavaScript I can do the following
response.split('\n').forEach(item => {
let keyValue = item.split('=');
this.test[keyValue[0]] = keyValue[1];
});
Is there a similar way in Kotlin?
You cannot readily do this in Kotlin the same way you would in JavaScript (unless you are prepared to handle reflection yourself), but there is a possibility of using a Kotlin feature called Delegated Properties (particularly, a use case Storing Properties in a Map of that feature).
Here is an example specific to code in your original question:
class Test(private val map: Map<String, String>) {
val propA: String by map
val propB: String by map
override fun toString() = "${javaClass.simpleName}(propA=$propA,propB=$propB)"
}
fun main() {
val rawResp: String? = """
propA=valueA
propB=valueB
""".trimIndent()
val props = rawResp?.split('\n')?.map { item ->
val (key, value) = item.split('=')
key to value
}?.toMap() ?: emptyMap()
val test = Test(props)
println("Property 'propA' of test is: ${test.propA}")
println("Or using toString: $test")
}
This outputs:
Property 'propA' of test is: valueA
Or using toString: Test(propA=valueA,propB=valueB)
Unfortunately, you cannot use data classes with property delegation the way you would expect, so you have to 'pay the price' and define the overridden methods (toString, equals, hashCode) on your own if you need them.
By the question, it was not clear for me if each line represents a Test instance or not. So
If not.
fun parse(rawResp: String): Test = rawResp.split("\n").flatMap { it.split("=") }.let { Test(it[0], it[1]) }
If yes.
fun parse(rawResp: String): List<Test> = rawResp.split("\n").map { it.split("=") }.map { Test(it[0], it[1]) }
For null safe alternative you can use nullableString.orEmpty()...

Kotlin Data class copy extension

I am trying to find a solution for a nice kotlin data class solution. I have already this:
data class Object(
var classMember: Boolean,
var otherClassMember: Boolean,
var example: Int = 0) {
fun set(block: Object.() -> kotlin.Unit): Object {
val copiedObject = this.copy()
copiedObject.apply {
block()
}
return copiedObject
}
fun touch(block: Object.() -> kotlin.Unit): Object {
return this.set {
classMember = true
otherClassMember = false
block() }
}
}
val test = Object(true,true,1)
val changedTest = test.touch { example = 2 }
the result of this method is that the changedTest object has classMember = true, otherClassMember = false and example = 2
The problem with this solution is, the class properties are not immutable with var declaration. Does somebody have an idea how to optimize my methods to change var to val?
val says that a variable can't change it's value after initialization at the definition point. Kotlin's generated copy method does not modify an existing copy after construction: this method actually uses retrieved values from an object, replaces these values with ones that provided in copy method (if any), and after that just constructs a new object using these values.
So, it is not possible to perform such an optimization if you are going to change object's state after construction.
If I understood what you want correctly, you can do
data class Object(
val classMember: Boolean,
val otherClassMember: Boolean,
val example: Int = 0) {
fun touch(example: Int = this.example): Object {
return copy(
classMember = true,
otherClassMember = false,
example = example)
}
}
val test = Object(true,true,1)
val changedTest = test.touch(example = 2)
Though you need to repeat parameters other than classMember and otherClassMember but without reflection you can't do better.