데보션앱 소개페이지 바로가기
로그인 선택

신고하기

CLOSE
신고사유 (대표 사유 1개)
상세내용 (선택)
0/200
  • 신고한 게시글은 더 이상 보이지 않습니다.
  • 이용약관과 운영정책에 따라 신고사유에 해당하는지 검토 후 조치됩니다.
  • 허위 신고인 경우, 신고자의 서비스 이용이 제한될 수 있으니 유의하시어 신중하게 신고해 주세요.
(이 회원이 작성한 모든 댓글과 커뮤니티 게시물이 보이지 않고, 알림도 오지 않습니다.)

미리보기

커뮤니티

      1,234

      badge 23.06.15

      글 등록

      카테고리를 선택해주세요.

      DEVOTEE를 활성화 시키면
      지금 작성한 커뮤니티 글에 대해 1개의 댓글을 달아줍니다.

      버튼을 누르면 글 수정 시 ChatGPT가 작성한 댓글이 수정됩니다.

      임시저장함에 저장되었습니다. 저장일시 : 2022.5.17 14:29:08

      임시저장함

      제목을 선택하시면 이어서 작성이 가능하며,
      최대 20건까지 저장합니다.
      컨텐츠 유형, 제목, 저장일시, 삭제로 이뤄진 임시저장 목록
      컨텐츠 유형 제목 저장일 삭제

      데보션 블로그 게재 요청

      CLOSE
      • *
      • *

      본인인증

      효율적인 데보션 서비스 이용 및
      고객님의 소중한 개인정보보호를 위해
      본인인증을 진행해주세요. 본인인증 미 진행 시 로그인이 제한됩니다.
      본인인증 실패

      본인인증 로그인에 실패하였습니다.
      회원이 아니시거나 본인인증 등록이
      완료되지 않은 사용자입니다.

      회원정보 연결

      [kotlin] 널 세이프 프로그래밍 알아보기

      KIDO 24.02.06
      1,722 7 0
      DEVOTEE 요약
      코틀린은 기본적으로 변수에 null을 할당하지 못하도록 제한하며, null 값 할당을 위해선 '<타입>?'으로 지정해야 한다. 코틀린에서 NullPointerException (NEP)가 발생할 수 있는 원인에는 사용자가 명시적으로 NullPointerException()을 발생시키거나 '!!' 오퍼레이터를 사용한 경우, 초기화와 관련된 일치하지 않는 데이터, 초기화되지 않은 this 또는 객체를 사용한 경우, 참조하는 Java 객체가 null일 때 등이 있다. null 값을 안전하게 처리하기 위한 코틀린의 기능으로는 조건 검사 시 null 체크, val로 값을 할당하여 불변성 확보, '?.'를 이용한 안전 호출, 엘비스 연산자, 안전한 타입 캐스팅 등이 있다.

      Null Safe 프로그래밍

      from: https://kotlinlang.org/docs/null-safety.html#safe-casts

      • kotlin은 기본적으로 변수에 null을 할당할 수 없도록 제약하고 있다.

      • kotlin에서 NullPointerException (NEP) 가 발생할 수 있는 원인은 다음과 같다.

        • 명시적으로 throw NullPointerException() 을 수행한경우

        • '!!' 오퍼레이터를 사용한경우

        • 다음과 같은 경우 초기화와 관련된 데이터 불일치가 발생한다.

          • 생성자에서 사용할 수 있는 초기화 되지 않은 this가 전달되어 사용된경우

          • 수퍼클래스 생성자가 오픈 멤버를 호출한경우, 이때 오픈멤버가 초기화 되지 않은 객체인경우

        • 자바와 함께 사용하는 경우

          • 참조하는 Java 대상 객체가 널인경우

          • 자바 코드가 Generic 타입을 담는 객체에 널은 대입하고 이를 kotlin이 참조하는 경우


      기본적으로 null을 변수에 담을 수 없다.

      • 다음 코드와 같이 변수에 null을 대입하는 경우 컴파일 오류가 난다.

      // 일반적으로 변수에는 널을 할당할 수 없다. 
      var a: String = "abc" 
      a = null // compilation error 가 발생한다. 


      Null을 담을 수 있도록 선언하기

      • 다음과 같이 <타입>? 으로 지정한경우 널을 할당할 수 있다.

      // 다음 변수에는 널을 설정할 수 있다. 
      var b: String? = "abc"
      b = null // 널을 할당할 수 있다. 
      print(b)
      • 샘플을 활용하면 다음과 같다.

      fun main(args: Array<String>) {
      
        var a: String = "Hello1"
        var b: String? = "World"
        b = null
        print(b)
      
        //  다음은 NEP가 발생하지 않고 안전한 코드가 된다. 
        //  즉 val은 한번 값을 할당하면 다시 할당이 불가능하기 때문이다.
        val l = a.length
      
        //  다음은 널이 될 수없어서 오류가 난다. 
        //  01.NullTest.kt:13:13: error: only safe (?.) or non-null asserted (!!.) calls are allowed on a nullable receiver of type String?
        val l2 = b.length
      }


      조건 검사 시에 널을 체크

      • 다음과 같이 b가 널인지 명시적으로 확인하고 두 옵션을 별도로 처리할 수 있다.

      val l = if (b != null) b.length else -1
      • 다음은 널체크를 수행하는 방법을 보여준다.

      val b: String? = "Kotlin"
      if (b != null && b.length > 0) {
          print("String of length ${b.length}")
      } else {
          print("Empty string")
      }
      • 중요한 것중에서 val로 값을 할당하면 널에 대해서 안전하게 처리할 수 있다. (즉, 한번할당하면 값을 재 할당할 수 없으므로 널에 안전하게 처리할 수 있다.)


      널에 대해서 안전하게 호출하는 방법

      • '?.' 을 이용하면 값이 널이라도 안전하게 처리할 수 있다.

      • 이 의미는 널이 아니면 해당 값에 대한 참조를 수행하고, 널인경우라면 결과로 널을 반환한다.

      val a = "Kotlin"
      val b: String? = null
      println(b?.length)
      println(a?.length) // Unnecessary safe call
      • 즉 위 코드는 b가 널인경우 null을 반환한다.

      • 결과는 다음과 같다.

      null
      6
      • 다음과 같이 '?.' 은 체인으로 호출이 가능하다.

      bob?.department?.head?.name
      • 다음과 같이 리스트에 대해서 널에 대해서 안전하게 작업을 수행할 수 있다.

      val listWithNulls: List<String?> = listOf("Kotlin", null)
      for (item in listWithNulls) {
          item?.let { println(it) } // prints Kotlin and ignores null
      }
      • 결과는 다음과 같다.

      Kotlin


      Nullable Receiver 이용하기

      • 확장 함수는 nullable 수신가에서 정의될 수 있다.

      • 이렇게 하면 각 호출 사이트에서 null 검사 논리를 사용할 필요 없이 null에 대한 동작을 수행할 수 있다.

      • toString() 함수는 nullable receiver로 구현되어 있다.

      • 널이 전달되면 이는 "null" 을 출력한다.

      val person: Person? = null
      logger.debug(person.toString()) // Logs "null", does not throw an exception
      • 위와 같이 person.toString() 인경우 person이 널이라도 NEP가 일어나지 않는다.

      • 안전하게 호출한다면 다음과 같이 호출하자.

      var timestamp: Instant? = null
      val isoTimestamp = timestamp?.toString() // Returns a String? object which is `null`
      if (isoTimestamp == null) {
         // Handle the case where timestamp was `null`
      }


      엘비스 연산자 (?:) 이용하기

      • 널이 아닌경우 정상 처리를 수행하고, 널인경우 대안 처리를 하고자 한다면 엘비스 연산자를 사용하자.

      val l: Int = if (b != null) b.length else -1
      • 위 내용을 엘비스 연산자로 구현하면 다음과 같다.

      val l = b?.length ?: -1
      • 대안 작업에 예외도 던질 수 있다.

      fun foo(node: Node): String? {
          val parent = node.getParent() ?: return null
          val name = node.getName() ?: throw IllegalArgumentException("name expected")
          // ...
      }
      • 위와 같이 node.getName()가 널인경우 IllegalArgumentException 이 발생한다.


      '!!' 오퍼레이터

      • '!!' 은 널이아닌지 검사하고, 널이 아닌경우 원래 값을 반환하고, 널인경우 NEP를 발생시킨다.

      val l = b!!.length
      • 만약 NEP를 원한다면 위와 같이 명시적으로 예외를 던질 수 있다.


      안전하게 타입 캐스팅하기

      • 값을 다른값으로 타입 캐스팅을 할때 값이 널이면 ClassCastException이 발생한다.

      • 특정 타입에서 다른 타입으로 안전하게 캐스팅 하기 위해서는 다음과 같이 안전한 타입 캐스팅을 할 수 있다.

      val aInt: Int? = a as? Int
      • 위와 같이 a 값을 안전하게 Int 로 캐스팅이 가능하다.


      컬렉션에서 널이 아닌 값만 추출하기

      val nullableList: List<Int?> = listOf(1, 2, null, 4)
      val intList: List<Int> = nullableList.filterNotNull()
      • 위와 같이 리스트의 값중에 널이 있는경우 filterNotNull() 함수를 이용하면 널이 아닌 값만 추출할 수 있다.

      댓글 0

      DEVOTEE를 활성화 시키면
      지금 작성한 댓글에 AI가 댓글을 달아줍니다.

      KIDO 님의 최신 블로그

      더보기
      동영상 기고하기