본문 바로가기
  • 개발하는 곰돌이
Programming Language/Kotlin & Java

Kotlin 기본 문법 9 : Kotlin의 다양한 클래스(Data Class, Enum Class, Sealed Class)

by 개발하는 곰돌이 2022. 12. 16.

목차

    개요

    Kotlin에는 기본적인 클래스, 추상 클래스, 인터페이스 외에도 Data Class, Enum Class, Sealed Class가 존재한다. 물론 Enum Class는 Java에도 존재하는 개념이지만 Data Class와 Sealed Class는 Java에는 존재하지 않는 Kotlin만의 특별한 클래스이다. 이번 포스트에서는 이러한 Kotlin의 특별한 클래스에 대해 정리한다.


    Data Class

    Data Class는 주로 DB를 다루는 앱을 개발할 때 각 DTO(Data Transfer Object)나 VO(Value Object)를 작성할 때 사용할 수 있는 특수한 클래스이다. Java에서 DTO나 VO를 작성할 때 getter, setter를 비롯하여 equals(), hashCode(), toString() 메소드를 작성해야 하는 경우가 있는데 Kotlin에서는 이들을 모두 포함하는 Data Class를 손쉽게 작성할 수 있다.

     

    Data Class를 선언할 때는 data class 키워드를 사용하여 선언한다. 그 외에는 일반적인 클래스를 작성하는 것과 동일하게 작성하면 된다. Data Class의 특징은 클래스를 선언하기만 해도 getter, setter, equals(), hashCode(), toString()이 모두 정의된다는 점이다. 물론, Java를 사용할 때도 IDE의 도움을 받거나 Lombok을 사용하여 위 내용들을 손쉽게 작성할 수 있지만 Kotlin에서는 IDE의 도움 없이 단순히 Data Class만 선언하더라도 위 내용들이 정의된다는 차이가 있다. 이 점을 제외하면 Data Class도 일반 클래스와 동일하게 메소드를 작성할 수 있다.

    // Java
    public class MemberDTO {
        private String id;
        private String password;
    
        public MemberDTO(String id, String password) {
            this.id = id;
            this.password = password;
        }
    
        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;
        }
    
        @Override
        public boolean equals(Object o) {
            if (this == o) return true;
            if (o == null || getClass() != o.getClass()) return false;
    
            MemberDTO memberDTO = (MemberDTO) o;
    
            if (!id.equals(memberDTO.id)) return false;
            return password.equals(memberDTO.password);
        }
    
        @Override
        public int hashCode() {
            int result = id.hashCode();
            result = 31 * result + password.hashCode();
            return result;
        }
    
        @Override
        public String toString() {
            return "MemberDTO{" +
                    "id='" + id + '\'' +
                    ", password='" + password + '\'' +
                    '}';
        }
    }

    Java에서는 이렇게 길게 작성해야 하는 내용이

    // Kotlin
    data class MemberDTO(var id: String, var password: String)

    Kotlin에서는 이 한 줄로 모든게 정의된다. 실제로 동작시켜 보면 잘 작동한다는 것을 알 수 있다.

    data class 한 줄로 선언된 MemberDTO 클래스는 equals(), hashCode(), toString()까지 모두 정의된다.


    Data Class는 일반적으로는 상속이 불가능하다는 특징이 있다. data class 앞에 open을 붙이게 되면 두 키워드가 호환되지 않는다는 에러가 나타난다.

    Data Class는 일반적으로 open될 수 없다.


    Enum Class

    Enum Class는 Java와 다르지 않다. Java와 Kotlin의 기본적인 클래스 문법 차이를 제외하면 단지 Java에서는 선언 키워드가 enum인데 Kotlin에서는 enum class로 선언한다는 차이가 있을 뿐이다. 클래스 상속이 불가능하지만 인터페이스 구현은 가능하며, 싱글톤으로 작동한다는 점까지 동일하다.

    // Java
    public enum DetailedMajor {
        DB("데이터베이스"),
        AI("인공지능"),
        NETWORK("네트워크"),
        MOBILE("모바일"),
        OS("운영체제");
    
        private final String major;
    
        DetailedMajor(String major) {
            this.major = major;
        }
    
        public String getMajor() {
            return major;
        }
    }
    // Kotlin
    enum class DetailedMajor(val major: String) {
        DB("데이터베이스"),
        AI("인공지능"),
        NETWORK("네트워크"),
        MOBILE("모바일"),
        OS("운영체제")
    }

    Enum Class의 활용 방법은 Java와 Kotlin이 약간 차이가 있다. Kotlin에서 when 문에서 조건을 검사할 값에 Enum 타입을 사용하면 IntelliJ에서 자동으로 when에 누락된 값이 있다는 것을 알려주고 when의 조건에 모든 값을 추가할 수 있게 해준다. 또한 컴파일러가 해당 Enum 타입의 모든 값을 알고 있기 때문에 when 문을 expression으로 사용할 때 Enum 타입의 모든 값을 작성했다면 else를 작성하지 않아도 된다.

    when 문에 Enum 타입을 사용하면 누락된 값을 한 번에 추가할 수 있다.


    Sealed Class

    Sealed Class는 기본적으로는 추상 클래스인데 몇 가지 특이한 점이 있다. Sealed Class의 자식 클래스는 반드시 같은 패키지 내에서만 선언할 수 있으며, 추상 클래스이기 때문에 그 자체로는 객체를 생성할 수 없다. Sealed Class는 컴파일 타임에 자식 클래스의 타입을 모두 기억하기 때문에 Enum Class처럼 사용할 수 있다.

    package com.kotlin
    
    sealed class Animal(var name: String) {
        protected abstract fun eat(food: String)
    }
    package com.kotlin
    
    class Dog(name: String) : Animal(name) {
        override fun eat(food: String) = println("강아지가 ${food}를 맛있게 먹습니다.")
    }
    
    class Cat(name: String) : Animal(name) {
        override fun eat(food: String) = println("야옹이가 ${food}를 냠냠 먹어요.")
    }

    Sealed Class를 when 문과 함께 사용하면 Enum Class와 마찬가지로 when 문의 조건에 누락된 자식 클래스가 존재한다는 것을 알려주고 한 번에 추가할 수 있게 해준다. when문을 expression으로 사용할 때 모든 자식 클래스 타입을 작성했다면 else가 필요없다는 점도 동일하다.

    sealed class도 enum class와 마찬가지로 when 문에 사용하면 자식 클래스를 한 번에 추가할 수 있다.


    마무리

    이번 포스트에서는 Kotlin의 특수한 클래스인 Data Class, Enum Class, Sealed Class에 대해 정리하였다. 이러한 특수 클래스를 잘 사용하면 코드의 가독성을 늘리면서 코드의 길이는 줄여 코드가 깔끔해지는 효과를 얻을 수 있다. 이러한 편의성에 힘입어 Java에서도 JDK16부터 Data Class에 대응되는 Record Class가, JDK17부터 Sealed Class가 추가되었다고 한다.

     

    다음 포스트에서는 Kotlin의 커스텀 getter와 setter에 대해 정리할 예정이다.

     

    댓글 피드백은 언제나 환영합니다.

     

    댓글