Languages

kotlin의 null 가능성 (?. ?: !!)

!쪼렙조햄 2020. 5. 7. 11:28
반응형

6.1 널 가능성

  • NPE : null pointer exception
  • 코틀린은 Nullability로 NPE를 피할 수 있게 해준다

6.1.1 널이 될 수 있는 타입

  • 코틀린은 java와 다르게 null 이 될 수 있는 타입을 명시적으로 지원한다
  • 모든 타입은 기본적으로 널이 될 수 없는 타입이다
  • ?가 붙어야 널이 될 수 있다
  • 널이 될 수 있는 타입의 변수인 경우 수행할 수 있는 연산이 제한된다
    • 변수.메소드() 형식이 제한된다
      • ?. !! 등을 써줘야 한다
    • 널이 될 수 있는 값을 널이 될 수 없는 타입의 변수에 대입할 수 없다
    • 널이 될 수 있는 타입의 값을 널이 될 수 없는 타입의 파라미터를 받는 함수에 전달할 수 없다
  • null과 널이 될 수 있는 변수를 비교한 다음에는
    • 위의 제한 되는 부분들이 모두 풀리고 컴파일 가능해진다
    • 비교할때는 if 문을 수행할수도있고 다양한 도구가 있다

6.1.2 타입의 의미

  • kotlin에서는 null이 될 수 있는 값과, 될 수 없는 값을 구분해서 각각이 가능한 연산을 명확히 정해둠
    • 실행 시점에 예외를 발생시킬 수 있는 연산을 판단하여 아예 금지시킨다
  • null이 될 수 있는 타입과 널이 될 수 없는 타입의 객체는 같다
    • 널이 될 수 있는 타입은 널이 될 수 없는 타입을 감싼 래퍼타입이 아니다
    • 컴파일 시점에 검사를 수행
    • 그래서 kotlin에서 널이 될 수 있는 타입을 처리할 때 별도의 부가비용이 들지 않는다

6.1.3 안전한 호출 연산자 : ?.

  • ?.
    • null 검사와 메소드 호출
      s?.toUpperCase()
      if(s!=null) s.toUpperCase() else null
  • 아래 식처럼 써야 하는 것을 ?. 로 간단하게 표현 가능
  • ?. 의 결과값도 null이 될 수 있다
  • String.toUpperCase는 String을 리턴
  • String?.toUpperCase는 String?을 리턴
  • 메소드 호출 뿐만 아니라 property를 읽고 쓸때도 ?. 사용 가능
    • ?. 연쇄해서 사용하면 한줄로 정리 가능

6.1.4 엘비스 연산자 : ?:

  • null 대신 사용할 default 값을 지정할 때 사용하는 연산자
  • 이항 연산자
    • 좌항을 계산한 값이 널인지 계산

6.1.5 안전한 캐스트 : as?

  • as 로 타입을 바꿀 수 없으면 ClassCastException이 발생한다
  • as?
    • 대상 타입으로 변환할 수 없으면 null을 반환한다

6.1.6 널 아님 단언 : !!

  • not null assertion
  • 널에 !!를 적용하면 NPE가 발생한다
    person.company!!.address!!.country
  • !! 를 널에 대해서 사용해서 바생하는 예외의 stack trace에는 어떤 파일의 몇번째 줄인지에 대한 정보가 들어있다
    • 어떤 식에서 예외가 발생했는지는 들어있지 않기 때문에 한줄에 중첩해서 사용하는 것 지양

6.1.7 let 함수

  • 안전한 호출 연산자와 함께 let을 사용하면 원하는 식을 평가해서 결과가 널인지 검사한 다음에 그 결과를 변수에 넣는 작업을 처리
  • 널이 될 수 있는 값을 널이 아닌 값만 인자로 받는 함수에 넘기기
    fun sendEmailTo(email : String){~~}
    // sendEmailTo 함수는 null이 아닌 값만 받는다
    // 이 함수에게 널이 될 수 있는 타입의 값을 넘길 수는 없다
    if(email != null ) sendEmailTo(email)
    // 이렇게 검사 후 넘겨주는 방식도 있다
    // let 사용하면
    email?.let{email -> sendEmailTo(email)}
    // 더 짧은 방식
    email?.let{sendEmailTo(it)}
  • let 함수는 email 값이 null이 아닌 경우에만 호출된다
  • null 인 경우에는 아무일도 일어나지 않는다

6.1.8 나중에 초기화할 프로퍼티

  • lateinit
    • 프로퍼티를 나중에 초기화 하는데 사용
    • 나중에 초기화 하는 property는 반드시 var 이어야 한다
    • val property는 final 필드로 컴파일 되며, 생성자 안에서 반드시 초기화 해야 한다

6.1.9 널이 될 수 있는 타입 확장

  • 널이 될 수 있는 타입에 대한 확장함수 정의하면 null을 다루는 도구가 된다
  • ?. !! 등의 안전한 호출 없이도 호출 가능하다

6.1.10 타입 파라미터의 널 가능성

fun <T> printHashCode(t:T){
    println(t?.hashCode())
}
printHashCode(null)
  • T 는 Any? 타입
  • 물음표가 붙어있지 않지만 널이 될 수 있는 타입
fun <T : Any> printHashCode(t:T){
    println(t.hashCode())
}
  • 위와 같이 T:Any 로 지정을 해주면 T는 Any 타입이 된다
  • 널이 될 수 없는 타입이 됨

6.1.11 널 가능성과 자바

  • java와 kotlin의 상호 운용성
    • java 타입 시스템은 널 가능성을 지원하지 않는다
    • java 에서는 annotation으로 널 가능성을 표현할 수 있다
  • 플랫폼 타입
    • kotlin이 널 관련 정보를 알 수 없는 타입
  • 자바 api의 경우 대부분 널 관련 애노테이션을 사용하지 않기 때문에 주의해야 한다
val i: Int = person.name
ERROR : Type mismatch : inferred type is String! but Int was expected
  • String!
  • ! 표기는 String! 타입의 널 가능성에 대해 아무 정보도 없다는 뜻이다
  • 상속
    • 자바 코드를 상속받아 메소드를 오버라이드 할때
      • 널 가능한것, 널 불가능한것으로 모두 구현 가능하다
반응형