변수나 상수의 값을 나중에 초기화할 수 있다.
코틀린은 클래스를 설계할 때 안정성을 위해 반드시 변수의 값을 초기화할것을 권장한다. (클래스 만들 때 = ""나, = 0과 같이 공백이라도 넣어준 것이 바로 초기화했던 것!)
클래스를 설계할 때 초기의 값을 정의하기 난처해서 나중에 대입하기 위한 문법이다. (클래스 만들 때 바로 초기화할 수 있는 경우는 드물다.)
저사양으로 제한되어있는 환경에서는 메모리를 더욱 효율적으로 사용할 수 있다. (항상 처음부터 값이 들어있는 것 보다는 실제로 메모리를 효율적으로 관리하면서 필요할 때 값을 초기화해주는 게 더 좋다.)
코틀린은 지연초기화 또는 늦은초기화를 위해 변수는 lateinit, 상수는 lazy 키워드를 활용한다.
▶️ 변수의 지연초기화 : lateinit
fun main() {
var s1 = Student()
s1.name = "참새"
s1.displayInfo()
s1.age = 10
s1.displayInfo()
}
class Student {
lateinit var name: String // var name: String = "" 으로 해도 되지만 가독성이 좋지 않다.
var age: Int = 0
fun displayInfo() {
println("이름은: ${name}입니다.")
println("나이는: ${age}입니다.")
}
}
/* 결과
이름은: 참새입니다.
나이는: 0입니다.
이름은: 참새입니다.
나이는: 10입니다.
*/
fun main() {
var s1 = Student()
s1.name = "참새"
s1.displayInfo()
s1.age = 10
s1.displayInfo()
}
class Student {
lateinit var name: String
var age: Int = 0
fun displayInfo() {
if (this::name.isInitialized) { // name이 lateinit으로 잡혀있는데 사용하기 전에 정말 초기화 됐는지 확인하기 위해 isInitialized를 부르고 그게 true면(초기화됨) if를 실행하고, false(초기화 안 됨) else를 실행한다.(this::name으로 접근)
println("이름은: ${name}입니다.")
println("나이는: ${age}입니다.")
} else {
println("name변수를 초기화해주세요.") // 이렇게 해야 더 안정성이 있게 된다. 만약 초기화가 안 됐는데 코드를 실행한다면, 예외가 발생하고, 예외 처리를 위한 try-catch로 묶어야 해서 코드가 길어지고 성능이 저하됨(isInitialized 한 줄로 훨씬 간결하게 작성)
}
}
}
/* 결과
이름은: 참새입니다.
나이는: 0입니다.
이름은: 참새입니다.
나이는: 10입니다.
*/
▶️ 상수의 지연초기화 : lazy
fun main() {
var s1 = Student()
s1.name = "참새"
s1.displayInfo()
s1.age = 10
s1.displayInfo()
}
class Student {
lateinit var name: String
var age: Int = 0
val address: String by lazy { // 자료형이 String인 상수 adress를 lazy로 만들건데 초기화 되었을 떄 실행할 코드와 초기화 값(seoul)을 넣어 줌.
println("address 초기화")
"seoul"
}
fun displayInfo() {
println("이름은: ${name} 입니다.")
println("나이는: ${age} 입니다.")
println("주소는: ${address} 입니다.")
}
}
/* 결과
이름은: 참새 입니다.
나이는: 0 입니다.
address 초기화
주소는: seoul 입니다.
이름은: 참새 입니다.
나이는: 10 입니다.
주소는: seoul 입니다.
*/
▶️ 변수를 null을 저장할 수 있다는 것은 String?으로 선언한다.
// 주소를 저장하는 address를 String?으로 선언해 보자.
fun main() {
var s = Student() // s라는 이름으로 인스턴스 만듦
s.name = "참새"
s.address = "서울"
s.displayInfo()
}
class Student {
lateinit var name: String
var address: String? = null
fun displayInfo() {
println("이름은: ${name} 입니다")
println("주소는: ${address} 입니다") // address는 처음에 Null을 가졌지만, 서울이라는 값을 넣어줬음.
}
}
/* 결과
이름은: 참새 입니다
주소는: 서울 입니다
*/
▶️ 메소드를 호출하고 전달받은 리턴값(변수, 상수 값)이 null이 아님을 !!키워드로 보증한다.
fun main(){
// var data = readLine()!!.toInt() // 이 한 줄을 밑의 두 줄로 쪼개 볼 수 있다.(Null이 아니니 바로 toInt() 부름)
var inputData = readLine()!! // readLine이라는 메소드를 호출하면 리턴되는 값이 반드시 Null이 아니라는 것을 !! 붙여서 보장하는 것
var data = inputData.toInt() // 위에서 inputData에 넣어준 값은 당연히 Null이 아니니까 Null 체크할 필요 없이 바로 .toInt() 실행.
println("Null아닌 값: ${data}")
}
▶️ ?.키워드로 Null인지 확인하고 Null이 아닐 때만 참조하는 메소드를 실행하도록 작성해야 한다. (Null을 가진 애가 있지만 Null이 아닐 때만 실행하는 똑똑한 코드)
fun main(){
var s = Student()
s.name = "참새"
s.displayAddressLength()
s.address = "서울"
s.displayInfo()
}
class Student {
lateinit var name:String
var address:String? = null // address는 null을 저장할 수 있다.
fun displayInfo() {
println("이름은: ${name} 입니다")
println("주소는: ${address} 입니다")
}
fun displayAddressLength() {
println("주소의 길이는: ${address?.length} 입니다") // 메소드는 null이면 호출이 안 된다. ?.을 붙이면 address가 null인지 확인해서 null이 아니면 메소드 실행하고, null 발생했을 때 프로그램이 꺼지지 않고 null이라고 호출한다.
}
}
/* 결과
주소의 길이는: null 입니다
이름은: 참새 입니다
주소는: 서울 입니다
*/
▶️ ?. 키워드로 안전하게 실행했지만 null이 출력되는것을 막고싶을 때 ?:(엘비스 연산자) 키워드를 함께 사용해서 null 대신에 다른 문자열을 출력할 수 있다.
fun main(){
var s = Student()
s.name = "참새"
s.displayAddressLength()
s.address = "서울"
s.displayInfo()
}
class Student {
lateinit var name:String
var address:String? = null
fun displayInfo() {
println("이름은: ${name} 입니다")
println("주소는: ${address} 입니다")
}
fun displayAddressLength() {
println("주소의 길이는: ${address?.length ?: "초기화하세요"} 입니다") // null 대신 "초기화하세요"가 출력
}
}
/* 결과
주소의 길이는: 초기화하세요 입니다
이름은: 참새 입니다
주소는: 서울 입니다
*/