[Kotlin] Special Classes : Data, Enum, Sealed, Object Keyword

Kotlin의 특별한 클래스인 Data 클래스, Enum 클래스, Sealed클래스, Object 키워드에 대해서 알아보자.
Apr 19, 2024
[Kotlin] Special Classes : Data, Enum, Sealed, Object Keyword

Special Classes

Data Classes

데이터 클래스를 사용하면 값을 저장하는 데 사용되는 클래스를 쉽게 만들 수 있다.

이러한 클래스에는 복사, 문자열 표현 가져오기, 컬렉션에서 인스턴스 사용 메서드가 자동으로 제공된다. 클래스 선언에서 자체 구현으로 이러한 메서드를 재정의할 수 있다.

  1. equals(): 객체의 동등성을 검사하는 메서드다. 데이터 클래스에서는 모든 프로퍼티가 동일한지 비교하여 동등성을 판단한다.

  2. hashCode(): 객체의 해시 코드를 반환하는 메서드다. 데이터 클래스에서는 모든 프로퍼티의 해시 코드를 사용하여 객체의 해시 코드를 계산한다.

  3. toString(): 객체를 문자열로 표현하는 메서드다. 데이터 클래스에서는 클래스 이름과 함께 모든 프로퍼티의 이름과 값을 포함하는 문자열을 반환한다.

  4. copy(): 객체의 복사본을 생성하는 메서드다. 데이터 클래스에서는 모든 프로퍼티를 복사하여 새로운 객체를 생성한다. 필요한 경우, 특정 프로퍼티의 값을 변경하여 복사본을 생성할 수도 있다.

  5. componentN(): N번째 프로퍼티의 값을 반환하는 메서드다. 데이터 클래스에서는 프로퍼티 선언 순서에 따라 component1(), component2(), ...와 같은 메서드를 제공한다. 이 메서드들은 구조 분해 선언에서 사용된다. 자동 생성된 컴포넌트N 함수를 사용하면 선언된 순서대로 프로퍼티 값을 가져올 수 있다.

Enum Classes

열거형 클래스는 방향, 상태, 모드 등과 같이 유한한 값의 집합을 나타내는 유형을 모델링하는 데 사용된다.

Sealed Classes

Sealed Class를 사용하면 어떤 클래스나 인터페이스를 상속받을 수 있는지를 엄격하게 제한하는 기능을 제공한다. 일단 Sealed Class를 선언하면 Sealed Class가 선언된 동일한 패키지 내부에서만 서브클래싱할 수 있다. Sealed Class가 선언된 패키지 외부에서는 서브클래싱할 수 없다.

💬 예를 들면, 한 종류의 동물을 나타내는 클래스를 만든다고 가정하자. 고양이, 강아지, 호랑이와 같은 여러 하위 클래스가 있다. 이런 클래스들이 다른 사람들이 만든 코드에서 임의로 상속받아 새로운 종류의 동물을 만들지 못하도록 할 때 Sealed 클래스를 사용하면 이를 실현할 수 있다.

Sealed 클래스를 사용하면 코드에서 패턴 매칭(when 표현식 등)을 사용하여 가능한 모든 하위 클래스에 대한 동작을 쉽게 다룰 수 있다. 그러면 코드를 더 안전하고 예측 가능하게 만들어준다.

결국, sealed 클래스는 프로그램의 일부분이 다른 부분들에게 어떤 방식으로 상속될 수 있는지를 엄격하게 제어하여 프로그램을 안전하게 유지하고 예측 가능하게 만들어준다.

📌 그러면, 어떤 상황에서 Sealed 클래스를 사용해야하는지 알아보자.

  1. 제한된 클래스 상속이 필요한 경우
    클래스가 특정한 하위 클래스들만 상속 받아야 할 때 사용됨. 동물 클래스에서 고양이, 강아지, 호랑이와 같은 한정된 종류의 동물만 상속받아야 하는 경우 사용한다. 컴파일 시간에 이미 알려진 한정된 하위 클래스 집합을 가지고 있을 때 유용하다.

    "컴파일 시간에 이미 알려진 한정된 하위 클래스 집합"이란, 코드를 컴파일할 때 이미 어떤 클래스가 특정 클래스의 하위 클래스로 정의되어 있음을 알 수 있는 상황을 가르킨다.
    즉, 프로그래머가 코드를 작성할 때 컴파일러에게 특정 클래스의 하위 클래스들을 미리 알려줄 수 있고, 컴파일러는 이 정보를 이용하여 코드를 컴파일한다.


    따라서 "컴파일 시간에 이미 알려진 한정된 하위 클래스 집합을 가지고 있을 때"는 sealed 클래스를 사용하는 이유 중 하나이다. 이것은 코드를 더 예측 가능하고 안전하게 만들어주며, 개발자가 의도한 대로 코드가 동작할 수 있도록 한다.
     

  2. 유형별 안전 설계가 필요한 경우
    프로그램의 다양한 상태를 관리하거나 복잡한 조건 로직을 처리할 때 유용하다. Ssealed 클래스는 when 표현식과 함께 사용하여 다양한 하위 클래스에 대한 동작을 쉽게 다룰 수 있으며, 이는 코드를 더 예측 가능하고 안전하게 만든다.

  3. Closed API와 작업할 때
    Closed API는 외부에서의 접근을 엄격하게 제한하여 외부 사용자가 API를 변경하거나 확장하지 못하도록 하는 API를 가리킨다. 일종의 캡슐화(encapsulation)를 통해 API의 안정성과 예측 가능성을 유지하고자 하는 개념이다.

    일반적으로, 라이브러리/프레임워크를 개발할 때 닫힌 API를 사용한다. 외부 사용자는 라이브러리의 기능을 사용할 수 있지만 내부 구현에 접근하거나 수정할 수 는 없다. 이러면 라이브러리의 안정성을 유지하고 다른 개발자들이 예상치 못한 방식으로 라이브러리를 변경하는 것을 방지한다.
     

    sealed 클래스는 이러한 닫힌 API를 구현하는데 도움 된다. 즉, sealed 클래스를 통해 특정 클래스의 하위 클래스를 변경하거나 확장할 수 없으므로 API의 안정성과 예측 가능성을 유지할 수 있다.

Constructors

Sealed 클래스의 경우, 직접적으로 인스턴스화할 수 없지만, 서브클래스의 인스턴스를 생성하는 데 사용되는 생성자를 포함할 수 있다. 즉, Sealed 클래스의 생성자는 클래스 자체를 인스턴스화하는 것이 아니라, 하위 클래스의 인스턴스를 생성하기 위한 것이다.

Enum class와 같이 사용하는 예제는 다음과 같다.

이렇게 sealed class를 사용하면, 에러의 종류를 한정적으로 관리할 수 있으며, 각 에러에 따른 처리를 컴파일 타임에서 체크할 수 있다. 이는 런타임 에러를 줄이는 데 도움이 된다.

sealed classprotected(기본값)와 private의 접근제어자를 가진다. protected 생성자는 해당 클래스 내부와 하위 클래스에서만 보인다. private 생성자는 해당 클래스 내에서만 보이며, 이를 통해 인스턴스 생성을 더욱 엄격하게 제어한다.

public, internal 접근제어자를 가진 생성자는 sealed class에서 허용되지 않는다.

Use case scenarios : UI 애플리케이션의 상태 관리

sealed class를 사용하여 UI의 상태를 표현하고, 이에 따른 UI 업데이트를 처리하는 예제 코드이다. UIState라는 sealed class를 정의한다. Loading, Success, Error 세 가지 서브 클래스를 가진다.

각각의 서브 클래스는 UI의 상태를 나타내며, Success와 Error는 각각 val dataval exception를 가진다.

다음으로, updateUI라는 함수를 정의한다. 이 함수는 UIState 타입의 매개변수를 받아, UI의 상태에 따라 처리한다. when 표현식을 사용하여 UI의 상태를 체크하고, 각 상태에 맞는 함수(showLoadingIndicator, showData, showError)를 호출한다.

Use case scenarios : API request-response handling

API 요청과 응답을 처리하는 예제 코드이다. 먼저, ApiRequest라는 sealed interface를 정의한다. ApiRequest 인터페이스는 LoginRequestLogoutRequest 두 가지 클래스를 가지고 있다. 각각의 클래스는 ApiRequest 인터페이스를 구현하며, 각각의 API 요청에 필요한 데이터를 가지고 있다.

다음으로, ApiResponse라는 sealed class를 정의한다. ApiResponse 클래스는 UserSuccess, UserNotFound, Error 세 가지 서브 클래스를 가지고 있다. 각각의 서브 클래스는 API 응답의 상태를 나타내며, UserSuccessError는 각각 응답 데이터와 에러 메시지를 가지고 있다.

handleRequest 함수는 ApiRequest 타입의 매개변수를 받아, API 요청에 따른 처리를 수행하고, ApiResponse 타입의 결과를 반환한다. when 표현식을 사용하여 API 요청의 종류를 체크하고, 각 요청에 맞는 처리를 수행한다.

getUserById 함수는 사용자 ID를 받아, 해당 사용자의 정보를 반환하거나, 사용자를 찾을 수 없는 경우에는 UserNotFound 응답을 반환한다.

마지막으로, main 함수에서는 handleRequest 함수와 getUserById 함수를 호출하여, 각각의 API 요청과 응답을 시뮬레이션하고, 결과를 출력한다.

Object Keyword

Share article

code-with-me