The copy() of the data class is a shallow copy
A shallow copy is a bitwise copy of an object that creates a new object with an exact copy of the original object’s property values. If the property is of a primitive type, the value of the primitive type is copied. If the property is a memory address (reference type), the memory address is copied, so if one object changes the address, the other object will be affected.
Deep copy copies all attributes and dynamically allocated memory to which the attributes point. Deep copy occurs when an object is copied along with the object it references. Deep copy is slower and more expensive than shallow copy.
Copy () of a data class is a copy function that copies all or part of an object’s properties.
For example:
data class Address(var street:String)
data class User(var name:String,var password:String,var address: Address)
fun main(args: Array<String>) {
val user1 = User("tony"."123456", Address("renming"))
val user2 = user1.copy()
println(user2)
println(user1.address===user2.address) // Check whether the copy of the data class is a shallow copy. If the address of the data class is the same as the memory address of the data class, it is a shallow copy; otherwise, it is a deep copy
val user3 = user1.copy("monica")
println(user3)
val user4 = user1.copy(password = "abcdef")
println(user4)
}
Copy the code
Execution Result:
User(name=tony, password=123456, address=Address(street=renming))
true
User(name=monica, password=123456, address=Address(street=renming))
User(name=tony, password=abcdef, address=Address(street=renming))
Copy the code
User1. address=== If the value of user2.address is true, the two memory addresses are the same. If an object contains variables of reference type, the object is copied to the same address, indicating a shallow copy. Therefore, the copy of the data class is a shallow copy.
Of course, there are many ways to implement deep copy, such as using serialization deserialization, some open source libraries (e.g. github.com/enbandari/K…
What follows in this article is not deep copy, but has some relevance to deep copy, and is the mapping between Java beans and Java Beans. Similar tools include Apache’s BeanUtils, Dozer, MapStruct, and so on.
Introduction to MapStruct
MapStruct is a Java annotation processor based on JSR 269. The developer only needs to define a Mapper interface that declares any required mapping methods. MapStruct generates an implementation class for this interface during compilation.
MapStruct allows you to automatically map between two Java beans by creating an interface. Because it creates the concrete implementation automatically at compile time, there is no overhead such as reflection, and performance is better than Apache’s BeanUtils, Dozer, and so on.
Kotlin uses MapStruct
There is an open source project for MapStruct Kotlin on Github: github.com/Pozo/mapstr…
3.1 Mapstruct-Kotlin installation:
Add the kapt plugin
apply plugin: 'kotlin-kapt'
Copy the code
Then add the following dependencies to your project:
api("Com. Making. Pozo: mapstruct - kotlin: 1.3.1.2." ")
kapt("Com. Making. Pozo: mapstruct kotlin - processor: 1.3.1.2." ")
Copy the code
In addition, the following dependencies need to be added:
api("Org. Mapstruct: mapstruct: 1.4.0 Beta3." ")
kapt("Org. Mapstruct: mapstruct - processor: 1.4.0. Beta3." ")
Copy the code
3.2 Basic use of mapstruct-kotlin
For data classes that need MapStruct, you must add an @kotlinBuilder annotation
@KotlinBuilder
data class User(var name:String,var password:String,var address: Address)
@KotlinBuilder
data class UserDto(var name:String,var password:String,var address: Address)
Copy the code
Adding the @kotlinBuilder annotation generates the UserBuilder, UserDtoBuilder object at compile time, which is used in Mapper’s implementation class to create and assign objects.
Define a Mapper again:
@Mapper
interface UserMapper {
fun toDto(user: User): UserDto
}
Copy the code
That way, it’s ready to use. MapStruct automatically generates the UserMapperImpl class at compile time, completing the transformation of the User object into a UserDto object.
fun main(a) {
val userMapper = UserMapperImpl()
val user = User("tony"."123456", Address("renming"))
val userDto = userMapper.toDto(user)
println("${user.name}.${user.address}")}Copy the code
Execution Result:
tony,Address(street=renming)
Copy the code
3.3 Complex application of Mapstruct-Kotlin
For slightly more complex classes:
// domain elements @KotlinBuilder data class Role(val id: Int, val name: String, val abbreviation: String?) @KotlinBuilder data class Person(val firstName: String, val lastName: String, val age: Int, val role: Role?) // dto elements @KotlinBuilder data class RoleDto(val id: Int, val name: String, val abbreviation: String, val ignoredAttr: Int?) @KotlinBuilder data class PersonDto( val firstName: String, val phone: String? , val birthDate: LocalDate? , val lastName: String, val age: Int, val role: RoleDto? )Copy the code
The Person class also contains the Role class, and cases where the attributes of Person and PersonDto are not exactly the same. In the Mapper interface, @mappings is supported for mapping.
@Mapper(uses = [RoleMapper::class])
interface PersonMapper {
@Mappings(
value = [
Mapping(target = "role", ignore = true),
Mapping(target = "phone", ignore = true),
Mapping(target = "birthDate", ignore = true),
Mapping(target = "role.id", source = "role.id"),
Mapping(target = "role.name", source = "role.name")])fun toDto(person: Person): PersonDto
@Mappings(
value = [
Mapping(target = "age", ignore = true),
Mapping(target = "role.abbreviation", ignore = true)])@InheritInverseConfiguration
fun toPerson(person: PersonDto): Person
}
Copy the code
In PersonMapper’s toDto(), ignore = true can be used in Mapping for attributes that PersonDto does not have.
Let’s look at mapping Person to personDto and personDto back to Person.
fun main(a) {
val role = Role(1."role one"."R1")
val person = Person("Tony"."Shen".20, role)
val personMapper = PersonMapperImpl()
val personDto = personMapper.toDto(person)
val personFromDto = personMapper.toPerson(personDto)
println("personDto.firstName=${personDto.firstName}")
println("personDto.role.id=${personDto.role? .id}")
println("personDto.phone=${personDto.phone}")
println("personFromDto.firstName=${personFromDto.firstName}")
println("personFromDto.age=${personFromDto.age}")}Copy the code
Execution Result:
personDto.firstName=Tony
personDto.role.id=1
personDto.phone=null
personFromDto.firstName=Tony
personFromDto.age=0
Copy the code
PersonDto. Phone =null since Person does not have phone and is ignored in Mapping, PersonDto.
PersonDto has an age attribute, but it is ignored in the Mapping, so personFromdto.age =0 after being converted to Person.
The result met our expectations.
conclusion
When using Kotlin’s Data class, MapStruct is a good choice if you need to do mapping between Java beans.