Kotlin 데이터 클래스 Data Class & JAVA POJO

박미소·2023년 12월 15일
0

코틀린

목록 보기
11/44

DTO(Data Transfer Object)

데이터 전달을 위한 순수한 데이터 객체이다.
구현 로직을 가지고 있지 않고 순수한 데이터 객체를 표현만 해서 사용하면 된다. 데이터를 접근하는 게터/세터를 포함한다. toString(), Equals()등과 같은 데이터 표현 및 비교 메서드를 포함한다.



데이터 클래스(data class)

일반 클래스와 다르게 다양한 메소드를 자동으로 생성해주는 클래스이다. 편리함을 제공하는 유용한 메소드들이 자동생성된다. DTO를 표현하기 적합한 클래스 표현으로 data class 키워드를 사용해 정의한다. 데이터 클래스는 다음과 같은 조건을 만족해야 한다.



데이터 클래스 특징

  • 주 생성자는 최소한 하나의 매개변수를 가져야 한다.
  • 주 생성자의 모든 매개변수는 val, var로 지정된 프로퍼티여야 한다.
  • 다른 클래스를 상속받을 수 없다(상위 클래스를 가질 수 없음). 단, sealed 클래스는 상속받을 수 있으며, 인터페이스는 구현할 수 있다.
  • 데이터 클래스는 abstract, open, sealed, inner 키워드를 붙일 수 없다.
  • 자동으로 생성한 메소드를 오버라이딩할 경우 오버라이드 된 메소드를 사용한다.

데이터 클래스로부터는 open을 사용한 상속이나 abstract의 추상 클래스, 내부 클래스를 위한 inner등을 사용할 수 없고 오로지 데이터를 기술하기 위한 용도로만 사용됩니다. 하지만 필요하다면 추가로 부 생성자나 init 블록을 넣어 데이터를 위한 간단한 로직을 포함할 수 있다.

  • 정의 예제 1
data class Customer(var name: String, var email: String)

  • 정의 예제 2
data class People(
    val name: String,
    val age: Int
)



객체 destructuring 하기

디스트럭처링(destructuring)한다는 것은 객체가 가지고 있는 프로퍼티를 개별 변수들로 분해하여 할당하는 것을 말한다. 변수를 선언할 때 소괄호를 사용해서 분해하고자 하는 객체를 지정한다.

  • 디스트럭처링 예제1
 val (name, email) = cus1
println("name = $name, email = $email")

// 특정 프로퍼티를 가져올 필요 없는 경우
val (_, email) = cus1 // 첫번째 프로퍼티 제외

- 디스트럭처링 예제 2
fun main() {
   val peopleA = People("H43RO", 23)
   val peopleB = People("LULU", 21)
}

코틀린은 자바와는 다르게 간단하게 데이터 객체를 이용할 수 있다.





데이터 클래스 생성 시 자동 생성되는 메소드들

  • hashCode( )
  • copy( )
  • equals( )
  • toString( )
  • conponentsN( )

a. toString() 메소드

생성한 객체를 그대로 출력해보면 갖고 있는 프로퍼티의 값들이 알아서 출력되는 것을 확인할 수 있다. toString() 이 이미 구현되어있기 때문이다.

println(peopleA)

// People(name=H43RO, age=23)

b. copy() 메소드

copy() 메소드 역시 사용할 수 있다. 특정 필드값만 바꿔서 복사하기에 간편하다.

val olderPeopleA = peopleA.copy(age = 33)
println(olderPeopleA)

// People(name=H43RO, age=33) -> 나이가 23 에서 33 으로 변경되었음!

c. hashcode() 메소드

프로퍼티 값이 완전히 같은 두 Data Class 객체를 만들고, hashCode() 를 출력.

val peopleA = People("H43RO", 23)
val peopleB = People("H43RO", 23)

println(peopleA.hashCode())
println(peopleB.hashCode())
2110922579
2110922579

d. equals() 메소드

이어서 두 객체를 비교하는 연산도 수행.

println(peopleA == peopleB)

// true (두 객체의 프로퍼티가 완전히 같음

'==' 연산자 하나로, 두 객체가 동일한 값을 담고있는지 쉽게 검사할 수 있다.

또한, === 연산도 가능하다. 메모리 상 다른 객체이므로 false 를 출력한다.

println(peopleA == peopleB)

// false (두 객체의 메모리 주소가 다름)

< Java vs Kotlin 동등성 비교 >

  • 갖고 있는 값이 동일한지 검사
    Java : equals()
    Kotlin : ==

  • 메모리상 같은 객체인지 검사
    Java : ==
    Kotlin : ===


e. componentN() 메소드

Data Class 는 기본적으로 componentN() 메소드가 생성이 되기 때문에, 각 프로퍼티에 번호가 붙어 구조 분해가 가능한 형태가 된다. 직접 구조 분해 (Destructuring Declarations) 해본 예제

val peopleA = People("H43RO", 23)
val (name, age) = peopleA

println("Destructuring Declarations : $name, $age")

// Destructuring Declarations : H43RO, 23

이런 식으로 프로퍼티의 값들을 가독성있게 분해해냈다. 매우 편리한 기능이다.

내부적으론 다음과 같이 동작한다. 우선, 다시 People 클래스의 구조를 살펴본다면,

data class People(
    val name: String,
    val age: Int
)

이 클래스는 프로퍼티가 2개이다. 이 때 선언 순서가 name 다음에 age 형태로 되어있기 때문에, component1() 메소드에 name 필드가 대응되고, component2() 메소드에 age 필드가 대응되게 된다. 이 모든 과정을 컴파일러가 알아서 자동으로 해준다. 실제로 . 을 찍어서 멤버를 확인해보면, 메소드가 생성되어 있다.

따라서, 컴파일러에 의해 아래와 같이 구조 분해가 일어나게 된다.

val name = peopleA.component1()
val age = peopleA.component2()

개발자가 val (name, age) = peopleA 라고만 적어줘도, 위 동작을 내부적으로 수행해준다.





프로퍼티를 데이터 클래스 body에 선언하는 예제

data class Person(val name: String) {
    var age: Int = 0
}

이 예제에서 name 프로퍼티는 .toString(), .equals(), .hashCode(), and .copy() 4개의 메소드만 사용 가능하다.
그리고 class body에 있는 age는 이 4개를 사용하지 못하고 componentN()메소드만 사용가능하다. 만약 두 개의 Person 객체가 같은 name프로퍼티를 가진다면 둘은 같다고 여긴다. 왜냐하면 .equals() 메소드가 name 프로퍼티만 체크하기 때문이다.

val age = Person.component1() // 프로퍼티 번호가 붙어 분해가능한 메소드. 상수에 할당됨.
val person1 = Person("John")
val person2 = Person("John")
person1.age = 10
person2.age = 20

println("person1 == person2: ${person1 == person2}")
// person1 == person2: true

println("person1 with age ${person1.age}: ${person1}")
// person1 with age 10: Person(name=John)

println("person2 with age ${person2.age}: ${person2}")
// person2 with age 20: Person(name=John)
person1 == person2: true
person1 with age 10: Person(name=John)
person2 with age 20: Person(name=John)



스탠다드(표준) 데이터 클래스

일반 라이브러리에서는 Pair와 Triple 클래스를 제공해준다. 많이 사용되지만 데이터 클래스는 프로퍼티들에 의미가 담긴 이름을 넣을 수 있어 더 나은 디자인이 가능해 코드를 더 쉽게 읽히도록 해준다.




POJO(Plain Old Java Object)

자바에서는 POJO는 Plain Old Java Object의 약자로 직역하면 순수한 오래된 자바 객체이다. 자바로 생성하는 순수한 객체를 뜻한다. POJO는 IoC/DI, AOP, PSA를 통해서 달성할 수 있다는 것을 의미한다. POJO는 객체 지향적인 원리에 충실하면서 환경과 기술에 종속되지 않고 필요에 따라 재활용될 수 있는 방식으로 설계된 object를 의미한다.


POJO 프로그래밍

  • POJO 예제1

자바나 자바의 스펙에 정의된 것 이외에는 다른 기술이나 규약에 얽매이지 않아야 한다. 다음 코드는 getter와 setter만 가지고 있는 코드의 예제이다. 자바에서 제공하는 기능만 사용하기 때문에 이 클래스는 자바 언어 이외의 특정한 기술에 종속되어 있지 않은 순수한 객체여서 POJO라고 부를 수 있다.

public class User {
    private String userName;
    private String id;
    private String password;

    public String getUserName() {
        return userName;
    }

    public void setUserName(String userName) {
        this.userName = userName;
    }

    public String getId() {
        return id;
    }

    public void setId(String id) {
        this.id = id;
    }

    public String getPassword() {
        return password;
    }

    public void setPassword(String password) {
        this.password = password;
    }
}

  • POJO 예제2
class People {
	String name;
    Int age;
    
    @Override
    public String toString(){
    	return "[People] name : " + name + ", age : " + Integer.toString(age);
    }
    
    public String getName() {
    	return name;
    }
    
    public void setName(String name) {
    	this.name = name;
    }
}

Java를 활용하여 이름과 나이를 갖는 People 이라는 클래스를 사용한다고 가정한 코드. toString()을 오버라이딩하고, Getter / Setter 등을 정의해줘야 한다. 모두 필요한 코드는 맞지만 간단한 클래스임에도 보일러 플레이트 코드가 너무 비대하다.

  • 보일러플레이트 코드 : 최소한의 변경으로 여러곳에서 재사용되며, 반복적으로 비슷한 형태를 띄는 코드.

이걸 데이터 클래스로 변환한다면,

data class People(
    val name: String,
    val age: Int
)

자바 예제 클래스와 같은 동작을 한다.

데이터 클래스 객체 생성해서 사용가능

fun main() {
    val peopleA = People("H43RO", 23)
    val peopleB = People("LULU", 21)
}

참고:
https://blog.naver.com/aki1013/222793378871

https://velog.io/@haero_kim/Kotlin-%EA%B0%90%EB%8F%99-%EC%8B%A4%ED%99%94-Data-Class-%EC%95%8C%EC%95%84%EB%B3%B4%EA%B8%B0

https://ittrue.tistory.com/211

https://kotlinlang.org/docs/data-classes.html#properties-declared-in-the-class-body

0개의 댓글