[Kotlin] Special Classes : Data, Enum, Sealed, Object Keyword
Special Classes
Data Classes
데이터 클래스를 사용하면 값을 저장하는 데 사용되는 클래스를 쉽게 만들 수 있다.
이러한 클래스에는 복사, 문자열 표현 가져오기, 컬렉션에서 인스턴스 사용 메서드가 자동으로 제공된다. 클래스 선언에서 자체 구현으로 이러한 메서드를 재정의할 수 있다.
equals()
: 객체의 동등성을 검사하는 메서드다. 데이터 클래스에서는 모든 프로퍼티가 동일한지 비교하여 동등성을 판단한다.hashCode()
: 객체의 해시 코드를 반환하는 메서드다. 데이터 클래스에서는 모든 프로퍼티의 해시 코드를 사용하여 객체의 해시 코드를 계산한다.toString()
: 객체를 문자열로 표현하는 메서드다. 데이터 클래스에서는 클래스 이름과 함께 모든 프로퍼티의 이름과 값을 포함하는 문자열을 반환한다.copy()
: 객체의 복사본을 생성하는 메서드다. 데이터 클래스에서는 모든 프로퍼티를 복사하여 새로운 객체를 생성한다. 필요한 경우, 특정 프로퍼티의 값을 변경하여 복사본을 생성할 수도 있다.componentN()
: N번째 프로퍼티의 값을 반환하는 메서드다. 데이터 클래스에서는 프로퍼티 선언 순서에 따라component1()
,component2()
, ...와 같은 메서드를 제공한다. 이 메서드들은 구조 분해 선언에서 사용된다. 자동 생성된 컴포넌트N 함수를 사용하면 선언된 순서대로 프로퍼티 값을 가져올 수 있다.
Enum Classes
열거형 클래스는 방향, 상태, 모드 등과 같이 유한한 값의 집합을 나타내는 유형을 모델링하는 데 사용된다.
Sealed Classes
Sealed Class를 사용하면 어떤 클래스나 인터페이스를 상속받을 수 있는지를 엄격하게 제한하는 기능을 제공한다. 일단 Sealed Class를 선언하면 Sealed Class가 선언된 동일한 패키지 내부에서만 서브클래싱할 수 있다. Sealed Class가 선언된 패키지 외부에서는 서브클래싱할 수 없다.
💬 예를 들면, 한 종류의 동물을 나타내는 클래스를 만든다고 가정하자. 고양이, 강아지, 호랑이와 같은 여러 하위 클래스가 있다. 이런 클래스들이 다른 사람들이 만든 코드에서 임의로 상속받아 새로운 종류의 동물을 만들지 못하도록 할 때 Sealed 클래스를 사용하면 이를 실현할 수 있다.
Sealed 클래스를 사용하면 코드에서 패턴 매칭(when 표현식 등)을 사용하여 가능한 모든 하위 클래스에 대한 동작을 쉽게 다룰 수 있다. 그러면 코드를 더 안전하고 예측 가능하게 만들어준다.
결국, sealed 클래스는 프로그램의 일부분이 다른 부분들에게 어떤 방식으로 상속될 수 있는지를 엄격하게 제어하여 프로그램을 안전하게 유지하고 예측 가능하게 만들어준다.
📌 그러면, 어떤 상황에서 Sealed 클래스를 사용해야하는지 알아보자.
제한된 클래스 상속이 필요한 경우
클래스가 특정한 하위 클래스들만 상속 받아야 할 때 사용됨. 동물 클래스에서 고양이, 강아지, 호랑이와 같은 한정된 종류의 동물만 상속받아야 하는 경우 사용한다. 컴파일 시간에 이미 알려진 한정된 하위 클래스 집합을 가지고 있을 때 유용하다.
"컴파일 시간에 이미 알려진 한정된 하위 클래스 집합"이란, 코드를 컴파일할 때 이미 어떤 클래스가 특정 클래스의 하위 클래스로 정의되어 있음을 알 수 있는 상황을 가르킨다.
즉, 프로그래머가 코드를 작성할 때 컴파일러에게 특정 클래스의 하위 클래스들을 미리 알려줄 수 있고, 컴파일러는 이 정보를 이용하여 코드를 컴파일한다.
따라서 "컴파일 시간에 이미 알려진 한정된 하위 클래스 집합을 가지고 있을 때"는 sealed 클래스를 사용하는 이유 중 하나이다. 이것은 코드를 더 예측 가능하고 안전하게 만들어주며, 개발자가 의도한 대로 코드가 동작할 수 있도록 한다.
유형별 안전 설계가 필요한 경우
프로그램의 다양한 상태를 관리하거나 복잡한 조건 로직을 처리할 때 유용하다. Ssealed 클래스는 when 표현식과 함께 사용하여 다양한 하위 클래스에 대한 동작을 쉽게 다룰 수 있으며, 이는 코드를 더 예측 가능하고 안전하게 만든다.Closed API와 작업할 때
Closed API는 외부에서의 접근을 엄격하게 제한하여 외부 사용자가 API를 변경하거나 확장하지 못하도록 하는 API를 가리킨다. 일종의 캡슐화(encapsulation)를 통해 API의 안정성과 예측 가능성을 유지하고자 하는 개념이다.일반적으로, 라이브러리/프레임워크를 개발할 때 닫힌 API를 사용한다. 외부 사용자는 라이브러리의 기능을 사용할 수 있지만 내부 구현에 접근하거나 수정할 수 는 없다. 이러면 라이브러리의 안정성을 유지하고 다른 개발자들이 예상치 못한 방식으로 라이브러리를 변경하는 것을 방지한다.
sealed 클래스
는 이러한 닫힌 API를 구현하는데 도움 된다. 즉,sealed 클래스
를 통해 특정 클래스의 하위 클래스를 변경하거나 확장할 수 없으므로 API의 안정성과 예측 가능성을 유지할 수 있다.
Constructors
Sealed 클래스
의 경우, 직접적으로 인스턴스화할 수 없지만, 서브클래스의 인스턴스를 생성하는 데 사용되는 생성자를 포함할 수 있다. 즉, Sealed 클래스
의 생성자는 클래스 자체를 인스턴스화하는 것이 아니라, 하위 클래스의 인스턴스를 생성하기 위한 것이다.
Enum class와 같이 사용하는 예제는 다음과 같다.
이렇게 sealed class
를 사용하면, 에러의 종류를 한정적으로 관리할 수 있으며, 각 에러에 따른 처리를 컴파일 타임에서 체크할 수 있다. 이는 런타임 에러를 줄이는 데 도움이 된다.
sealed class
는 protected
(기본값)와 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 data
와 val exception
를 가진다.
다음으로, updateUI
라는 함수를 정의한다. 이 함수는 UIState
타입의 매개변수를 받아, UI의 상태에 따라 처리한다. when
표현식을 사용하여 UI의 상태를 체크하고, 각 상태에 맞는 함수(showLoadingIndicator
, showData
, showError
)를 호출한다.
Use case scenarios : API request-response handling
API 요청과 응답을 처리하는 예제 코드이다. 먼저, ApiRequest
라는 sealed interface
를 정의한다. ApiRequest
인터페이스는 LoginRequest
와 LogoutRequest
두 가지 클래스를 가지고 있다. 각각의 클래스는 ApiRequest
인터페이스를 구현하며, 각각의 API 요청에 필요한 데이터를 가지고 있다.
다음으로, ApiResponse
라는 sealed class
를 정의한다. ApiResponse
클래스는 UserSuccess
, UserNotFound
, Error
세 가지 서브 클래스를 가지고 있다. 각각의 서브 클래스는 API 응답의 상태를 나타내며, UserSuccess
와 Error
는 각각 응답 데이터와 에러 메시지를 가지고 있다.
handleRequest
함수는 ApiRequest
타입의 매개변수를 받아, API 요청에 따른 처리를 수행하고, ApiResponse
타입의 결과를 반환한다. when
표현식을 사용하여 API 요청의 종류를 체크하고, 각 요청에 맞는 처리를 수행한다.
getUserById
함수는 사용자 ID를 받아, 해당 사용자의 정보를 반환하거나, 사용자를 찾을 수 없는 경우에는 UserNotFound
응답을 반환한다.
마지막으로, main
함수에서는 handleRequest
함수와 getUserById
함수를 호출하여, 각각의 API 요청과 응답을 시뮬레이션하고, 결과를 출력한다.