Как клонировать объект в Котлине?

Вопрос в том, что просто.

Документация Kotlin описывает клонирование только при доступе к Java и классу enum. В последнем случае клон просто бросает исключение.

Итак, как я/должен был бы клонировать произвольный объект Kotlin?

Должен ли я использовать clone() как в Java?

Ответ 1

Для data class вы можете использовать метод copy() созданный компилятором. Обратите внимание, что он выполнит мелкую копию.

Чтобы создать копию коллекции, используйте toList() или toSet(), в зависимости от типа коллекции, в которой вы нуждаетесь. Эти методы всегда создают новую копию коллекции; они также выполняют мелкую копию.

Для других классов не существует специального решения клонирования, специфичного для Kotlin. Вы можете использовать .clone() если это соответствует вашим требованиям, или создать другое решение, если оно не подходит.

Ответ 2

Вы можете использовать библиотеку Gson, чтобы преобразовать исходный объект в строку, а затем преобразовать эту строку обратно в фактический тип объекта, и у вас будет клон. Смотрите мой пример. Поместите эту функцию в класс/модель, для которой вы хотите создать клон. В моем примере я клонирую объект типа Project, поэтому я помещу его в класс Project class

class Project{
 fun clone(): Project {
                val stringProject = Gson().toJson(this, Project::class.java)
                return Gson().fromJson<Project>(stringProject, Project::class.java)
            }
}

Тогда используйте это так:

val originalProject = Project()
val projectClone = originalProject.clone()

Ответ 4

Я проголосовал за @yole за хороший ответ, но другими способами, если вы не используете (или не можете) использовать класс данных. Вы можете написать вспомогательный метод следующим образом:

object ModelHelper {

    inline fun <reified T : Serializable> mergeFields(from: T, to: T) {
        from::class.java.declaredFields.forEach { field ->
            val isLocked = field.isAccessible
            field.isAccessible = true

            if (field.get(from) != null) {
                field.set(to, field.get(from))
            }

            field.isAccessible = isLocked
        }
    }

}

Таким образом, вы можете "скопировать" экземпляр A в B следующим образом:

val bInstance = AClassType()
ModelHelper.mergeFields(aInstance, bInstance)

Иногда я использую этот способ для объединения данных из множества экземпляров в один объект, значение которого доступно (не ноль).

Ответ 5

Если класс, который вы пытаетесь клонировать, не реализует Cloneable или не является классом данных и является частью внешней библиотеки, вы можете создать метод расширения, который возвращает новый экземпляр. Например:

class Person {
  var id: String? = null
  var name: String? = null
} 
fun Person.clone(): Person {
  val person = Person()
  person.id = id
  person.name = name
  return person 
}