Search code examples
kotlinoverridingclone

How to clone object in Kotlin (not data class)


I need to define some binary operation (like matrix addition) for some base class, but need it to return derived class if applied to derived class. For this I need to clone derived class without knowing it's class at compile time, for example

class Matrix private constructor(private val data: DataClass) {

   public constructor(...) : this(...) {
     ...
   }

   operator fun times(ano: Matrix) : Matrix {
       return Matrix(data.specialOp(ano.data))
   }
}

class EnhancedMatrix {
...
}

fun main() {
    val a = EnhancedMaptrix(...)
    val b = EnhancedMaptrix(...)
    val c = a * b;

    println(c is EnhancedMartrix) // should be true w/o explicit overriding of `times`

}

Is it possible?


Solution

  • Probably the easiest way without using a reflection is to provide a factory by the subclass:

    open class Matrix(
        private val data: DataClass,
        private val factory: (DataClass) -> Matrix
    ) {
        operator fun times(ano: Matrix) : Matrix {
            return factory(data.specialOp(ano.data))
        }
    }
    
    class EnhancedMatrix(data: DataClass) : Matrix(data, ::EnhancedMatrix)
    

    Also, if we would like c to not only be initialized at runtime to EnhancedMatrix object, but also to be resolved to EnhancedMatrix type at the compile time, then this is more complicated, but still possible. We have to use self-referencing generic type like this:

    open class Matrix<T : Matrix<T>>(
        val data: DataClass,
        private val factory: (DataClass) -> T
    ) {
        operator fun times(ano: Matrix<*>) : T {
            return factory(data.specialOp(ano.data))
        }
    }
    
    class EnhancedMatrix(data: DataClass) : Matrix<EnhancedMatrix>(data, ::EnhancedMatrix)
    

    Matrix is parameterized by its specific type, so if we use times() on EnhancedMatrix (which is Matrix<EnhancedMatrix>) then it itself returns EnhancedMatrix as well. Just be aware this solution is pretty complicated, it isn't easy to understand, so use it only if really needed.