Kotlin / value passing to List<>() - kotlin

I have a question in List<Contact>() I'm asked to pass init and size. I'm not sure if it's obligated to pass it as in my following tutorial ArrayList<String>() was empty, maybe it's because I was using List<>? Also, it doesn't recognize lowercase() and add() is it also related to List<>?
Code Snippet
val contacts = remember { DataProvider.contactList }
var filteredContacts: List<Contact>
val textState = remember { mutableStateOf(TextFieldValue("")) }
LazyColumn(
...
) {
val searchText = textVal.value.text
filteredContacts = if (searchText.isEmpty()){
contacts
}
else{
val resultList = List<Contact>()
for (contact in contacts) {
if (contact.lowercase(Locale.getDefault()).contains(searchText.lowercase(Locale.getDefault()))) {
resultList.add(contact)
}
}
resultList
}

In kotlin, List has no add method. For that you would need to have a MutableList.
Regarding lowercase method, this is available for Strings. You are trying to apply that to a Contact object, which I guess has no lowercase method.

Related

Kotlin Reflection on Object Expression

I have a top level kotlin object with various constants declared in it. How can I iterate and reflect on those properties/fields?
object Foobar {
val MEANINGFUL_CONSTANT = SomeClass(...)
#JvmStatic
val getConstants: List<SomeClass>
get() {
val props = Foobar::class.staticProperties
return props.mapNotNull { property ->
val p = property.get()
if (p is SomeClass) {
p
} else {
null
}
}
}
}
No matter what I put in for Foobar::class.staticProperties, I get an empty list back. How do I properly reflect on Foobar?
According to the doc of staticProperties
Only properties representing static fields of Java classes are considered static.
That is why staticProperties on Foobar isn't returning anything.
You can use memberProperties and have a condition to match the KType of SomeClass as below. Maybe that helps.
val getConstants: List<Any>
get() {
val props = Foobar::class.memberProperties
val someClassType = SomeClass::class.createType()
return props.mapNotNull { property ->
if (property.returnType == someClassType) {
property.getter.call(this)
} else {
null
}
}
}

Can I use State<ArrayList<T>> or State<mutableListOf()> for observed by Compose to trigger recomposition when they change?

The following content is from the article.
1: I don't understand fully if I can use State<ArrayList<T>> or State<mutableListOf()> for observed by Compose to trigger recomposition when they change?
2: I'm very strange why State<List<T>> and the immutable listOf() can be observed by Compose to trigger recomposition when they change but in fact List<T> and immutable listOf() are immutable, could you give me some sample codes?
Caution: Using mutable objects such as ArrayList or mutableListOf() as state in Compose will cause your users to see incorrect or stale data in your app.
Mutable objects that are not observable, such as ArrayList or a mutable data class, cannot be observed by Compose to trigger recomposition when they change.
Instead of using
non-observable mutable objects, we recommend you use an observable
data holder such as State<List> and the immutable listOf().
Image
The core concept is
Recomposition happens only when an observable state change happens.
For mutable objects, we have options to use add(), remove() and other methods and modify the object directly.
But the change would not trigger a recomposition as the change is not observable. (The object instance is NOT changed)
Even for mutable objects, we can trigger proper recomposition by assigning them to a new object instance. (The object instance is changed)
Hence using mutable objects is error-prone.
We can also, see a lint error due to this problem.
On the other hand, an immutable object like list can not be modified. They are replaced with a new object instance.
Hence they are observable and proper recomposition happens. (The object instance is changed)
Use this as an example to understand the concept.
#Composable
fun ComposeListExample() {
var mutableList: MutableState<MutableList<String>> = remember {
mutableStateOf(mutableListOf())
}
var mutableList1: MutableState<MutableList<String>> = remember {
mutableStateOf(mutableListOf())
}
var arrayList: MutableState<ArrayList<String>> = remember {
mutableStateOf(ArrayList())
}
var arrayList1: MutableState<ArrayList<String>> = remember {
mutableStateOf(ArrayList())
}
var list: MutableState<List<String>> = remember {
mutableStateOf(listOf())
}
Column(
Modifier.verticalScroll(state = rememberScrollState())
) {
// Uncomment the below 5 methods one by one to understand how they work.
// Don't uncomment multiple methods and check.
// ShowListItems("MutableList", mutableList.value)
// ShowListItems("Working MutableList", mutableList1.value)
// ShowListItems("ArrayList", arrayList.value)
// ShowListItems("Working ArrayList", arrayList1.value)
// ShowListItems("List", list.value)
Button(
onClick = {
mutableList.value.add("")
arrayList.value.add("")
val newMutableList1 = mutableListOf<String>()
mutableList1.value.forEach {
newMutableList1.add(it)
}
newMutableList1.add("")
mutableList1.value = newMutableList1
val newArrayList1 = arrayListOf<String>()
arrayList1.value.forEach {
newArrayList1.add(it)
}
newArrayList1.add("")
arrayList1.value = newArrayList1
val newList = mutableListOf<String>()
list.value.forEach {
newList.add(it)
}
newList.add("")
list.value = newList
},
) {
Text(text = "Add")
}
}
}
#Composable
private fun ShowListItems(title: String, list: List<String>) {
Text(title)
Column {
repeat(list.size) {
Text("$title Item Added")
}
}
}
P.S: Use mutableStateListOf if you have a list of items that needs to be modified as well as trigger recomposition properly.
I managed to do like this:
#Composable
fun ComposeListExample(
allObjects: List<Object>,
selectedObjects: List<Object>
) {
val selectedItems = remember {
mutableStateListOf<Object>().apply { addAll(selectedObjects) }
}
Column {
allObjects.forEach { item ->
SomeView(
title = item.title,
onSelect = {
if (selectedItems.contains(item)) {
selectedItems.remove(item)
} else {
selectedItems.add(item)
}
})
}
}
}

Expecting Member Declaration with ArrayList

I am trying to create an ArrayList in a class, like so:
class ConvertableTests : BaseTest(){
var categories = ArrayList<String>()
categories.add('a') <---- //Expecting member declaration error here
inner class ConvertableClass : Convertible...
Why I can't add objects to my array list after I initialize the list?
You can add items into the list after you initialize the list if you aren't doing so at the root scope of the class. Same as if you would have tried to do the same thing in Java.
i.e.
//this won't work, like you just found out
class Example {
var categories = ArrayList<String>()
categories.add("a") // this isn't inside a function or an `init` block
}
You need to put it inside of a function or an init block
fun functionExample() {
var categories = ArrayList<String>()
categories.add("a") // This would work fine
}
or
class Example {
var categories = ArrayList<String>()
init {
categories.add("a")
}
}
To elaborate on Sergey's example of the apply and why that works if you don't do it inside of a function or an init
class Example {
var categories = ArrayList<String>().apply {
add("a")
}
}
The kotlin compiler is performing an optimization and it's actually treating this as if you were putting it into an init block. If you decompile this and see what's happening, it's actually doing this
/// This is what it compiles back up to in Java
public Example() {
ArrayList var8 = new ArrayList();
var8.add("a");
this.category = var8;
}
Which is the same thing that happens when you use the init block.
Hope that helps!
You can use init block to initialize array:
class ConvertableTests : BaseTest() {
var categories = ArrayList<String>()
init {
categories.add("a")
}
// ...
}
Or apply extension function:
var categories = ArrayList<String>().apply {
add("a")
}
Also you should use double quotes " to add a String:
var categories = ArrayList<String>()
categories.add("a")
Single quotes are used for Chars:
var categories = ArrayList<Char>()
categories.add('a')

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.

Split algorithm and view part using a Strategy Pattern in Kotlin

This is the code I would like to refactor:
val postListener = object : ValueEventListener {
override fun onDataChange(dataSnapshot: DataSnapshot) {
// Get Post object and use the values to update the UI
requestsUsers?.clear()
val match = dataSnapshot.children
val keysArray = KeysHandler()
if (match != null) {
for (data in match) {
keysArray.addToList(data.key)
}
if (keysArray.list.size > 0) {
repeat(keysArray.list.size) { i ->
val onlineMatch = dataSnapshot.child(keysArray.getElement(i)).getValue(OnlineMatch::class.java)!!
onlineMatch.key = keysArray.list[i]
requestsUsers.add(onlineMatch)
}
}
}
//Updating GUI
updateRequests()
}
As you can see I am downloading data in an array called match. Then I parse the same array obtaining an array of keys (keysArray). Then I add a specific element of the keys array to another array (requestsUser).
Considering that this algorithm could be changed I would like to incapsulate the algorithm part in another class. I read somewhere that in these kind of situation the best thing to do is to use a Strategy Pattern, but I am working in kotlin. How could I implement a Strategy Pattern in Kotlin?
It should be similar to Java.
Suppose the type of requestsUsers is ArrayList<RequestsUser>.
Create the strategy interface.
interface Strategy {
fun getRequestsUsers(dataSnapshot: DataSnapshot): ArrayList<RequestsUser>
}
Implement the interface.
class StrategyImpl: Strategy {
override fun getRequestsUsers(dataSnapshot: DataSnapshot): ArrayList<RequestsUser> {
val match = dataSnapshot.children
val keysArray = KeysHandler()
val requestsUsers = arrayListOf<RequestsUser>()
if (match != null) {
for (data in match) {
keysArray.addToList(data.key)
}
if (keysArray.list.size > 0) { //this line can be omitted
repeat(keysArray.list.size) { i ->
val onlineMatch = dataSnapshot.child(keysArray.getElement(i)).getValue(OnlineMatch::class.java)!!
onlineMatch.key = keysArray.list[i]
requestsUsers.add(onlineMatch)
}
}
}
return requestsUsers
}
}
Declare the strategy in your class
var strategy = StrategyImpl() //make it var so that it can be changed
Finally, use strategy to get the list of data and add to the list.
val postListener = object : ValueEventListener {
override fun onDataChange(dataSnapshot: DataSnapshot) {
// Get Post object and use the values to update the UI
requestsUsers?.clear()
requestsUsers?.addAll(strategy.getRequestsUsers(dataSnapshot))
//Updating GUI
updateRequests()
}
}