0101011001010111

Kotlin_4-5 널 세이프티 ? !! ?. ?: 본문

Kotlin/스파르타_코틀린 문법

Kotlin_4-5 널 세이프티 ? !! ?. ?:

[진주] 2023. 8. 2. 17:58
728x90
반응형

널 세이프티는,

코틀린의 Null 안정성을 향상시켜줄 수 있어요.

Null예외는 프로그램의 가용성을 저하시키는 치명적인 오류에요.

ㄴNull예외의 의미가 뭐지 ?

예외는, 프로그램이 실행되다가 런오류로 중단되고 종료되는 현상을 말하고,

그럼 null예외는 null때문에 예외 현상이 생긴다는 건가?

답 : 내가 생각하는게 맞다함!!! 오케이!!!! 저 뜻이 맞구나!!!!

 

자 Null예외가 발생하면 프로그램이 꺼져버립니다.(사용하다 종료되는 것 만큼 안좋은 행위는 없겠죠?)

 

코틀린은 Null예외로 부터 안전한 설계를 위해 자료형에 Null 여부를 명시할 수 있어요.

- Null 여부 명시 : null을 가질수 있다 없다를 표기하는 행위  // 그렇기 때문에 Null예외에서 안전하다라고 말할 수 있게 되는 거죠.(뭐보다? JAVA보다요 ^_^;)

 

Null예외로 부터 안전한 설계를 위한 다양한 키워드들을 지원합니다.

Null예외로 부터 안전한 설계를 위해 지원하는 키워드들

코틀린은, ? , !! , ?. , ?: 로 Null예외로 부터 살아남으려 한다.

BUT!!!! 강제로 null이 아니라고 하는 의미의 !!는 최대한 사용을 지양해주세요!!( 최대한 사용하지말기)

ㄴ ㅋㅋ 정말!! 자신있는거 아니면 사용 하지말기 ^ㅁ^


다양한 널 세이프티 값 검색해보기▼

앨비스 연산자.

앨비스 연산자

앨비스 연산자는 ?: 으로 표현하고, ?: 의 왼쪽 객체non-null이면 그 객체의 값이 리턴되고,

null이라면 ?: 오른쪽 값을 리턴합니다.

그러니까, 

 5 ?: 9 면, 왼쪽 객체가 non-null(널이아님) 이니까 5가 리턴되고,

null ?: 9 이면 왼쪽값이 null이어서 9를 리턴한다는 거


?

코틀린에서 '?'는 nullable (널허용) 타입을 지정하는데 사용되는 기호입니다.

기본적으로 Kotlin에서는 모든 타입이 non-nullable 입니다. ( 즉, 변수에 null값을 할당X)

그러나 경우에 따라선 null값이 허용되는 것이 필요할 수 있음.

이런 겅우 '?'를 사용하여 타입을 nullable로 선언 가능.

 

예제▼

var a: String = "Hello World"
a = null 

▲위와 같은 코드가 있다고 생각해보자.

문제는 코틀린 기본값이 : non-nallable (null값 허용안됨) 이므로, 변수 a가 null 이기 때문에 오류가 발생할 수 있다.

 

이럴때는 아래코드처럼▼

var a: String? = "Hello World"
a = null // This line is now valid

String 에 ?를 붙여 nullable(널값허용) 해주면 문제가 해결된다!

?는 nullable이므로!!!


 

안전 호출 연산자(safe call operator / Safe-calls)

 

이 연산자는 해당 객체가 null이 아닐 경우에만 메서드를 호출하거나 필드에 접근합니다.

 

예제▼

var a: String? = "Hello World"
println(a?.length)

여기서 변수 a는 : null일수 있는 String(문자열)타입 입니다.

ㄴ String?  ?는 nullable을 뜻하므로

따라서 ?.(안전호출연산자 safe call operator)를 사용하여 a가 null이 아닌 경우에만 length 속성에 접근합니다.

ㄴ뭔말이야 ㅡㅠ 아~ println(a?.length)니까, a가 null이 아닐경우에만 저 프린트를 한다는 말이지 

근데, a?.length라고 표현하면 그냥 nullable을 표시하는 ?를 쓰고 싶은데, 그 연결해주는 연산자가 ( . ) 점이니까 

그럼 a?.length라고 써야하는데 이게 ?.(safe call operator)인지, 아니면 ?(nullable)인지 어떻게 구분해 ? 

 

아? 이해가 되었다.

?는 a를 선언할때 사용하는 거니까 위와같은 println(a?.length) 막 이런 불러오는거에 조합에서 쓸 이유는 없지! 

- 시원 - 

그러니까 저 불러오는거에서는 a?.length가 있으면 nallable이 아니라 safe call operator 인거지!!

 

 

 

(+고오급 지식 추가)안전 호출 연산자(safe call operator)은 체이닝 할 수 도 있다!!!!

멋져.. 추가 개념까지 공부하는 나의모습.ㅋㅋㅋㅋㅋㅋ 쫌 초보 탈출했나 후후.

자 다음 예제를 살펴보자 ▼

var a: Person? = getPerson() // This might return a Person object or null
println(a?.address?.city) // This will print the city only if a and a.address are not null

이런 코드가 있다고 해봐, 

보자마자 난 궁금했지. 

println(a?.address?.city)

이부분이!!!!!!!!!

물음표 많이 들어가니까 갑자기 현타오지만 

 

다시 복기하자 안전 호출 연산자 safe call operator는 뭐라고 ?! 

★이 연산자는 해당 객체가 null이 아닐 경우에만 메서드를 호출하거나 필드에 접근합니다.★

맞아. 

 

그렇기 때문에 ?. 이 2개든 100개든 두렵지 않아.

이건 ?.이 붙은 모든 조건이 null이 아니어야 'city'에 접근할 수 있어!!!


non-null assertion operator (null이 아님을 단언하는 연산자)

 

- 이건 값이 null이 아닌 것을 확신/단언 하는 연산자이다. 

ㄴ 조심해서 써야한다. 100% 확신하는 연산자라.

 

!! 연산자를 사용하면 nullable한 객체에 대해 null이 아님을 단언합니다. 

즉 'a!!'라는 코드는 'a'가 null이 아니라고 확신하고 non-null 타입으로 처리하겠다는 의미입니다.

그러나 만약 'a'가 실제로 null인 경우에는 NullPointerException이 발생합니다.

var a: String? = null
println(a!!.length)

▲ 위의 코드는 실행되지 않고 NullPointerException을 발생시킵니다.

왜냐면 a는 null이지만 !! 연산자를 사용하여 a를 non-null로 취급하려고 시도했기 때문입니다.

 

따라서, !! 연산자는 null이 아님을 확실히 알고 있는 경우에만 사용해야합니다.

또한 이 연산자를 남용하면 NullPointerException을 발생시킬 수 있으므로 주의해야 합니다.

Kotlin의 주요 목표 중 하나는 NullPointerException을 최소화하는 것이므로. 가능하면 `?.` 연산자나 `?:`(elvis operator)등을 사용하여 null을 안전하게 처리하는것이 좋습니다.


 

 

예시 ▼

주소를 저장하는 address변수는 null을 저장할 수 있다고 String?으로 선언해요

null을 저장하지 않고 설계하려면 lateinit var로 대체할수 있어요. // 저번시간에 lateinit은 배웠다!! 그.. 변수 지연초기화! 

fun main(){
    var s = Student()
    s.name = "참새"
    s.address = "서울"
    s.displayInfo()
}

class Student {
    lateinit var name:String
    var address:String? = null

    fun displayInfo() {
        println("이름은: ${name} 입니다")
        println("주소는: ${address} 입니다")
    }
}

lateinit var name:String

name은 lateinit var (변수지연초기화)로 선언되었고, 문자열이다.

address는 물음표(?)를 붙여서 nullable (널 허용) 으로 해줬습니다.

address의 값은 null이죠 ? 

 

처음에 null을 가졌지만, address라는 거에 서울로 바꿔준거고 

s.displayInfo()하면 이름과 주소를 정상적으로 출력 하게 됩니다.


!! 의 예제

!! : 해당값은 null이 아님을 100% 단언 하는 연산자 

 

fun main(){
//  var data = readLine()!!.toInt()

    var inputData = readLine()!!
    var data = inputData.toInt()
    println("Null아닌 값: ${data}")
}

ㄴ 이것 같은 경우에는 저희가 많이 사용했던 키워드에요.

이 변수/상수 값은 반드시 null이 아니야 라고 해주는거죠

 

readLine()!!

readLine이라는 메서드를 호출() 하면 어떤 리턴을 해주는데, 그 값이 반드시 null이 아니야 라고 !! 를 붙여준거에요 

 

var inputData = readLine()!!

그것을 변수 inputData에 넣어주고 

 

var data = inputData.toInt()

(다음줄) inputData는 당연히 null이 아니니까 null체크를 할 필요없이 바로 toInt라는 걸 실행()하고 

 

println("Null아닌 값: ${data}")

null이 아닌 값인 데이터를 출력 할 수 있게 되는 겁니다. 

 

지금 설명한건 var data = readLine()!!.toInt() << 요 한줄로 끝나는 걸 풀어서 설명한 것이다.

이렇게 한줄로 가능한 이유는

원래 위에 설명했듯, 

 

 

var inputData = readLine()!!     //원래는 여기서 끊고 

var data = inputData.toInt()    //이렇게 다음이 나와야하지만, 

 

var data = readLine()!!.toInt()    //이게 널이 아니라고 했으니 바로 toInt()를 써줄수 있었던 것이다.


?.키워드로 Null인지 확인하고 Null이 아닐때만 참조하는 메소드를 실행하도록 작성해요

 

var address:String? = null

주소를 저장하는 address는 초기값이 null이기 대문에 null위협에 놓여있어요.

 

Null이 아님을 보장해야하는데 강제로 !!를 사용하는 것은 현 상황에 바람직 하지 않아요.

 

?.안전 호출연산자(safe-calls)라고 해요

 

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} 입니다")
    }
}


위 코드 실행시 결과값

자, 메인에 있는 순서대로 실행이 된다.

 

var s = Student()

 

먼저 s라는 변수는 Student()를 실행하는거라고 알려주고.

 

s.name = "참새"

s.displayAddressLength()

 

s //스튜던트 실행시 

.name 은 참새이다.

 

이름값 들어갔고, 

 

이제 s.displayAddressLength()를 위의 값으로 실행시킴.

여기서 

println("주소의 길이는: ${address?.length} 입니다") // 이름은 출력시 필요하지 않은 함수이다.

 

var address:String? = null

address값은 null이기 때문에 주소는 null인데,

 

 

?. 를 썼을 때, null이면 그냥 종료되는 것이 아니라 ,

null일경우 null로 출력이 되게 된다.

 

그렇기 때문에 위와 같은 값은 여기서 나온거다.

 

 

자, 다음 main에서 실행할 구문은 

 

s.address = "서울"

s.displayInfo()

 

이다.

 

이번엔 address값을 서울로 받았고, 

 

s.displayInfo()

s = student클래스를 토대로 // displayInfo() 함수를 실행한다.

 

fun displayInfo() {

println("이름은: ${name} 입니다")

println("주소는: ${address} 입니다")

}

 

출력하게 되면 

아래 두줄에 해당 하는 값이 아랫줄 값이다.


자, 근데 ?. 안전 호출연산자(safe-calls)로 실행을 했는데,

?. 에서 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 ?: "초기화하세요"} 입니다")
    }
}

 

println("주소의 길이는: ${address?.length ?: "초기화하세요"} 입니다")

 

?.이라는 것을 해서 null이란 것을 호출했는데, 

이제 다시 ?:으로 null이면 "초기화하세요"라는걸 호출할거에요 (null대신에)

결과값.

null 대신에 초기화 하세요 라고 나온거에요 

 

앨비스 연산자

앨비스 연산자는 ?: 으로 표현하고, ?: 의 왼쪽 객체 non-null이면 그 객체의 값이 리턴되고,

null이라면 ?: 오른쪽 값을 리턴합니다.


널 세이프티는 코틀린의 가장 큰 장점중 하나에요.

그중에 null을 가질 수 있게하는 `?` 키워드 

null이 아님을 보장하는 `!!`키워드 (하지만 지양하셔야되고)

?. (safe-calls)는 null이 아닐때만 실행하게 할 수 있어요. //다만, null일 경우 null이라고 그대로 출력이 됩니다.

 

그래서 ?:  , 앨비스 연산자를 통해서 null이 아닐때와 null일때를 구분할 수 있고 null이라는 문자열 대신에 다른 문자열로 대체해서 출력도 할 수 있게 됩니다.

 

 

 

 

 

 

 

 

 

 

728x90
반응형