Chapter6. 코틀린 타입 시스템 (1)

김신영·2022년 10월 16일
0

kotlin-in-action

목록 보기
6/11
post-thumbnail

Nullable Type ?

fun strLenSafe(s: String?): Int =  
    s?.length ?: 0  
  
fun main(args: Array<String>) {  
    val x: String? = null  
    println(strLenSafe(x))      // 0
    println(strLenSafe("abc"))  // 3
}

안전한 호출 연산자 ?.

  • null 검사와 Method 호출을 한번에 수행
  • s?.method()
  • if (s != null) s.method() else null
  • 객체가 null이라면 null을 반환
  • 안전한 호출 연산자의 반환타입은 Nullable Type
fun printAllCaps(s: String?) { 
	val allCaps = if (s != null) s.toUpperCase() else null
    val allCaps: String? = s?.toUpperCase()  
    println(allCaps)  
}
fun printAllCaps(s: String?) { 
    val allCaps: String? = s?.toUpperCase()  
    println(allCaps)  
}

엘비스 연산자 ?:

  • elvis operator
  • null 대신 디폴트 값을 설정할 때 사용
  • return 이나 throw 구문 또한 expression이므로, 엘비스 연산자에 사용가능하다.
fun strLenSafe(s: String?): Int = s?.length ?: 0
class Address(val streetAddress: String, val zipCode: Int,  
              val city: String, val country: String)  
  
class Company(val name: String, val address: Address?)  
  
class Person(val name: String, val company: Company?)  
  
fun printShippingLabel(person: Person) {  
    val address = person.company?.address  
      ?: throw IllegalArgumentException("No address")  
    with (address) {  
        println(streetAddress)  
        println("$zipCode $city, $country")  
    }  
}

안전한 캐스트 as?

  • foo as? String
  • if (foo is String) foo as String else null
  • ?: 엘비스 연산자와 함께 많이 사용
class Person(val firstName: String, val lastName: String) {  
   override fun equals(o: Any?): Boolean {  
      val otherPerson = o as? Person ?: return false  
  
      return otherPerson.firstName == firstName &&  
             otherPerson.lastName == lastName  
   }  
}

fun main(args: Array<String>) {  
    val p1 = Person("Dmitry", "Jemerov")  
    val p2 = Person("Dmitry", "Jemerov")  
    println(p1 == p2)        // true
    println(p1.equals(42))   // false
}

명시적 타입 캐스트 as

  • Java와 마찬가지로 is 를 통해 미리 변환 가능한 타입인지 확인 해야한다.
  • 만약 as 로 지정한 타입이 변환 불가능하다면, ClassCastException 이 발생

Not Null Assertion !!

  • 코틀린 컴파일러에게 Nullable Type의 값이 Null이 아님을 알려주는 것
  • 컴파일러에게 "나는 입 값이 null이 아님을 잘 알고 있다. 내가 잘못 생각했다면 예외가 발생해도 감수하겠다." 라고 전달하는 것
  • foo!!
  • if (foo != null) foo else throw NullPointerException()

NOTE:
실제 널에 대해 !! 를 사용하면 NullPointerException 이 발생한다.

!! 를 한줄에 여러번 쓰는 건 피해야 한다.
person.company!!.address!!.country
코드 몇번째 라인에서 발생했는지는 알 수 있으나, 자세히 어떤 값에서 발생했는지는 알 수 없다.

let 함수

  • if (email != null) sendEmailTo(email)
  • email?.let { sendEmailTo(it) }
  • let 함수는 자신의 수신 객체를 인자로 전달받은 람다에게 넘긴다.
  • 만약 널 값이라면 아무 일도 일어 나지 않는다.
val person: Person? = getTheBestPersonInTheWorld()
if (person != null) {
   sendEamilTo(person.email)
}
getTheBestPersonInTheWorld()?.let { sendEmailTo(it.email) }

나중에 초기화할 프로퍼티 lateinit var

  • 나중에 초기화하는 프로퍼티는 항상 var 여야 한다.
  • 프로퍼티 초기화 하기 전에 프로퍼티 접근하면 lateinit property XXXX has not been initialized 라는 예외가 발생한다.
class MyService {  
    fun performAction(): String = "foo"  
}  
  
class MyTest {  
    private lateinit var myService: MyService  
  
    @Before fun setUp() {  
        myService = MyService()  
    }  
  
    @Test fun testAction() {  
        Assert.assertEquals("foo",  
            myService.performAction())  
    }  
}

Nullable Type의 확장 함수/프로퍼티

  • Kotlin의 경우, Nullable Type의 확장함수 안에서는 this가 null이 될 수 있다.
  • Java의 경우, Method안의 this는 그 Method가 호출된 수신 객체를 가리키므로 항상 null이 아니다.
fun String?.isNullorBlank(): Boolean =
	this == null || this.isBlank()

fun main() {
    println("".isNullOrBlank())    // true
    println(null.isNullOrBlank())  // true
}
  • Kotlin에서는 String에 확장함수 isNullOrBlank() 를 제공한다.

Generic Type Parameter의 Nullability

  • Kotlin에서는 함수나 클래스의 모든 타입 파라미터는 기본적으로 널이 될 수 있다.
  • 타입 파라미터 T는 타입 파라미터 이름 끝에 물음표가 없더라도, Nullable Type을 의미한다.
fun <T> printHashCode(t: T) {  
    println(t?.hashCode())  
}  
  
fun main(args: Array<String>) {  
    printHashCode(null)  
}
  • 타입 상한 (upper bound)이 없다면, 컴파일러는 기본적으로 타입 파라미터 TAny? 타입으로 추론한다.
fun <T: Any> printHashCode(t: T) {  
    println(t.hashCode())  
}  
  
fun main(args: Array<String>) {  
    printHashCode(null)  // Compile Erorr
}

// Type parameter bound for T is not satisfied: inferred type Nothing? is not a subtype of Any

Platform Type

  • 플랫폼 타입은 코틀린이 널 관련 정보를 알 수 없는 타입을 말한다.
  • Java에서 @NotNull, @Nullable 과 같이 널 가능성 애노테이션이 없을 경우, 코틀린 컴파일러는 플랫폼 타입으로 인식한다.
  • 컴파일러는 플랫폼 타입에 대해 모든 연산을 허용한다.
  • 따라서 책임은 개발자에게...
JavaKotlin
@Nullable MyClassMyClass?
@NotNull MyClassMyClass
MyClassMyClass? or MyClass
  • 코틀린에서 플랫폼 타입을 선언할 수 없다.
  • 플랫폼 타입은 널이 될 수 있는 타입, 널이 될수 없는 타입으로 모두 사용 가능

Nullable Type, NonNullable Type 정리

  • ?. , ?: , as? 안전한 연산을 위한 안전 연산자
  • !! 안전하지 못한 참조를 위한 연산자 (Not Null Assertion)
  • let 널 안정성 검증한 결과를 널이 될수 없는 타입을 받아들이는 함수에게 전달해주는 라이브러리 함수
  • Nullable Type 확장 함수/프로퍼티
  • 자바 타입은 코틀린에서 플랫폼 타입이다.
profile
Hello velog!

0개의 댓글