마지막 Kotlin 기초 3편에서는 1) 초기화 지연 2) data class 3) sealed class 4) object 5) companion object
이 5개의 내용에 대해서 다룰 것입니다.
1. 초기화 지연
- 선언, 지정은 나중에
- null-Safe
- 메모리 효율을 높임
lateInit var | val by lazy |
변수 타입을 지정할 것 | 선언과 동시에 초기화가 진행 |
초기화 타입에는 사용하지 않는다. (대체: Delegate 쓸 것) [예시] 1) private lateInit var age: Integer (Int X) 2) private var age by Delegates.notNull<Int>() |
람다형태라 마지막 행이 정의 값 [예시] val age: Int by lazy { println("age 초기화") 32 } |
1) age = 32 2) age = Integer(value = 32) |
이후 초기화 불가능 // 단, 선언 후 사용하지 않으면 메모리 할당은 안됨 |
lateInit var은 선언 이후에 초기화가 필요하고 타입 선언은 필수지만, primitive type은 초기화 할 수 없습니다.
val 변수명: 타입 by lazy 형태로 초기화하는 것은 람다 형식이기에 바로 선언과 함께 마지막 행에 값을 할당받아 초기화되지만,
실제로 호출해서 사용하지 않는다면 초기화 자체가 발생하지 않기 때문에 메모리 할당도 되지 않습니다.
즉, 효율을 높이는 것이며 val 선언이기에 lateInit var과 다르게 추후 변수 변경이 되지 않습니다.
2. data class
1) 데이터를 담기위한 클래스로 toString(), hashCode(), equals(), copy() 메서드를 자동으로 생성합니다.
2) override해서 직접 커스텀하여 사용할 수도 있습니다.
3) 1개 이상의 Property가 존재해야 합니다.
4) 네트워크 통신을 통해서 서버 내용을 받을 때, Response의 원하는 데이터를 담을 수 있는 클래스로 구현할 때 쓰입니다.
data class Person (
val name: String,
val age: Int,
) {
override fun toString(): String {
return "이름: $name, 나이: $age"
}
}
fun main() {
val person = Person("성호", 32)
println(person.toString())
println(person.copy(age = 33))
}
이처럼 data class를 정의할 때 Property로 name, age를 선언해주고 toString()은 이미 구현되어 있지만
커스텀해서 재구현하면 person.toString() 할 때 커스텀한 함수가 수행될 것입니다.
또한 copy()라는 메서드가 이미 정의가 되어 있기 때문에 .copy(age = 33) 으로 하게 된다면
person이라는 객체에서 age의 property로 정의된 값 32를 33으로 복사하여 재설정합니다.
따라서 이후 출력된 값은 age값이 33으로 변경되어 반환될 것 입니다.
3. sealed class
추상클래스입니다. 그렇다면 상속과 구현부가 필요하겠죠..? 그럼 abstract class랑 차이는 무엇일까요..?
abstract class와 sealed class와의 차이는 상속받은 자식 클래스를 부모가 알고 있는지 없는지 차이입니다.
코드로 설명하겠습니다.
abstract class Dog
class DogA : Dog()
class DogB : Dog()
class DogC : Dog()
class DogD : Dog()
class DogE : Dog()
fun main(){
val dog: Dog = DogA()
val result = when(dog){
is DogA -> { 구현부 }
is DogB -> { 구현부 }
is DogC -> { 구현부 }
else -> { 구현부 }
}
}
main에서 result 부분에 이 dog가 DogA인지, DogB인지, DogC인지 어떤 것을 상속받은지 부모는 모릅니다.
또한 자식 클래스가 얼마나 더 있는지 부모 클래스는 모르기 때문에 필히 else를 써주어야 합니다.
DogZ까지 있을수도 있으니까 else가 필요하다는 말입니다.
그렇지만 sealed class인 경우는 부모 클래스가 내 자식 클래스(상속 받은 것)의 수를 알고 있기 때문에 else가 불필요합니다.
물론 써도 되긴 하지만.. 확실하게 명시해서 자식 하나하나를 구현해주는 것이 더 좋습니다.
sealed class Dog
class DogA : Dog()
class DogB : Dog()
class DogC : Dog()
class DogD : Dog()
class DogE : Dog()
fun main(){
val dog: Dog = DogA()
val result = when(dog){
is DogA -> { 구현부 }
is DogB -> { 구현부 }
is DogC -> { 구현부 }
is DogD -> { 구현부 }
is DogE -> { 구현부 }
}
}
이렇게 자신을 상속받은 자식 클래스의 갯수를 명확히 알고 있기 때문에 각각 정의를 해줘야 합니다.
이는 에러 케이스를 체크할 때나 RecyclerView에서 View Type을 체크할 때 아주 효율적으로 사용됩니다.
4. object
1) 클래스를 정의함과 동시에 객체를 선언을 합니다. (싱글턴)
2) 생성자 사용이 불가합니다.
3) Property, Method, init { } 사용 가능합니다.
4) 다른 클래스나 인터페이스를 상속받을 수 있습니다.
object Counter : Greeting() {
init {
println("초기화")
}
private var count = 0
fun addCount(){
count++;
}
fun getCount(): Int {
return count
}
}
open class Greeting(){
fun hello() = println("안녕!")
fun bye() = println("잘가!")
}
private lateInit var count: Integer
fun main(){
count = Counter.getCount()
println(count)
Counter.addCount()
Counter.addCount()
count = Counter.getCount()
Counter.hello()
}
1) object는 클래스를 정의함과 동시에 객체를 선언합니다.
: 위 코드에서 Counter라는 object로 선언한 값을 main에서 객체로 선언하지 않고도 바로 사용할 수 있습니다.
싱글턴 패턴과 비슷한 느낌입니다. 그냥 Counter.getCount(), Counter.addCount() 이렇게 수행할 수 있습니다.
2) constructor를 사용할 수 없습니다.
: object에서는 생성자를 허용하지 않기 때문입니다.
3) Property로 count를 셋팅했고 Method는 addCount, getCount를 정의했습니다.
init 초기화 블록을 둘 수 있었으며, 1)에 의해서 바로 호출하여 사용할 수 있었습니다.
4) Greeting이라는 클래스를 상속받았습니다. 상속을 해주기 위해선 open이라는 키워드가 필요합니다.
상속을 받은 object는 부모 클래스의 메소드를 반환하여 호출할 수 있습니다.
5. companion object
1) Java에서의 static 역할을 합니다.
2) 클래스 안에서는 하나만 생성할 수 있습니다.
3) 팩토리 패턴으로도 활용 가능합니다.
import com.example.kotlin.Book.Novel.NAME
fun main(){
val bookName: String = Book.NAME
println(bookName)
println(NAME)
val book1 = Book.create()
}
class Book {
companion object Novel {
const val NAME: String = "소설책"
fun create() = Book() // 팩토리 패턴
}
}
이처럼 1) Book이라는 클래스의 NAME을 따로 객체 선언하지 않아도 companion object로 감쌋기 때문에 static 느낌으로
가져올 수 있습니다. (Book.NAME)
2) Book이라는 클래스 안에는 companion object는 하나만 생성되어야 합니다.
3) companion object의 이름을 Novel로 셋팅했습니다.
원래는 NAME이라는 static 변수를 Book.NAME 형태로 가져와야 하는데 companion을 import해서 가져올 수도 있습니다.
허나, 여기선 Novel로 명명하였기 때문에 "import com.example.kotlin.Book.Novel.NAME" 형태로 가져올 수 있습니다.
4) companion object 안에 create라는 함수를 생성해놓고 이를 Book 클래스 객체 생성 형태로 정의해두었습니다.
팩토리 패턴을 활용한 방식이며, 메인에서 Book.create() 호출만으로 객체 생성을 완료할 수 있습니다.
지금까지 Kotlin의 기초 3편에 대해서 정리하였으며, 코틀린 정리에 마지막 단계입니다.
다음 포스팅부터는 안드로이드 개발 내용에 대해서 파헤치도록 하겠습니다. 감사합니다.
'Android' 카테고리의 다른 글
Xml - style 구성 / Intent 관리 (0) | 2024.07.21 |
---|---|
카운트를 측정하는 앱 - 화면전환 (0) | 2024.07.19 |
Kotlin 기초 2편 (0) | 2024.07.11 |
Kotlin 기초 1편 (0) | 2024.07.11 |
BottomNavigationView (0) | 2024.07.07 |