클래스와 프로퍼티 간단한 정리
코틀린의 클래스는 비공개 필드, 공개 게터, (선택적으로) 공개 세터를 디폴트로 제공합니다.
class Person(
val name: String, // 비공개 필드, 공개 게터
var isMarried: Boolean // 비공개 필드, 공개 게터, 공개 세터
)
👇
person.name = "new name" // ❌ 불가능
person.isMarried = true // ⭕ 가능
+ 비공개 필드란, 프로퍼티가 저장되는 곳입니다.
필드는 비공개하여 직접 접근을 막는 대신 getter/setter를 통해 값을 읽거나 쓸 수 있게 해요.
커스텀 접근자도 만들 수 있어요.
내부적으로 관리가 필요하지 않지만, 외부에서 알고싶은 값에 사용하면 될 것 같아요.
class Rectangle (val height: Int, val width: Int) {
val isSquare: Boolean
get() = height == width
}
Data Class
특징
- 상속 불가
- 1개 이상의 프로퍼티 필수로 가져야 함
유용한 메서드
- copy() ⭐
data class HomeUiState (
val myProfile: Profile? = null,
val otherProfiles: List<Profile> = listOf()
)
class HomeViewModel : ViewModel() {
private val _uiState = MutableStateFlow(HomeUiState())
val uiState: StateFlow<HomeUiState> = _uiState.asStateFlow()
init {
getMyProfile()
getOtherProfiles()
}
fun getMyProfile() {
_uiState.value = _uiState.value.copy(
myProfile = myProfile
)
}
fun getOtherProfiles() {
_uiState.value = _uiState.value.copy(
otherProfiles = homeProfiles
)
}
}
- toString()
data class Rectangle (
val height: Int,
val width: Int
)
fun main() {
val rec = Rectangle(3, 4)
println(rec)
}
// 결과 : Rectangle(height=3, width=4)
- hashCode()
data class Rectangle (
val height: Int,
val width: Int
)
fun main() {
val rec1 = Rectangle(3, 4)
val rec2 = Rectangle(3, 4)
val rec3 = Rectangle(32, 42)
println(rec1.hashCode()) // 97
println(rec2.hashCode()) // 97
println(rec3.hashCode()) // 1034
val recSet = setOf(rec1, rec2, rec3)
println(recSet) // [Rectangle(height=3, width=4), Rectangle(height=32, width=42)]
}
- equals()
data class Rectangle (
val height: Int,
val width: Int
)
fun main() {
val rec1 = Rectangle(3, 4)
val rec2 = Rectangle(3, 4)
println(rec1 == rec2) // true (equals())
println(rec1 === rec2) // false
}
Interface
특징
- 클래스의 규칙을 정의하는 타입/계약
- 객체를 만들 수 없음
- 다른 클래스를 통해 구현됨
예제
- 레포지토리 추상화 "UserRepository를 구현하는 클래스는 반드시 getUserInfo 메서드를 제공한다"
interface UserRepository {
suspend fun getUserInfo() : UserResponse
}
인터페이스 규칙에 따라 실제 레포지토리 구현
// 네트워크 연동된 구현체
class UserRepositoryImpl (
private val userDataSource: UserDataSource,
) : UserRepository {
override suspend fun getUserInfo(
) = userDataSource.getUserInfo()
}
// 테스트용 Mock 구현체
class MockUserRepository : UserRepository {
override suspend fun getUserInfo(
) = UserResponse(name = "dodo", part = "android")
}
레포지토리를 사용하는 ViewModel은 인터페이스인 UserRepository로 레포지토리를 받아옴
인터페이스가 getUserInfo 메서드를 보장하므로 호출 가능
class HomeViewModel (
private val userRepository: UserRepository,
) : ViewModel() {
private val _uiState = MutableStateFlow(HomeUiState())
val uiState: StateFlow<HomeUiState> = _uiState.asStateFlow()
private fun getUserInfo() {
viewModelScope.launch {
val userInfo = userRepository.getUserInfo()
_uiState.value = _uiState.value.copy(
)
}
}
}
인터페이스를 사용했기 때문에 HomeViewModel의 코드를 전혀 수정하지 않고도
상황에 따라 구현체를 넣어줌으로서 동작을 변경할 수 있음
Sealed Class
개념
- 추상 클래스
- 자신을 상속받는 여러 개의 서브 클래스를 제한적으로 가질 수 있음
특징
- enum class의 확장
sealed class UIState {
data object Loading : UIState()
data class Success(val data: String) : UIState()
data class Error(val exception: Exception) : UIState()
}
fun updateUI(state: UIState) {
when (state) {
is UIState.Loading -> showLoadingIndicator()
is UIState.Success -> showData(state.data)
is UIState.Error -> showError(state.exception)
}
}
enum class와 달리 클래스 별로 다른 프로퍼티를 가질 수 있음
UIState를 상속받는 클래스가 정확히 Loading, Success, Error 3개로 보장
외부에서 임의로 상속 불가하며, when에서 else를 사용하지 않아도 오류가 발생하지 않음
'Android' 카테고리의 다른 글
| [Kotlin] Kotlin in Action 3장. 함수 정의와 호출 (0) | 2025.11.12 |
|---|---|
| [Kotlin] Kotlin in Action 2장. 코틀린의 기초 (0) | 2025.11.02 |
| [Kotlin] Kotlin in Action 1장. 코틀린이란 무엇이며, 왜 필요한가? (0) | 2025.10.26 |