Kotlin: How to get a type of a method parameter - kotlin

I know I can get the type of a method parameter by using "Method#parameters#name".
However, my parameters are all the subclass of A and I dont want to get the type A. I want to get the subclass name.
if (checkMethod(i)) {
val type = i.parameters[0].simpleName
if (!functions.containsKey(type)) {
functions[type] = HashMap()
}
if (!functions[type]?.containsKey(identifier)!!) {
functions[type]?.put(identifier, ArrayList())
}
functions[type]?.get(identifier)?.add(i)
}
Final Solution:
private fun analysis(clazz: KClass<EventHandler>, identifier: String) {
clazz.members.forEach {
if(it is KFunction) {
if(checkMethod(it)) {
val type = methodEventType(it)
if(!invokeMethods.containsKey(type)) invokeMethods[type] = HashMap()
if(!invokeMethods[type]!!.containsKey(identifier)) invokeMethods[type]!![identifier] = ArrayList()
invokeMethods[type]!![identifier]!!.add(it.javaMethod)
}
}
}
}
private fun checkMethod(method: KFunction<*>): Boolean {
method.annotations.forEach {
if(it is EventSubscriber) {
val type = method.parameters[1].type.classifier
if(type is KClass<*>) {
if(method.parameters.size == 2 && type.superclasses.contains(Event::class)) {
return true
}
}
}
}
return false
}
And notice here. I dont know why the method`s first parameter is allways a instance of its class. So the real parameter is start from 1 instead of 0.

Maybe you'll find this example useful (works with kotlin-reflect:1.4.21)
import kotlin.reflect.full.createType
import kotlin.reflect.full.isSubtypeOf
import kotlin.reflect.jvm.reflect
open class A
val aType = A::class.createType()
class B: A()
class C: A()
val foo = { b: B, c: C ->
println(b)
println(c)
}
println(foo.reflect()!!.parameters[0].type.classifier == B::class) // true
println(foo.reflect()!!.parameters[1].type.classifier == C::class) // true
println(foo.reflect()!!.parameters[0].type.isSubtypeOf(aType)) // true
To get all subclasses
println((foo.reflect()!!.parameters[0].type.classifier as KClass<*>).allSuperclasses.contains(A::class)) // true

Try this to get the class of the first parameter:
i.parameters[0]::class.java

Related

find value in arraylist in kotlin

Hey I am working in kotlin. I am working on tree data structure. I added the value in list and now I want to find that value and modified their property. But I am getting the error.
VariantNode, StrengthNode, ProductVariant
StrengthNode.kt
class StrengthNode : VariantNode() {
var pricePerUnit: String? = null
var defaultValue = AtomicBoolean(false)
}
ActivityViewModel.kt
class ActivityViewModel : ViewModel() {
var baseNode: VariantNode = VariantNode()
private val defaultValueId = "12643423243324"
init {
createGraph()
}
private fun createGraph() {
val tempHashMap: MutableMap<String, VariantNode> = mutableMapOf()
val sortedList = getSortedList()
sortedList.forEach { productVariant ->
productVariant.strength?.let { strength ->
if (tempHashMap.containsKey("strength_${strength.value}")) {
baseNode.children.contains(VariantNode(strength.value)) // getting error
return#let
}
val tempNode = StrengthNode().apply {
value = strength
pricePerUnit = productVariant.pricePerUnit?.value
if (productVariant.id == defaultValueId) {
defaultValue.compareAndSet(false, true)
}
}
baseNode.children.add(tempNode)
tempHashMap["strength_${strength.value}"] = tempNode
}
productVariant.quantity?.let { quantity ->
if (tempHashMap.containsKey("strength_${productVariant.strength?.value}_quantity_${quantity.value}")) {
return#let
}
val tempNode = QuantityNode().apply {
value = quantity
}
val parent =
tempHashMap["strength_${productVariant.strength?.value}"] ?: baseNode
parent.children.add(tempNode)
tempHashMap["strength_${productVariant.strength?.value}_quantity_${quantity.value}"] =
tempNode
}
productVariant.subscription?.let { subscription ->
val tempNode = SubscriptionNode().apply {
value = subscription
}
val parent =
tempHashMap["strength_${productVariant.strength?.value}_quantity_${productVariant.quantity?.value}"]
?: baseNode
parent.children.add(tempNode)
}
}
baseNode
}
}
I am getting error on this.
I want to find that node value and modified other property.
Your class VariantNode only has a single no-arg constructor, but you're trying to call it with arguments, hence the error
Too many arguments for public constructor VariantNode() defined in com.example.optionsview.VariantNode
Either you have to provide a constructor, that matches your call, e.g.
open class VariantNode(var value: ProductValue?) {
var children: MutableList<VariantNode> = arrayListOf()
}
or you need to adjust your code to use the no-arg constructor instead.
val node = VariantNode()
node.value = strength.value
baseNode.children.contains(node)
Note however, that your call to contains most likely will not work, because you do not provide a custom implementation for equals. This is provided by default, when using a data class.
If you just want to validate whether baseNode.children has any element, where value has the expected value, you can use any instead, e.g.:
baseNode.children.any { it.value == strength.value }

How can I check the constructur arguments of a mockk Mock?

I have the following code (in Kotlin):
class X {
fun foo() {
val A(1, true, "three")
val b = B()
b.bar(A)
}
}
What I want to to is find out what A has been instantiated with.
My test code looks like so:
// Needed for something else
every { anyConstructed<A>().go() } returns "testString"
// What I'm using to extract A
val barSlot = slot<A>()
verify { anyConstructed<B>().bar(capture(barSlot)) }
val a = barSlot.captured
How can I check what values A has been instantiated with now I've managed to capture the mock that was created when it was constructed (thanks to the every statement)?
Thanks!
You can do it in two ways:
Using slot to capture the parameter:
#Test
fun shouldCheckValuesAtConstruct() {
val a = A(1, true, "s")
val b = mockk<B>()
val aSlot = slot<A>()
every { b.bar(a = capture(aSlot)) } returns Unit
b.bar(a)
val captured = aSlot.captured
assertEquals(1, captured.a)
assertEquals(true, captured.b)
assertEquals("s", captured.s)
}
Or using withArg function and inline assertions
#Test
fun shouldCheckValuesAtConstructInlineAssertion() {
val a = A(1, true, "s")
val b = mockk<B>()
every { b.bar(a) } returns Unit
b.bar(a)
verify {
b.bar(withArg {
assertEquals(1, it.a)
assertEquals(true, it.b)
assertEquals("s", it.s)
})
}
}

How can i call an interface in kotlin?

I do not have a project in my work and they have asked me to give me a pass, but after passing the whole project, there is a part that has given me a code error at the moment. Clearly it's my first time in Kotlin and I have no idea, but I do have an idea. I tried to solve it and I have not succeeded. So I was asking for help. I get an error right at the beginning of the
= SpeechService.Lintener {
Here the code
private val mSpeechServiceListener = SpeechService.Listener { text: String?, isFinal: Boolean ->
if (isFinal) {
mVoiceRecorder!!.dismiss()
}
if (mText != null && !TextUtils.isEmpty(text)) {
runOnUiThread {
if (isFinal) {
if (mText!!.text.toString().equals("hola", ignoreCase = true) || b == true) {
if (b == true) {
mText!!.text = null
mTextMod!!.text = text
repro().onPostExecute(text)
random = 2
} else {
b = true
mText!!.text = null
val saludo = "Bienvenido, ¿que desea?"
mTextMod!!.text = saludo
repro().onPostExecute(saludo)
}
}
} else {
mText!!.text = text
}
}
}
}
and here the interface
interface Listener {
fun onSpeechRecognized(text: String?, isFinal: Boolean)
}
Please, help me. the error is "Interface Listener does not have constructor"
The SpeechService.Listener { } syntax for SAM interfaces is only possible when the interface is written i Java (see https://kotlinlang.org/docs/reference/java-interop.html#sam-conversions). Because the interface is written in Kotlin, you have to write it like this:
private val mSpeechServiceListener = object : SpeechService.Listener {
override fun onSpeechRecognized(text: String?, isFinal: Boolean) {
// Code here
}
}
You don't really need the SpeechService.Listener interface in Kotlin though. You could just use a lambda function. This depends on whether the interface comes from a library or if you've written it yourself though.
private val mSpeechServiceListener: (String?, Boolean) -> Unit = { text, isFinal ->
// Code here
}

Kotlin general setter function

I am new to kotlin. I wonder if this is possible
I wish to create a function that will change the value of the properties of the object and return the object itself. The main benefit is that I can chain this setter.
class Person {
var name:String? = null
var age:Int? = null
fun setter(propName:String, value:Any): Person{
return this.apply {
try {
// the line below caused error
this[propName] = value
} catch(e:Exception){
println(e.printStackTrace())
}
}
}
}
//usage
var person = Person(null,null)
person
.setter(name, "Baby")
.setter(age, 20)
But I get error "unknown references"
This question is marked as duplicate, however the possible duplicate question specifically want to change the property of "name", but I wish to change anyProperty that is pass from the function to object. Can't seem to connect the dot between two questions. #Moira Kindly provide answer that explain it. thankyou
Why not just simplify your answer to
fun setter(propName: String, value: Any): Person {
val property = this::class.memberProperties.find { it.name == propName }
when (property) {
is KMutableProperty<*> ->
property.setter.call(this, value)
null ->
// no such property
else ->
// immutable property
}
}
Java reflection isn't needed, its only effect is to stop non-trivial properties from being supported.
Also, if you call it operator fun set instead of fun setter, the
this[propName] = value
syntax can be used to call it.
After googling around, I think I can provide an answer, but relying on java instead of kotlin purely. It will be great if someone can provide a better answer in kotlin.
class Person(
var name: String,
val age: Int
){
fun setter(propName: String, value: Any): Person{
var isFieldExistAndNotFinal = false
try{
val field = this.javaClass.getDeclaredField(propName)
val isFieldFinal = (field.getModifiers() and java.lang.reflect.Modifier.FINAL == java.lang.reflect.Modifier.FINAL)
if(!isFieldFinal) {
// not final
isFieldExistAndNotFinal = true
}
// final variable cannot be changed
else throw ( Exception("field '$propName' is constant, in ${this.toString()}"))
} catch (e: Exception) {
// object does not have property
println("$e in ${this.toString()}")
}
if(isFieldExistAndNotFinal){
val property = this::class.memberProperties.find { it.name == propName }
if (property is KMutableProperty<*>) {
property.setter.call(this, value)
}
}
return this;
}
}
usage like this
person
.setter(propName = "age", value = 30.00)
.setter(propName = "asdf", value = "asdf")
.setter(propName = "name", value = "A Vidy")
You have error because when you do this[propName] = value you are trying to use this as a list, but it is not a list, it is a Person and it doesn't overload the [] operator.
What you can do is to add a check for the property that is setted:
class Person {
privavar name:String? = null
var age:Int? = null
fun setter(propName:String, value:Any): Person{
return this.apply {
if (propName == "name" && value is String?) {
it.name = value as String?
} else if (propName == "age" && value is Int?) {
it.age = value as Int?
} else {
// handle unknown property or value has incorrect type
}
}
}
}
Another more dynamic solution without reflection:
class Person {
private var fields: Map<String, Any?> = HashMap()
fun setter(propName:String, value:Any): Person{
return this.apply {
it.fields[propName] = value;
}
}
fun getName() = fields["name"]
}
If you want to get rid of the getters as well then you need to use reflection.

Singleton serialization in Kotlin

I'm wondering if it's possible in Kotlin to deserialize (restore property values) of a declared object, without having to manually assign the properties or resorting to reflection. The following snippet further explains:
object Foo: Serializable {
var propOne: String = ""
// ...
fun persist() {
serialize(this)
// no problem with serialization
}
fun restore(bytes: ByteArray) {
val fooObj: Foo = deserialize(bytes) as Foo
// It seems Kotlin allows us to use singleton as type!
// obvioulsly either of the following is wrong:
// this = fooObj
// Foo = fooObj
// ... is there a way to 'recover' the singleton (object) other than
// manual assignment of properties (or reflection) ???
}
}
There is no way to reassign the global reference to a singleton with a new instance. At most you can write out the properties during serialization, and then on deserialization directly read the properties and mutate the state in the original object. It will require custom code for you to assign the properties into the object either by direct assignment or reflection. It would be better if you make your own singleton mechanism that holds an instance that you can swap out to be another instance that you deserialize.
have faced with the same issue, and want to share with you my solution:
import com.fasterxml.jackson.databind.DeserializationFeature
import com.fasterxml.jackson.databind.ObjectMapper
import com.fasterxml.jackson.databind.SerializationFeature
import java.io.File
import java.lang.reflect.Modifier
typealias ObjMap = HashMap<String, Any?>
fun <T : Any> T.getInstance() : Any? {
val target = if(this is Class<*>) this else javaClass
return target.getDeclaredField("INSTANCE")?.get(null)
}
class ObjectHelper {
companion object {
val mapper = ObjectMapper().apply {
enable(SerializationFeature.INDENT_OUTPUT)
configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false)
}
fun objectToMap(obj: Any): ObjMap {
var res = ObjMap()
val instance = obj.getInstance()
val o = instance ?: obj
o.javaClass.declaredFields.forEach {
if(it.name != "INSTANCE") {
it.isAccessible = true
val value = if(Modifier.isStatic(it.modifiers)) it.get(null) else it.get(o)
res[it.name] = value
}
}
o.javaClass.classes.forEach {
res[it.simpleName] = objectToMap(it)
}
return res
}
fun saveObject(path: String, obj: Any) {
mapper.writeValue(File(path), objectToMap(obj))
}
fun loadObject(path: String, obj: Any) {
val json = mapper.readValue<HashMap<*,*>>(File(path), HashMap::class.java) as ObjMap
loadObject(obj, json)
}
fun loadObject(obj: Any, props: ObjMap) {
val objectParam = mapper.writeValueAsString(props)
mapper.readValue(objectParam, obj::class.java)
obj.javaClass.classes.forEach {
val instance = it.getInstance()
val map = props[it.simpleName]
if(map != null && instance != null) {
loadObject(instance, map as ObjMap)
}
}
}
}
}
Usage example:
object TestObj {
var f1: String = "f1"
var f2: String = "f2"
object TestObj_2 {
var f1: String = "f1_1"
var f2: String = "f2_2"
}
}
TestObj.f1 = "aaa"
saveObject("out.json", TestObj)
TestObj.f1 = "bbb"
loadObject("out.json", TestObj)
println(TestObj.f1)
Result will be "aaa".