Kotlin: ArrayList<Object> storing variables as values instead of reference [duplicate] - kotlin

This question already has answers here:
Java ArrayList copy
(10 answers)
Closed 2 years ago.
I'm having an issue where I assign a variable based on data that is being received from a server (in JSON).
I have two class variables
teams - which will be changed based on user interaction
reset_teams - which should always stay the same as the original list so that a user can reset all values whenever they choose.
These variables are used to (re-)populate a RecyclerView at various times based on user interaction (i.e., pressing a button inside of the app)
My problem is that any time I make a change to an object in the teams ArrayList, it also makes a change to the reset_teams ArrayList, as if they are somehow connected (similar to pass by value/reference in a function).
I am somewhat new to Kotlin, so it's quite possible I am missing something, but I'm wondering how I can keep the reset_teams ArrayList exactly the same at all times, even if the teams ArrayList has changes to different properties of the objects it stores?
// Class variables
var teams: ArrayList<myObject> = ArrayList()
var reset_teams: ArrayList<myObject> = ArrayList()
// Function to handle JSON after receiving data from remote server
private fun handleJson(jsonString: String?) {
val jsonArray = JSONArray(jsonString)
val list = ArrayList<myObject>()
var x = 0
while (x < jsonArray.length()) {
val jsonObject = jsonArray.getJSONObject(x)
list.add(
myObject(// a bunch of code to make an object of type myObject)
)
x++
}
teams = list
reset_teams = list
test() // Change a property
}
private fun test(){
teams[0].someProperty = 10
println(teams) // The someProperty of the first element of the ArrayList has been correctly assigned to 10
println(reset_teams) // The someProperty of the first element of the ArrayList has been changed to 10 even though the reset_teams was never assigned a new value
}

By assigning
teams = list
reset_teams = list
both list teams and list reset_teams reference the same list (space) in memory (heap). With teams[0].someProperty = 10 you change someProperty of the first element of this referenced list to 10. Since reset_teams references the same list, it also shows the changes.
In order to fix this unintended behaviour you will have to add copies of the objects to both lists teams and reset_teams. If class myObject is a data class, then you can add a copy by calling teams.add(m.copy()) where m is an instance of class myObject.
Remarks:
use camelCase for variable names: resetTeams instead of reset_teams
start class names with a capital letter: MyObject instead of myObject
prefer immutable objects if possible

Related

Hello guys, can someone explain to me the difference between a reference and a referenced object? [duplicate]

This question already has answers here:
Why can final object be modified?
(7 answers)
What is the difference between var and val in Kotlin?
(41 answers)
Closed 3 years ago.
can someone explain to me the difference between a reference and a referenced object?
Notice that the type of the variable reference (var, val) relates to the reference
itself, not the properties of the referenced object. This means that when using a
read-only reference (val), we will not be able to change the reference that is
pointing to a particular object instance (we will not be able to reassign variable
values), but we will still be able to modify properties of referenced objects. Let's
see it in action using an array:
val list = mutableListOf("a","b","c") //1
list = mutableListOf("d", "e") //2 Error
list.remove("a") //3
Initialize mutable list
The compiler will throw an error because value reference cannot be changed
(reassigned)
The compiler will allow to modify content of the list
The keyword val cannot guarantee that the underlying object is immutable.
Consider do following code:
data class ReferencedObject(
val valAttribute: String,
var varAttribute: String
)
fun main() {
val reference = ReferencedObject(
valAttribute = "cant change",
varAttribute = "can change"
)
// it doesn't compile because reference can't be reassigned
// val reference = ReferencedObject(
// valAttribute = "cant change",
// varAttribute = "can change"
// )
// it doesn't compile because valAttribute of referenced object can't be reassigned
// reference.valAttribute = "change"
// it works because the var attribute of referenced object can be reassigned
reference.varAttribute = "change"
}
In this example, reference is a final "variable" that points to an object of type ReferencedObject. The referenced object has two attributes: a valAttribute which is imutable and varAttribute which is not.
So, when you run reference.varAttribute = "change" you are not changing the reference variable, you are changing the referenced object.
This is what is happening in your example, when you run list.remove("a") you are changing the state of the MutableList object that is referenced by the list reference "variable".

Kotlin: Should mutableList be declared as Val or Var?

I am reading through the existing codebase for my team, and I notice mutableListOf are always declared as val. In some scenarios, elements are added to the mutableListOf only once. E.g
val jobList = mutableListOf<JobActivity>()
jobList.addAll(job.activities)
In other scenarios, elements are added to mutableListOf in a loop. E.g
val jobList = mutableListOf<JobActivity>()
newJobList.filterScanType(retrieveJobType(JobContext.NEW)).forEach {
jobList.add(it)
}
Since the list is not initialized on creation, why not declare mutableListOf as var? A lot of examples found online also follow the same pattern of declaring mutableListOf as val.
Which is best to use in the 2 scenarios described, val or var?
I think it's declared as val because the list will be the same always, the only thing that changes is it's elements. You'll never do something like:
joblist = anotherList
And as #Taseer said, the properties of the object can be changed even if it's a val. For example:
data class Example(var name: String)
val exampleObject = Example("SomeName")
You'll still be able to do this:
exampleObject.name = "AnotherName"
But you can't do this:
exampleObject = anotherObject
The general rule of thumb while using Kotlin.
Difference in val and var
You may already know the differences but for the sake of an answer, I will repeat it. var lets you modify the reference of an object while val does not permit to change the reference of an object.
An object can be declared safely using either var or val keyword but the reason why you want to use val on an object(in most cases) is that you don't want to refer that class member with a new reference of a new instance of an object. That way, you always keep a reference to the original object and you can modify object properties.
In the case of var, though nothing wrong with it, you can still use it 'without' any problems. You can still access the object properties and modify them and also you will be able to refer that class member to a reference of a new object.
Example:
val myObject = MyObject()
myObject.something = 1 //can still modify object property.
myOjbect = MyObject() //re-referencing the object, NOT POSSIBLE
var myNewObject = MyNewObject()
myNewObject.someThing = "Hello world!" //can still modify object properties
myNewObject = MyNewObject() //can still reference it.
Why to use val over var in case of 'immutable' objects?
It gives you the security of not 'accidentally' placing a new reference.
But is there any performance benefit using val?
Answer: Final keyword benefit
val is more idiomatic for the reasons given in other answers and comments.
You said the val is not instantiated, but in your example code, it is.
val jobList = mutableListOf<JobActivity>()
is a factory that instantiates an empty MutableList<JobActivity>
Using this pattern (val not var, instantiated upon declaration) ensures that your code will never find an uninitialized or null value for jobList; and the compiler can prove it.
In short - there are no rules, its up to you
if you use val you can modify mutableList, but not reassign
if you need reassign another list to same variable, use var. In most cases you dont need it, thats why your team uses it frequently
Whether a variable is var or val distinguishes between a variable of which the value (reference) can be changed (var = mutable) or not (val = immutable).
You should always strive to use val over var to avoid unwanted side-effects (changing it in another thread for example).
In case of the MutableList you should most likely use a val, because you don't want to mutate the reference to the list but rather its contents.
Here an overview of your options:
// Do you want to change its reference (r) / contents (c)?
var a = mutableListOf(1, 2, 3) // r = yes, c = yes
var b = listOf(1, 2, 3) // r = yes, c = no
val c = mutableListOf(1, 2, 3) // r = no, c = yes
val d = listOf(1, 2, 3) // r = no, c = no
You create a variable with var that is mutable (that can change). Mutable means variable can be changed in future.
val is used when variable will not be changed in future that means constant or final.
Here changed means new value or new things will be assigned to the variable but
val list = mutableListOf()
in this list variable you assigned mutable list. You just changed the value of the list. But you didn't assign new instance or new value to the variable you just added and remove value from the list. That's it. So here list is immutable itself.
It will be mutable if you do things like below...
var list = mutableListOf()
list = mutableListOf()
Two initialization on the same variable.

Kotlin multiple class for data storage

I am developing a simple Android app, that will display an icon of a vehicle and the user can click on the icon to display the vehicle information. I want to load the data dynamically when I build the app i.e. the data will come from an external source including the picture for the icon.
I am new to Kotlin and not sure what to search for to understand a suitable solution. What is the correct way to define the data, is it best to create an class as below then create an array of the class (not sure if this is possible)
public class VehicleSpec()
{
var OEM: String? = null
var ModelName: String? = null
var EngineSize: String? = null
}
Or would be better to create a multiple dimension array and then link the data to the cells?
var VehicleSpec = arrayOf(20,20)
VehicleSpec[0][0] = Null //OEM
VehicleSpec[0][1] = Null //ModelName
VehicleSpec[0][2] = Null //EngineSize
What is the best way to set up the data storage, is there any good references to understand how this should be setup?
What is the correct way to define the data, is it best to create an class as below then create an array of the class
Using an array for the properties of an object is not making the full use of the type safety you have in Kotlin (and even Java for that matter).
If what you want to express is multiple properties of an object, then you should use a class to define those properties. This is especially true if the properties have different types.
There is no performance difference between an array and a class, because you'll get a reference to the heap in both cases. You could save on performance only if you convert your multi-dimensional array approach to a single-dimension array with smart indexing. Most of the time, you should not consider this option unless you are handling a lot of data and if you know that performance is an issue at this specific level.
(not sure if this is possible)
Defining lists/arrays of classes is definitely possible.
Usually, for classes that are only used as data containers, you should prefer data classes, because they give you useful methods for free, and these methods totally make sense for simple "data bags" like in your case (equals, hashcode, component access, etc.).
data class Vehicle(
val OEM: String,
val ModelName: String,
val EngineSize: String
)
Also, I suggest using val instead of var as much as possible. Immutability is more idiomatic in Kotlin.
Last but not least, prefer non-null values to null values if you know a value must always be present. If there are valid cases where the value is absent, you should use null instead of a placeholder value like empty string or -1.
First at all, using the "class aprocah" makes it easy for you to understand and give you the full benefits of the language itself... so dont dry to save data in an array .. let the compiler handle those stuff.
Secondly i suggest you have maybe two types (and use data classes ;-) )
data class VehicleListEntry(
val id: Long,
val name: String
)
and
data class VehicleSpec(
val id: Long,
val oem: String = "",
val modelName: String = "",
val engineSize: String = ""
)
from my perspective try to avoid null values whenever possible.
So if you have strings - which you are display only - use empty strings instead of null.
and now have a Model to store your data
class VehicleModel() {
private val specs: MutableMap<Long, VehicleSpec> = mutableMapOf()
private var entries: List<VehicleListEntry> = listOf()
fun getSpec(id: Long) = specs[id]
fun addSpec(spec: VehicleSpec) = specs[spec.id] = spec
fun getEntries(): List<VehicleListEntry> = entries
fun setEntries(data: List<VehicleListEntry>) {
entries = data.toMutableList()
}
}
You could also use a data class for your model which looks like
data class VehicleModel(
val specs: MutableMap<Long, VehicleSpec> = mutableMapOf(),
var entries: List<VehicleListEntry> = listOf()
)
And last but not least a controller for getting stuff together
class VehicleController() {
private val model = VehicleModel()
init{
// TODO get the entries list together
}
fun getEntries() = model.entries
fun getSpec(id: Long) : VehicleSpec? {
// TODO load the data from external source (or check the model first)
// TODO store the data into the model
// TODO return result
}
}

in kotlin how to put function reference in an array

Having class member function like:
private fun getData1(uuid:String): IData? {
...
}
private fun getData2(uuid:String): IData? {
...
}
private fun getData3(uuid:String): IData? {
...
}
and would like to put in a function reference array:
var funArray = ArrayList<(uuid: String) -> IData?> (
this::getData1,
this::getData2,
this::getData3)
it does not compile:
None of the following functions can be called with the arguments
supplied:
public final fun <E> <init>(): kotlin.collections.ArrayList<(uuid: String) -> IData?> /* = java.util.ArrayList<(uuid: String) -> IData?> */ defined in kotlin.collections.ArrayList ...
if do:
var funArray: ArrayList<(uuid: String) -> IData?> = ArrayList<(uuid: String) -> IData?>(3)
funArray[0] = this::getData1 //<== crash at here
funArray[1] = this::getData2
funArray[2] = this::getData3
crash with exception
java.lang.IndexOutOfBoundsException: Index: 0, Size: 0
How to put function reference in an array?
The first attempt fails because ArrayList doesn't have a constructor taking (a variable argument list of) values.
You can get pretty much the same effect by replacing ArrayList with listOf() (or, if you need mutability, mutableListOf()), as that does take a vararg list:
var functions = listOf<(uuid: String) -> IData?>(
this::getData1,
this::getData2,
this::getData3)
That's perhaps the most natural solution.  (However, mutableListOf() is only guaranteed to return a MutableList implementation; it may not be an ArrayList.)
The second attempt fails because it's constructing an empty list.
(The ArrayList constructor it uses takes a parameter called initialCapacity; it ensures that the list could take at least 3 elements without needing to reallocate its arrays, but its initial size is zero.)
Perhaps the confusion is because although you say you ‘would like to put in a function reference array’, you're creating a List, not an Array.
(The ArrayList class is an implementation of the List interface which happens to use an array internally.  This follows the Java convention of naming implementation classes as <Implementation><Interface>.)
If you need to create an actual array, you could use arrayOf() in the first example:
var functions = arrayOf<(uuid: String) -> IData?>(
this::getData1,
this::getData2,
this::getData3)
Lists are probably used more widely than arrays in Kotlin, as they're more flexible.  (You can choose between many different implementations, with different characteristics.  They work better with generics; for example, you can create a List of a generic type.  You can make them immutable.  And of course if they're mutable, they can grow and shrink.)
But arrays have their place too, especially if performance is important, you need to interoperate with code that uses an array, and/or the size is fixed.

dataclass copy with field ArrayList - change the ArrayList of the copied class changes the original

I have a data class like this
data class Person(val id: Long = BaseDataContract.BaseData.UNDEFINED_ID.toLong(),
.....
val personConsents: ArrayList<PersonConsent> = ArrayList<PersonConsent>())
I have two copies of the object:
person = originalPerson.copy()
Then I change the elements of personConsents for the object person - I add/delete/edit them.
But by some reason I see that the same changes are happening in originalPerson object which I don't want to be. originalPerson is not supposed to be changed at all.
Suspect there is something with ArrayList references, but need your advice what i can do? At the end I need to compare two objects likefun dataChanged(): Boolean = originalPerson != person bu it doesn't work when ArrayList is changing.
I found a simple solution. I use my own clone function which creates a new object for ArrayList and fill it by copied elements.
fun getPersonClone(person: Person): Person {
val personConsents: ArrayList<PersonConsent> = ArrayList<PersonConsent>()
person.personConsents.forEach { personConsents.add(it.copy()) }
return Person(person.id, ......., personConsents)
}
So,this link here, will help you understand that the copy method in Kotlin, does not do a deep copy, it only does a shallow one. This is specially seen with non-primitive data types such as the ArrayList one you're using.
If you must use a method to copy the data class directly, what you can do is serialize it, and then de-serialize it.
I've done something like this below, using Gson.
Using the Data Class
data class Person(var name: String? = null,val cars : ArrayList<String> = ArrayList() )
The main method
fun main (args: Array<String>) {
var original =Person("Jon", arrayListOf("Honda City","VW Vento"))
var clone =Gson().fromJson(Gson().toJson(original), Person::class.java)
original.cars.add("VW Polo")
original.name = "Henry"
print(clone.cars) // Prints [Honda City, VW Vento]
}
This approach seems really hacky, and I'd encourage anything better.
The Kotlin copy method is a shallow copy. So your copy ends up referencing the exact same array as the original object.
I would say the simplest way to fix this is to implement the copy method yourself (no need to create an extra custom clone method):
data class Person(val id: Long = BaseDataContract.BaseData.UNDEFINED_ID.toLong(), val personConsents: ArrayList<PersonConsent> = ArrayList<PersonConsent>()) {
fun copy() = Person(this.id, ArrayList(this.personConsents))
}