How to Store Class in a Variable Kotlin - kotlin

i want to store a Class in a variable, the purpose is to check if other variable is an instanceOf the class
Here is My Code :
when (it.itemId) {
R.id.vend_list -> {
replace(R.id.fragmentContainer, vendingList)
id = VendListClass
}
R.id.label -> {
replace(R.id.fragmentContainer, label)
id = LabelClass
}
R.id.home -> {
replace(R.id.fragmentContainer, mainMenu)
id = MainMenuClass
}
R.id.statistic -> {
replace(R.id.fragmentContainer, statistic)
id = StatisticClass
}
else -> {}
}
for(fragment in supportFragmentManager.fragments){
if(fragment !is id){
remove(fragment)
}
}

I don't know your exact requirement. But it probably could be designed in other ways, enum? sealed classes? inheritances?
Anyway, straight to your question, hope this helps:
val listCls = List::class
val strCls = String::class
val listObj = listOf(1,2,3)
println("check listObj and listCls: ${listCls.isInstance(listObj)}")
println("check listObj and strCls: ${strCls.isInstance(listObj)}")
output:
check listObj and listCls: true
check listObj and strCls: false

You can store a class reference in a KClass<*> variable using ::class or in a Class<*> variable using ::class.java
So based on your original code, this is how you could do it
// this has to be a nullable type because of your else option
var id: KClass<*>? = null
when (it.itemId) {
R.id.vend_list -> {
replace(R.id.fragmentContainer, vendingList)
id = VendListClass::class
}
R.id.label -> {
replace(R.id.fragmentContainer, label)
id = LabelClass::class
}
R.id.home -> {
replace(R.id.fragmentContainer, mainMenu)
id = MainMenuClass::class
}
R.id.statistic -> {
replace(R.id.fragmentContainer, statistic)
id = StatisticClass::class
}
else -> {}
}
for(fragment in supportFragmentManager.fragments){
// if the else option from when has to remove all
// fragments, just flip the condition to
// if(id == null || id.isInstance(fragment))
if(id != null && id.isInstance(fragment)){
remove(fragment)
}
}

Thanks #Kent for helping, but I figured out how to solve this in an ineffective way.
Here is my code:
val obj1 = Object1()
val obj2 = Object2()
val obj3 = Object3()
val obj4 = Object4()
var id : SuperObject? = null
when(certainConditions) {
option1 -> id = Object1()
option2 -> id = Object2()
option3 -> id = Object3()
option4 -> id = Object4()
}
val otherObject = Object1()
if(id == otherObject) {
//process logic
}
I'm still looking for the more effective way though.
If anyone found out a better way, please share your answer.

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 }

Kotlin: How to get a type of a method parameter

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

Kotlin combined usage of arrow, lamda, ?, . , and let. what it mean?

fun theItemDTO.toDomainModel(
domainOrderId: String,
pIds: List<Long> = emptyList()
): theItem = let { dto ->
OrderProtoBuilders.theItem {
this.id = dto.id.toString()
skuId = dto.catalogEntryId.toString()
orderId = domainOrderId
quantity = dto.quantity
unitPrice = dto.unitPrice
totalPrice = dto.totalPrice
price = null
for (pId in pIds)
addpId(pId.toString())
dto.someMap[MAP_A]
?.let(::setAId)
dto.someMap[MAP_B]
?.let(::setBId)
}
}
For above kotlin function, my order of questions starts from the inner block code to the outter block:
1. what is dto.someMap[MAP_A]
?.let(::setAId) doing? It looks like a reassignment of someMap[MAP_A], but also a declaration of let something... what is it doing?
the meaning of so many { wrapper is the same as return. correct? Since I did read -> is like a function returning value automatically. So the end value of ...: theItem is really what OrderProtoBuilders.theItem {... created?
let { dto ->
OrderProtoBuilders.theItem {...
dto.someMap[MAP_A]?.let(::setAId) can be expanded like
val value = dto.someMap[MAP_A]
if (value != null) {
setAId(value)
}
2.
Yep, it's correct. You can think about it like
//the whole function will return item created by OrderProtoBuilders.theItem
fun theItemDTO.toDomainModel(
domainOrderId: String,
pIds: List<Long> = emptyList()
): theItem = let { dto ->
//return OrderProtoBuilders.theItem
OrderProtoBuilders.theItem {
//do some additional initialization of the Item
}
}

Kotlin How to use java streams .map() in kotlin to map a different object response

I am trying to map an array of objects to another array with different kind of objects, I used to do this using streams in java 8 it was pretty straight forward, instantiate an object set its values and return the object. I just switched to Kotlin and really sometimes is more confusing to do this kind of operations. All the examples I found are really simple and could not find something I want.
I have this BalanceMap class:
data class BalanceMap #JsonCreator constructor(
var balType: String,
var value: Any
)
I am getting the data from web service.
val balances: List<AcctBal> = res.getAcctBals();
the AcctBal class looks like this
public class AcctBal {
#SerializedName("CurAmt")
#Expose
private CurAmt curAmt;
#SerializedName("Desc")
#Expose
private String desc;
#SerializedName("ExpDt")
#Expose
private LocalDateTime expDt;
}
and try to map that response to var balanceList: List<BalanceMap>
balances.map {}
--> var balanceList: List<BalanceMap> = balances.map { t -> fun AcctBal.toBalanceMap() = BalanceMap(
balType = "",
value = ""
)}
I want to do something like this:
List<ProductDetail> details = acctBal.stream().filter(f -> f.getBalType() != null).map(e -> {
String bal = e.getBalType();
if (avalProductInfo.getBankId().equals("00010016")) {
bal = e.getBalType();
}
ProductDetail detail = new ProductDetail();
detail.setItem(bal);
if (e.getCurAmt() != null) {
detail.setValue(e.getCurAmt().getAmt().toString());
} else if (e.getRelationDt() != null) {
detail.setValue(e.getRelationDt().toGregorianCalendar().getTimeInMillis());
} else if (e.getMemo() != null) {
detail.setValue(e.getMemo());
}
return detail;
}).collect(toList());
I've been experimenting but is always wrong, any help will be highly appreciated. Happy coding!
some quick prototyping
details = acctBal
.filter{ f -> f.getBalType() != null }
.map { it -> mapToProductDetail (it) }
you can have a look here
Thanks to #Hakob Hakobyan for pointing in the right direction,
I left my solution like this:
fun mapRs(rs: AthProductResponse): BalanceByAccountRs {
val res = rs.getPartyAcctRelRec();
val balances: List<AcctBal> = res.getAcctBals();
val account = Account(res.getPartyAcctRelInfo().depAcctId.acctId, res.getPartyAcctRelInfo().depAcctId.acctType)
var balanceList: List<BalanceMap> = balances
.filter { f -> f.getDesc() != null }
.map { it -> mapToProductDetail(it) }
.toList()
return BalanceByAccountRs(account, balanceList)
}
fun mapToProductDetail(bal: AcctBal): BalanceMap {
var propertyValue: Long = 0L;
if(bal.getExpDt() != null) {
propertyValue = Timestamp.valueOf(bal.getExpDt()).getTime()
} else {
propertyValue = bal.getCurAmt().getAmt().toLong()
}
return BalanceMap(bal.getDesc(), propertyValue)
}
Just in case someone is going through the same. Happy coding

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.