In Kotlin, how do I create an instance of a class where the class name is a variable? - kotlin

I am writing a Graph class in Kotlin. Here's my code so far:
import java.util.*
class Graph<Class>(connectionProb: Double) {
// a general network class
val nodes = mutableListOf<Class>()
val connectionProb = connectionProb
val connections = hashMapOf<Class, MutableList<Class>>()
var nextID = 0
private fun createNode() {
// Here is where I need help
}
}
I want to be able to specify the node type for each Graph. The code above will compile, but I'm unsure how to initialize new nodes. The only argument I want to pass into each node, regardless of its type, is nextID.
Thanks!

First thing you should do is define a class for your graph node, and instead of maintaining a map for connections, let every node maintain a list of its connections.
So your code would look like this
class Graph<T>(connectionProb: Double) {
var nextId = 0
val nodes = mutableListOf<Node<T>>()
// Define a node class, so if you have graph of Integer then you want a node of Integer
class Node<T>(var id: Int){
val connections = mutableListOf<Node<T>>()
fun addConnection(newConnection: Node<T>){
connections.add(newConnection)
}
override fun toString() = "$id"
/** add other functions, such as remove etc */
}
fun createNode(){
var newNode = Node<T>(nextId++)
nodes.add(newNode)
}
}
Now to use this graph you will do followig
fun main(){
var myGraph = Graph<Integer>(10.9)
myGraph.createNode()
myGraph.createNode()
myGraph.nodes[0].addConnection(myGraph.nodes[1])
/* Print all nodes in graph */
for(node in myGraph.nodes) print(" $node")
/* Print connections of first node */
for(node in myGraph.nodes[0].connections) print("\n\nConnection id: $node")
}
Please note that this will give you a general idea of how to go about implementing graph and there are many things that you can improve in the implementation. To learn more I suggest you read a good book, such as Data Structures and Algorithms in Java

Related

Kotlin — creating new instance with copy() does not prevent methods called on copied instance from modifying original instance [duplicate]

Could someone explain how exactly the copy method for Kotlin data classes work? It seems like for some members, a (deep) copy is not actually created and the references are still to the original.
fun test() {
val bar = Bar(0)
val foo = Foo(5, bar, mutableListOf(1, 2, 3))
println("foo : $foo")
val barCopy = bar.copy()
val fooCopy = foo.copy()
foo.a = 10
bar.x = 2
foo.list.add(4)
println("foo : $foo")
println("fooCopy: $fooCopy")
println("barCopy: $barCopy")
}
data class Foo(var a: Int,
val bar: Bar,
val list: MutableList<Int> = mutableListOf())
data class Bar(var x: Int = 0)
Output:
foo : Foo(a=5, bar=Bar(x=0), list=[1, 2, 3])
foo : Foo(a=10, bar=Bar(x=2), list=[1, 2, 3, 4])
fooCopy: Foo(a=5, bar=Bar(x=2), list=[1, 2, 3, 4])
barCopy: Bar(x=0)
Why is barCopy.x=0 (expected), but fooCopy.bar.x=2 (I would think it would be 0). Since Bar is also a data class, I would expect foo.bar to also be a copy when foo.copy() is executed.
To deep copy all members, I can do something like this:
val fooCopy = foo.copy(bar = foo.bar.copy(), list = foo.list.toMutableList())
fooCopy: Foo(a=5, bar=Bar(x=0), list=[1, 2, 3])
But am I missing something or is there a better way to do this without needing to specify that these members need to force a deep copy?
The copy method of Kotlin is not supposed to be a deep copy at all. As explained in the reference doc (https://kotlinlang.org/docs/reference/data-classes.html), for a class such as:
data class User(val name: String = "", val age: Int = 0)
the copy implementation would be:
fun copy(name: String = this.name, age: Int = this.age) = User(name, age)
So as you can see, it's a shallow copy. The implementations of copy in your specific cases would be:
fun copy(a: Int = this.a, bar: Bar = this.bar, list: MutableList<Int> = this.list) = Foo(a, bar, list)
fun copy(x: Int = this.x) = Bar(x)
Beware of those answers who are just copying list reference from an old object into the new one. One quick way (not very efficient, though) of deep copying is to serialize/deserialize objects i.e. convert the objects into JSON and then transform them back to POJO.
If you are using GSON, here is a quick piece of code:
class Foo {
fun deepCopy() : Foo {
return Gson().fromJson(Gson().toJson(this), this.javaClass)
}
}
As #Ekeko said, the default copy() function implemented for data class is a shallow copy which looks like this:
fun copy(a: Int = this.a, bar: Bar = this.bar, list: MutableList<Int> = this.list)
To perform a deep copy, you have to override the copy() function.
fun copy(a: Int = this.a, bar: Bar = this.bar.copy(), list: MutableList<Int> = this.list.toList()) = Foo(a, bar, list)
There is a way to make a deep copy of an object in Kotlin (and Java): serialize it to memory and then deserialize it back to a new object. This will only work if all the data contained in the object are either primitives or implement the Serializable interface
Here is an explanation with sample Kotlin code https://rosettacode.org/wiki/Deepcopy#Kotlin
import java.io.Serializable
import java.io.ByteArrayOutputStream
import java.io.ByteArrayInputStream
import java.io.ObjectOutputStream
import java.io.ObjectInputStream
fun <T : Serializable> deepCopy(obj: T?): T? {
if (obj == null) return null
val baos = ByteArrayOutputStream()
val oos = ObjectOutputStream(baos)
oos.writeObject(obj)
oos.close()
val bais = ByteArrayInputStream(baos.toByteArray())
val ois = ObjectInputStream(bais)
#Suppress("unchecked_cast")
return ois.readObject() as T
}
Note: This solution should also be applicable in Android using the Parcelable interface instead of the Serializable. Parcelable is more efficient.
Building on a previous answer, an easy if somewhat inelegant solution is to use the kotlinx.serialization facility. Add the plugin to build.gradle as per the docs, then to make a deep copy of an object, annotate it with #Serializable and add a copy method which converts the object to a serialised binary form, then back again. The new object will not reference any objects in the original.
import kotlinx.serialization.Serializable
import kotlinx.serialization.cbor.Cbor
#Serializable
data class DataClass(val yourData: Whatever, val yourList: List<Stuff>) {
var moreStuff: Map<String, String> = mapOf()
fun copy(): DataClass {
return Cbor.load(serializer(), Cbor.dump(serializer(), this))
}
This won't be as fast as a handwritten copy function, but it does not require updating if the object is changed, so is more robust.
I face the same problem. Because in kotlin, ArrayList.map {it.copy} not copying all items of an object specially if a member is list of another object inside this.
The only solution, for deep copying of all items of an object I found on the web, is to serialize and deserialize the object when you send or assign it to a new variable. Code like as follows.
#Parcelize
data class Flights(
// data with different types including the list
) : Parcelable
Before I receiving List of Flights, We can use JSON to deserialize the Object and serialize the object same time!!!.
First, we create two extension functions.
// deserialize method
fun flightListToString(list: ArrayList<Flights>): String {
val type = object : TypeToken<ArrayList<Flights>>() {}.type
return Gson().toJson(list, type)
}
// serialize method
fun toFlightList(string: String): List<Flights>? {
val itemType = object : TypeToken<ArrayList<Flights>>() {}.type
return Gson().fromJson<ArrayList<Flights>>(string, itemType)
}
We can use it like below.
// here I assign list from Navigation args
private lateinit var originalFlightList: List<Flights>
...
val temporaryList = ArrayList(makeProposalFragmentArgs.selectedFlightList.asList())
originalFlightList = toFlightList(flightListToString(temporaryList))!!
Later, I send this list to Recycler Adapter & there the content of the Flights object would be modified.
bindingView.imageViewReset.setOnClickListener {
val temporaryList = ArrayList(makeProposalFragmentArgs.selectedFlightList.asList())
val flightList = toFlightList(flightListToString(temporaryList))!!
**adapter**.resetListToOriginal(flightList)
}
Maybe you can use kotlin reflection in some way here, this example is not recursive but should give the idea:
fun DataType.deepCopy() : DataType {
val copy = DataType()
for (m in this::class.members) {
if (m is KProperty && m is KMutableProperty) {
m.setter.call(copy, if (m.returnType::class.isData) {
(m.getter.call(this) to m.returnType).copy()
} else m.setter.call(copy, m.getter.call(this)))
}
}
return copy
}
If you use Jackson and not concerned about performance,then this simple extension function will give you this feature.
private val objectMapper = ObjectMapper()
.registerModule(KotlinModule())
.registerModule(JavaTimeModule())
fun <T> Any.copyDeep(): T {
return objectMapper.readValue(objectMapper.writeValueAsString(this), this.javaClass) as T
}
Use this function:
private val gson = Gson()
fun <T> deepCopy(item: T?, clazz: Class<T>): T {
val str = gson.toJson(item)
return gson.fromJson(str, clazz)
}
What you want is a deep copy. There are many tools available to do this.
MapStruct: https://mapstruct.org/
Mapstruct generates code at compile time. Normally, it is to auto-generate mappers between java objects, but it also has a 'clone' functionality to create a deep copy of an object. Since this is generated code of what you'd manually write, it is the fastest way to achieve this.
There are many more (kryo, dozer, etc...), you can actually just google, for example here: https://programmer.group/performance-comparison-between-shallow-and-deep-copies.html
DO AVOID serialization-based 'clone': apache commons' SerializationUtils, jackson, gson, etc... They have a huge overhead, since it first creates a middle state. They are about 10-100 times slower than actual copying.

Kotlin Generics - It is possible get integer position of an element of whatever type passed like ordinals method does for Enum defined classes?

There's an already built in way that fulfill my request?
I know Set are unordered
I need to do a vector based Set.
I need to know the position of any value of a generic type to place them in the corresponding index of the array, So I can avoid duplications of the elements.
I'm not ordering or defining an order of the Set.
I don't have any operators that missbehave or break any Set Costraint
Please note that I know this implementation is not efficient for any types that seems infinite like Integers.
I need to do it for an educational purpose.
I have already implemented List Ordered and Hash Table based ones.
For now I have this class, that works flawlessy:
package ads
class MySet<E : Enum<E>> {
//More details for clarify
private val maxSet = 127
private var myset = arrayOfNulls<Boolean>(maxSet)
private fun getOrdinal(eelement : E) : Int{
return eelement.ordinal
}
/*
more set operators that needs of getOrdinal
*/
fun insert(xelement: E){
myset[getOrdinal(xelement)] = true
}
}
import ads.MySet as RawSet
enum class MyColors{
Red,
Green,
Blue,
Yellow,
Black,
Mint;
}
fun main() {
val myfavc = RawSet<MyColors>()
val yourfavc = RawSet<MyColors>()
//Following operations...
myfavc.insert(MyColors.Red)
yourfavc.insert(MyColors.Blue)
}
I need now to make another class in the same way but working with whatever abstract type already defined in kotlin.
package ads
class MySet<T> {
//More details for clarify
private val maxSet = 127
private var myset = arrayOfNulls<Boolean>(maxSet)
private fun getOrdinal(telement : T) : Int{
/*
For any abstract type return the order of any element
checks if the integer is not greater than maxSet otherwhise it
throws an Exception or manages this istance in other way
*/
}
/*
more set operators that needs of getOrdinal
*/
fun insert(xelement: T){
myset[getOrdinal(xelement)] = true
}
}
import ads.MySet as RawSeT
//Istance using Int
fun main() {
val myfav = RawSet<Int>()
val yourfav = RawSet<Int>()
//Following operations...
myfav.insert(11)
yourfav.insert(123)
}
I guess there's no built-in way to do so in a generic way.
but I'm still learning kotlin, so maybe I'm missing something useful.
I'm not asking to do my paper.
I don't need a full alternative solutions that I should find out by myself but
I'm opened to read about any tips or resources that can help me to clarify how abstract types works in Kotlin(or Programming Languages) infos like :
How are ordered.
If any value of any type can be compared based on their position
(like c > a for chars).
Range of values/Max values represented for any "apparently
infinite" type like Integers.
Thanks you!
P.S. = Please consider that I'm not an english native, be patient!
Enums are implicitly ordered in declaration order and amount of instances of each enum is finite, so they could be ordered globally and their ordinal could be represented as Int.
Ints (as well as Bytes and Chars) are naturally ordered and have ranges (Int.MIN_VALUE..Int.MAX_VALUE, etc.), so each of them could have an ordinal represented as Int too.
All types implementing Comparable interface could be compared in pairs and any subset of their instances could be ordered, but it doesn't mean that each of them have some global Int ordinal among all possible instances, because set of Ints is finite (2^32 items), and set of all unique instances of generic type T could be countably infinite (like BigInteger) or even uncountable (like Double) (see wiki about cardinality).
All other types couldn't be even compared in pairs (without respectful Comparator<T>).
So you need to manually limit instances of each T that are about to be added in your set and either maually order them or provide respectful Comparator<T> to construct a Map<T, Int> which you'll need to use for subsequent ordinal evaluation:
class SetOfSomehowOrderedInstancesOfType<T>(private val order: Map<T, Int>) {
private val maxSet = order.size
private var myset = BooleanArray(order.size)
private fun getOrdinal(eelement: T): Int {
return order[eelement] ?: throw RuntimeException("Order unknown")
}
fun insert(xelement: T) {
myset[getOrdinal(xelement)] = true
}
}
Usage:
fun main() {
val myFavouriteRealNumbersInMyFavouriteOrder =
listOf(99.2123, -2355.12, 1.1, 3.14, 100.0, 123214214215.123331322145)
val myfavc = SetOfSomehowOrderedInstancesOfType<Double>(myFavouriteRealNumbersInMyFavouriteOrder.mapToIndex())
myfavc.insert(99.2123) //will be inserted with ordinal = 0
val myFavouriteRealNumbersInNaturalOrder = myFavouriteRealNumbersInMyFavouriteOrder.sorted()
val yourfavc = SetOfSomehowOrderedInstancesOfType<Double>(myFavouriteRealNumbersInNaturalOrder.mapToIndex())
yourfavc.insert(99.2123) //will be inserted with ordinal = 3
}
Alternatively you may define Orderable and Ordinator<T> interfaces (similar to Comparable<T> and Comparator<T>) and determine ordinal using them:
fun interface Ordinator<T> {
fun getOrderOf(x : T) : Int
}
interface Orderable {
val order : Int
}
class MySet<T>(private val ordinator: Ordinator<T>? = null) {
private val maxSet = 127
private var myset = BooleanArray(maxSet)
private fun getOrdinal(eelement: T) = when {
eelement is Orderable -> eelement.order
ordinator != null -> ordinator.getOrderOf(eelement)
else -> throw RuntimeException()
}
fun insert(xelement: T) {
myset[getOrdinal(xelement)] = true
}
}
Also you may define auxilary function, generalizing previous approach:
fun <T> ordinatorOf(order: List<T>) = object : Ordinator<T> {
private val order = order.mapToIndex()
override fun getOrderOf(x: T) = this.order[x] ?: throw RuntimeException()
}
Usage:
val stringsOrderedByTheirLength = MySet<String> { it.length }
stringsOrderedByTheirLength.insert("aaa") //will be inserted with ordinal = 3
val myFavouriteRealNumbersInMyFavouriteOrder =
listOf(99.2123, -2355.12, 1.1, 3.14, 100.0, 123214214215.123331322145)
val myfavc = MySet(ordinatorOf(myFavouriteRealNumbersInMyFavouriteOrder))
myfavc.insert(99.2123) //will be inserted with ordinal = 0
As somebody pointed out to me that some abstract types representing for an istance Real Numbers can't be done so easily.
The implementation with the enum class works flawlessy but it's been refused from my university professor because I need to use the same syntax for every implementation of the same Data Structure.
Other implementation I have
- HashTableSet<T>
- OrderedListSet<T>
they ask only for an abstract type that can be whatever type, they will work without problem.
I need to do it the same with the
ArraySet<E : Enum<E>>
So if this can't be done easily with Generics, directly...
I'm thinking about mixing them.
Like whatever T type I have
it creates an object called "Domain" so whatever element it insert, before placing them in the array of the set, it place them inside there in a specific order so it simulates what enum it was doing.
I guess that Enumerations can't be defined during the runtime, dinamically.
Then probably I have to define Domain<T> as a private class/object(not sure) that :
Collect the element of the istance of ArraySet(above MySet) inserts with its opeator .insert()
It orders the element comparing to the ones that are already inside
Once is ordered delete all the duples
Re-arrange the whole Boolean Array everytime insert is used based on how Domain grows. (or find out a different algo that partially re-arrange the boolean array)
What do you think?
Advice me more, thanks.

Chained Variables / Anchor Variable and Error: Only a chained variable supports a singleton inverse

Preface: Google+SO+docu search did not appear to give relevant information.
Domain Model:
My domain model tries to picture an ProductionPlan, containing a List of Machines.
Each Machine has a list of chained jobs, thus a job which has a getNextEntry():Job Method, creating a list of jobs.
I tried to solve this problem with chained PlanningVariables, but apparently do not understand the concept of chainedVariables/shadowVariables/anchorVariables.
With my understanding, all jobs are getting chained and the anchorShadowVariable points to the beginning of the list, thus the machine.
To implement chaining, Job and Machine need to implement an interface or extend superclass, thus i created ChainSuperClass. Additionally i do not know if i have to overwrite getter/setter to set Annotations in Machine-Class, my guess was that because Machine extends ChainSuperClass, those Annotations carry over.
Edit: Kotlin specific improvements are also appreciated.
The full error log of my code execution is now:
Exception in thread "main" java.lang.IllegalArgumentException: The entityClass (class optaplanner.productionPlan.domain.ChainSuperClass) has a InverseRelationShadowVariable annotated property (nextEntry) which does not return a Collection with sourceVariableName (machine) which is not chained. Only a chained variable supports a singleton inverse.
ChainSuperClass:
#PlanningEntity
abstract class ChainSuperClass {
#PlanningId
open val id = Random.nextInt().toString()
#InverseRelationShadowVariable(sourceVariableName = "machine")
abstract fun getNextEntry(): Job?
abstract fun setNextEntry(job: Job)
}
Job:
#PlanningEntity
class Job(
val jobType: JobType,
val itemNumber: String,
val orderNumber: String,
val setupTime: Int,
val productionTime: Int
) : ChainSuperClass() {
#AnchorShadowVariable(sourceVariableName = "machine")
var machine: Machine? = null
private var nextEntry: Job? = null
#PlanningVariable(
valueRangeProviderRefs = ["jobList"],
graphType = PlanningVariableGraphType.CHAINED
)
override fun getNextEntry(): Job? {
return nextEntry
}
override fun setNextEntry(job: Job) {
this.nextEntry = nextEntry
}
}
Machine:
class Machine(override val id: String, val jobTypes: List<JobType>) : ChainSuperClass() {
private var nextEntry: Job? = null
override fun setNextEntry(job: Job) {
this.nextEntry = job
}
override fun getNextEntry(): Job? {
return nextEntry!!
}
}
I think the most important thing to realize with chained variables is this: When you have an entity, say Job A and solver assigns a value (job/machine) to its variable, it's not like the chain is being built forward starting from Job A. It's the other way around. By assigning a value to Job A's planning variable, Job A gets connected at the end of an existing chain.
Please take a look at the documentation to find more details about chaining and examples of valid chains.
By having understood this it should be clear that Job's planning variable name should be something like previousJobOrMachine (you'll probably want something simpler, for example previousStep) whereas the nextJob property is an inverse relation shadow variable derived from that (so when Job X gets connected to an existing chain ending with Job C by assigning Job X.previousStep=Job C, an inverse relation is established automatically: Job C.nextJob=Job X).
Based on that information, your model should look more like this:
#PlanningEntity
abstract class ChainSuperClass {
#PlanningId
open val id = Random.nextInt().toString()
// changed sourceVariableName to point to the planning variable
#InverseRelationShadowVariable(sourceVariableName = "previousStep")
abstract fun getNextEntry(): Job?
abstract fun setNextEntry(job: Job)
}
#PlanningEntity
class Job(
val jobType: JobType,
val itemNumber: String,
val orderNumber: String,
val setupTime: Int,
val productionTime: Int
) : ChainSuperClass() {
// changed sourceVariableName to point to the planning variable
#AnchorShadowVariable(sourceVariableName = "previousStep")
var machine: Machine? = null
// added planning variable
private var previousStep: ChainSuperClass? = null
private var nextEntry: Job? = null
#PlanningVariable(
// added machineList value range provider
valueRangeProviderRefs = ["jobList", "machineList"],
graphType = PlanningVariableGraphType.CHAINED
)
// getter for the new planning variable
fun getPreviousStep(): ChainSuperClass {
return previousStep
}
override fun getNextEntry(): Job? {
return nextEntry
}
override fun setNextEntry(job: Job) {
this.nextEntry = nextEntry
}
}
Notice that I added machineList as the source of possible values of the previousStep planning variable as previous step might be either a Job at the end of a non-empty chain or a Machine representing an empty chain.
Your domain is very similar to the vehicle routing domain, where you can see a working example of chaining. With good understanding of chaining principles you should be able to replicate it on your domain.

Giving User-Defined properties for a signal

Disclaimer: I'm not super familiar with Scala. I have used Spinal/Chisel some to test out what I want to do, as well as create some simple HW, so please forgive me if these questions are "entry-level" from a Scala perspective.
I'm planning to use SpinalHDL for some connection automation. We have a bunch of IP that we can't/won't convert to SpinalHDL and much of it is mixed-signal. Because of this, I have some situations where additional signal information would allow me to further automate the design process. As an example, there are cases where some signals are on various power domains and their connections either need to be limited to said specific power domain or we would have to insert some logic like a level shifter.
Let's say I have a bundle of signals for some IP (this Bundle is on one of my BlackBoxes)
class MyBundle extends Bundle{
val data = in UInt(8 bits)
val valid = in Bool
val pwr800mV = in Bool
val pwr1800mv= in Bool
val gnd = in Bool
}
MyBundle is on a BlackBox, and I instantiate several of these in a Component. My idea is to be able to walk through the IOs/Signals in the Bundle and connect them to the appropriate higher level power/ground signal. Example:
class PwrSignal(v : Double = 1.8) extends Bool{
val voltage = v
}
class MyBundle extends Bundle{
val data = in UInt(8 bits)
val valid = in Bool
val pwr800mV = in PwrSignal(0.8)
val pwr1800mv= in PwrSignal(1.8)
val gnd = in Bool
}
class MyComponent extends Component{
//IO creation
//pseudo...
walkBundleSignals{
if (signal.voltage == 1.8){ signal := top_level_power_1p8v}
}
}
At first I tried to create a PwrSignal class that extends Bool, however this fails since PwrSignal is not a part of spinal.core.in.
Is there anyway to go about doing this?
Thanks
Derp Alert: Since the default data types override the constructor, you just need to "new" the user defined data type.
class PwrSignal(v : Double = 1.8) extends Bool{
val voltage = v
}
class MyBundle extends Bundle{
val data = in UInt(8 bits)
val valid = in Bool
val pwr800mV = in (new PwrSignal(0.8))
val pwr1800mv= in (new PwrSignal(1.8))
val gnd = in Bool
}

Kotlin data class copy method not deep copying all members

Could someone explain how exactly the copy method for Kotlin data classes work? It seems like for some members, a (deep) copy is not actually created and the references are still to the original.
fun test() {
val bar = Bar(0)
val foo = Foo(5, bar, mutableListOf(1, 2, 3))
println("foo : $foo")
val barCopy = bar.copy()
val fooCopy = foo.copy()
foo.a = 10
bar.x = 2
foo.list.add(4)
println("foo : $foo")
println("fooCopy: $fooCopy")
println("barCopy: $barCopy")
}
data class Foo(var a: Int,
val bar: Bar,
val list: MutableList<Int> = mutableListOf())
data class Bar(var x: Int = 0)
Output:
foo : Foo(a=5, bar=Bar(x=0), list=[1, 2, 3])
foo : Foo(a=10, bar=Bar(x=2), list=[1, 2, 3, 4])
fooCopy: Foo(a=5, bar=Bar(x=2), list=[1, 2, 3, 4])
barCopy: Bar(x=0)
Why is barCopy.x=0 (expected), but fooCopy.bar.x=2 (I would think it would be 0). Since Bar is also a data class, I would expect foo.bar to also be a copy when foo.copy() is executed.
To deep copy all members, I can do something like this:
val fooCopy = foo.copy(bar = foo.bar.copy(), list = foo.list.toMutableList())
fooCopy: Foo(a=5, bar=Bar(x=0), list=[1, 2, 3])
But am I missing something or is there a better way to do this without needing to specify that these members need to force a deep copy?
The copy method of Kotlin is not supposed to be a deep copy at all. As explained in the reference doc (https://kotlinlang.org/docs/reference/data-classes.html), for a class such as:
data class User(val name: String = "", val age: Int = 0)
the copy implementation would be:
fun copy(name: String = this.name, age: Int = this.age) = User(name, age)
So as you can see, it's a shallow copy. The implementations of copy in your specific cases would be:
fun copy(a: Int = this.a, bar: Bar = this.bar, list: MutableList<Int> = this.list) = Foo(a, bar, list)
fun copy(x: Int = this.x) = Bar(x)
Beware of those answers who are just copying list reference from an old object into the new one. One quick way (not very efficient, though) of deep copying is to serialize/deserialize objects i.e. convert the objects into JSON and then transform them back to POJO.
If you are using GSON, here is a quick piece of code:
class Foo {
fun deepCopy() : Foo {
return Gson().fromJson(Gson().toJson(this), this.javaClass)
}
}
As #Ekeko said, the default copy() function implemented for data class is a shallow copy which looks like this:
fun copy(a: Int = this.a, bar: Bar = this.bar, list: MutableList<Int> = this.list)
To perform a deep copy, you have to override the copy() function.
fun copy(a: Int = this.a, bar: Bar = this.bar.copy(), list: MutableList<Int> = this.list.toList()) = Foo(a, bar, list)
There is a way to make a deep copy of an object in Kotlin (and Java): serialize it to memory and then deserialize it back to a new object. This will only work if all the data contained in the object are either primitives or implement the Serializable interface
Here is an explanation with sample Kotlin code https://rosettacode.org/wiki/Deepcopy#Kotlin
import java.io.Serializable
import java.io.ByteArrayOutputStream
import java.io.ByteArrayInputStream
import java.io.ObjectOutputStream
import java.io.ObjectInputStream
fun <T : Serializable> deepCopy(obj: T?): T? {
if (obj == null) return null
val baos = ByteArrayOutputStream()
val oos = ObjectOutputStream(baos)
oos.writeObject(obj)
oos.close()
val bais = ByteArrayInputStream(baos.toByteArray())
val ois = ObjectInputStream(bais)
#Suppress("unchecked_cast")
return ois.readObject() as T
}
Note: This solution should also be applicable in Android using the Parcelable interface instead of the Serializable. Parcelable is more efficient.
Building on a previous answer, an easy if somewhat inelegant solution is to use the kotlinx.serialization facility. Add the plugin to build.gradle as per the docs, then to make a deep copy of an object, annotate it with #Serializable and add a copy method which converts the object to a serialised binary form, then back again. The new object will not reference any objects in the original.
import kotlinx.serialization.Serializable
import kotlinx.serialization.cbor.Cbor
#Serializable
data class DataClass(val yourData: Whatever, val yourList: List<Stuff>) {
var moreStuff: Map<String, String> = mapOf()
fun copy(): DataClass {
return Cbor.load(serializer(), Cbor.dump(serializer(), this))
}
This won't be as fast as a handwritten copy function, but it does not require updating if the object is changed, so is more robust.
I face the same problem. Because in kotlin, ArrayList.map {it.copy} not copying all items of an object specially if a member is list of another object inside this.
The only solution, for deep copying of all items of an object I found on the web, is to serialize and deserialize the object when you send or assign it to a new variable. Code like as follows.
#Parcelize
data class Flights(
// data with different types including the list
) : Parcelable
Before I receiving List of Flights, We can use JSON to deserialize the Object and serialize the object same time!!!.
First, we create two extension functions.
// deserialize method
fun flightListToString(list: ArrayList<Flights>): String {
val type = object : TypeToken<ArrayList<Flights>>() {}.type
return Gson().toJson(list, type)
}
// serialize method
fun toFlightList(string: String): List<Flights>? {
val itemType = object : TypeToken<ArrayList<Flights>>() {}.type
return Gson().fromJson<ArrayList<Flights>>(string, itemType)
}
We can use it like below.
// here I assign list from Navigation args
private lateinit var originalFlightList: List<Flights>
...
val temporaryList = ArrayList(makeProposalFragmentArgs.selectedFlightList.asList())
originalFlightList = toFlightList(flightListToString(temporaryList))!!
Later, I send this list to Recycler Adapter & there the content of the Flights object would be modified.
bindingView.imageViewReset.setOnClickListener {
val temporaryList = ArrayList(makeProposalFragmentArgs.selectedFlightList.asList())
val flightList = toFlightList(flightListToString(temporaryList))!!
**adapter**.resetListToOriginal(flightList)
}
Maybe you can use kotlin reflection in some way here, this example is not recursive but should give the idea:
fun DataType.deepCopy() : DataType {
val copy = DataType()
for (m in this::class.members) {
if (m is KProperty && m is KMutableProperty) {
m.setter.call(copy, if (m.returnType::class.isData) {
(m.getter.call(this) to m.returnType).copy()
} else m.setter.call(copy, m.getter.call(this)))
}
}
return copy
}
If you use Jackson and not concerned about performance,then this simple extension function will give you this feature.
private val objectMapper = ObjectMapper()
.registerModule(KotlinModule())
.registerModule(JavaTimeModule())
fun <T> Any.copyDeep(): T {
return objectMapper.readValue(objectMapper.writeValueAsString(this), this.javaClass) as T
}
Use this function:
private val gson = Gson()
fun <T> deepCopy(item: T?, clazz: Class<T>): T {
val str = gson.toJson(item)
return gson.fromJson(str, clazz)
}
What you want is a deep copy. There are many tools available to do this.
MapStruct: https://mapstruct.org/
Mapstruct generates code at compile time. Normally, it is to auto-generate mappers between java objects, but it also has a 'clone' functionality to create a deep copy of an object. Since this is generated code of what you'd manually write, it is the fastest way to achieve this.
There are many more (kryo, dozer, etc...), you can actually just google, for example here: https://programmer.group/performance-comparison-between-shallow-and-deep-copies.html
DO AVOID serialization-based 'clone': apache commons' SerializationUtils, jackson, gson, etc... They have a huge overhead, since it first creates a middle state. They are about 10-100 times slower than actual copying.