Kotlin 문법 종합반 #4上. 에러 대응 _ 예외처리 / 지연 초기화 / 널세이프티

·2024년 6월 11일
0

4-2. 예외처리

숫자를 입력하는 칸이 있습니다

var number = readLine().toInt

여기 한글을 넣으면 Int로 변환할 수 없어 오류가 납니다
오류가 발생하면, 런타임 에러와 함께 앱이 비정상 종료됩니다

이를 방지하기 위해서
에러가 예상되는 경우 "예외처리"를 해야합니다

try - catch

try {
} catch (타입) {
}
  • try { } 에는 정상 수행할 코드를 적습니다

  • 괄호 () 속에는 에러의 타입을 적어줍니다. NumberFormatException 등이 있습니다.

  • catch() { } 에는 에러가 발생할 경우 예외적으로 수행할 코드를 적습니다
    경고문구를 적어두면 좋겠네요

while(true){
	try {
    var number = readLine().toInt
    break
    
    } catch (NumberFormatException) {
    println ("숫자만 입력하세요")

    } finally {
    <키보드 연결을 해제하는 코드>
    }
}

잘못된 입력 -> catch 코드 -> while 반복 -> 정상적인 입력 -> try 코드 -> break

  • 재시도가 필요한 경우, while 문과 try - catch 코드를 함께 쓰면 좋습니다
    이때 try에 break를 넣어둡니다
  • try와 catch에 공통으로 들어가는 코드는 finally { } 에 적어줍니다
    자원 관리를 위해 네트워크, 키보드 연결을 off 하는 코드를 finally에 종종 씁니다

throw 에러타입 ("")

throw(타입)
  • 에러가 예상될 경우, throw()로 예외를 던집니다
    코드를 즉시 중단하고 코드를 거슬러올라가 "catch"코드를 찾아 예외처리합니다
    만약 catch 코드가 없다면 앱이 종료됩니다

  • throw 뒤에 에러의 타입을 적어줍니다. NumberFormatException, IllegalArgumentException 등이 있습니다.

  • 괄호 () 속에는 로그 안내문구를 적어줍니다

  • 에러가 예상될 때마다 try 와 catch를 일일이 나눠적을 수 없으니
    if () throw () 로 예외를 던지고 → 이전에 작성한 try - catch 문으로 예외를 처리하여
    코드 양을 줄이는 것으로 이해했습니다


4-3. 지연 초기화

초기화

var 나이 = 3
val 종 = 푸들

변수, 상수를 선언하면 "초기값" 을 입력해야 합니다.
값이 없을 때 생기는 오류를 막기 위함입니다.

그러나 다수의 인스턴스를 생성할 때,
클래스가 가진 모든 프로퍼티에 전부 초기값을 대입하면
불필요한 정보로 인해 메모리가 낭비됩니다

그래서 지연초기화를 사용해,
불필요한 초기값을 유보하고 필요해졌을 때 값을 대입합니다


lateinit var 변수

class 동물(){
	lateinit var 이름:String
    
	fun 자기소개() {   
    	print (" 안녕! 나는 ${this.이름}이야 ")
	}
}

lateinit var 으로 "초기값 없이 선언" 합니다

fun main(){
	
    var 개 = 동물()
    개.이름 = 돌돌이
    개.자기소개()
}

인스턴스를 생성한 다음, 필요할 때 초기값을 대입하면 됩니다

초기화 검사

class 동물(){
	lateinit var 이름:String
    
	fun 자기소개() {

		if ( !this::이름.isInitialized ){
        return
        }
        
    	print (" 안녕! 나는 ${this.이름}이야 ")
	}

}

단, 초기값을 대입하지 않고 없는 상태로 프로퍼티를 언급할 경우
런타임 에러로 앱이 종료됩니다
그래서 if 문으로 초기화 여부를 검사하는 과정을 넣어주는 것이 안전합니다

.isInitialized

  • 초기화 여부를 검사합니다. 초기값이 대입되어 있으면 true, 없으면 false 입니다

this :: name

  • 클래스를 닷( this . name )으로 값만 가져와서는 초기화 여부를 알 수 없습니다
    리플렉션을 통해 메타데이터를 얻으려면 콜론2개( this :: name )로 "참조"하여 가져옵니다

val 상수 by lazy { "초기값" }

class 동물(){
	val 국적 by lazy {
    	"한국"
	}
        
	fun 자기소개() {
    print (" 저는 ${국적}에서 왔어요 ")
    }
}

상수는 읽기 전용이라서 초기값 수정이 불가능합니다.
따라서 "초기값을 미리 설정" 해두되,
대입은 하지 않았다가 → 처음 언급될 때, 해당 초기값을 대입합니다

  • 변수의 lateinit 은
    애초에 초기값이 존재하지 않습니다. 인스턴스를 만들 때 새로운 초기값을 대입합니다.

  • 상수의 by lazy는
    미리 초기값을 설정해뒀습니다. 대입을 유보함으로서 메모리를 절약합니다.


4-4. 널 세이프티

원래 변수를 초기화(대입)하지 않으면
비어있음을 뜻하는 null 이 출력되어야 합니다

하지만 코틀린은 null 출력을 미연에 방지하기 위해
초기화를 강제하고, 값이 없을 시 컴파일 에러를 띄웁니다

따라서 만약 값이 없다는 정보를 표현하고 싶다면,
null을 별도로 허용해주어야 합니다

null을 허용하는 방법

var 변수:자료형? = null

var name:String? = null
  • 원래는 String 자료형에 null을 대입할 수 없습니다
    자료형 뒤에 물음표 ( ? )를 붙여, 자료형을 널 허용 타입으로 설정했습니다
    이제 변수에 null을 할당하여 비어있음을 표현할 수 있습니다

  • 초기값으로 null을 대입해줍니다
    null로 초기화 했기 때문에, 새로운 값을 대입하기 전까진 null, 즉 "비어있음" 상태입니다

.to자료형OrNull

    println( "${  readLine()!!.toIntOrNull()  }" )

.toInt()

  • 자료형을 Int로 변환합니다. 만약 정상적으로 변환할 수 없다면 컴파일 에러가 뜹니다.

.toIntOrNull()

  • 자료형이 Int로 변환합니다. 만약 변환할 수 없다면 null을 출력합니다.
    이런 방식으로 코틀린에서도 null을 사용해 "비어있음"을 표현할 수 있습니다

null을 활용하는 방법

var name:String? = null

null이 생길 가능성을 허용했다면
아래와 같이 null 을 감지하고 대응해야 합니다

안전호출연산자 ( ?. )

print (" ${name?.length} ")

변수에 null 이 대입되었는지 검수합니다
만약 null이라면 .length 메서드를 실행하지 않습니다

  • 출력결과 → null

엘비스연산자 ( ?: )

print (" ${name? : "홍길동" } ")

변수에 null 이 대입되었는지 검수합니다
만약 null이라면 다음 "홍길동" 코드로 교체합니다

  • 출력결과 → 홍길동

!! 연산자

print (" ${name!!.length } ")

변수에 null이 대입될 경우, 런타임 에러가 발생합니다
null이 절대 없다는 확신이 있을 때만, 보증의 의미로 사용합니다

  • 출력결과 → 런타임 에러@@@

명시적 널 체크

    if (name != null) {
        print (" ${name.length } ")
    }

if 문을 활용해서 null 이 아닐 때만 명령이 동작하도록 체크하는 방법도 있습니다

  • 출력되지 않음
profile
디자이너의 개발 창업 도전기

0개의 댓글