본문 바로가기
프로그래밍 놀이터/Kotlin, Coroutine

[Kotlin Tutorial] Kotlin 의 Type system - Chap6. The Kotlin type system

by 돼지왕 왕돼지 2017. 8. 18.
반응형

 [Kotlin Tutorial] Kotlin 의 Type system - Chap6. The Kotlin type system


출처 : Kotlin in action


!, !!, 3rd lib, ?, ?., ?:, @nonnull, @nullable, android.support.annotation, annotation, as?, compile error, compile process, compile time type, concat, default value, dependency injection framework, elvis operator, Error Message, Extensions, Generic, IDE, java override, java8, java8 optional, javax.annotation, Kotlin, kotlin basics, kotlin tutorial, kotlin 기초 강좌, lateinit, lateinit property, let, let function, line #, method call, nonnull, nonnull and nullable, not-null assertions, NPE, null check, null-coalescing operator, nullability, nullability and java, Nullability of type parameters, nullable, nullable string, nullable type extensions, nullable types, nullable 정보가 없는 type, operator, optional, org.jetbrains.annotations, platform type, precondition, Runtime, runtime error, runtime nullable type, safe call operator, safe cast, stack trace, string?, type parameter nullability, verbose, [Kotlin Tutorial] Kotlin 의 Type system - Chap6. The Kotlin type system, 자유도, 코틀린, 코틀린 강좌


6.1. Nullability


6.1.1. Nullable types


-

Kotlin 은 nullable types 를 지원한다.

nullable type 이라는 것은 어떤 variable 이 null 을 가질 수 있는지를 명시하는 것이다.



-

nullable 하지 않은 곳에 null 을 넣으면 compile error 가 난다.

기본 type 은 nullable 하지 않으며, nullable 을 만드려면 type 뒤에 ? 를 붙여주면 된다.

어떤 타입이든 뒤에 ? 를 붙여줄 수 있다.

fun strLenSafe(s: String?) = …




6.1.2. The meaning of types


-

@Nullable, @NotNull annotation 이 java 에서 지원되고 IDE 가 NPE 를 찾아내는 데 도움이 되지만,

모든 code base 에 이 규칙을 지키는 것은 어려우며( 특히 3rd lib 을 쓰는 경우는 더 ), compile process 에 이 annotation 처리는 처리되지 않는다.

그래서 쉽게 NPE 를 만날 수 있다.



-

Java8 에서는 Optional 이 등장하긴 했지만, 객체를 만들기 때문에 Runtime 에 문제가 될 수도 있고,

verbose 코드들이 증가하며, 관리하는 것이 쉽지 않고, 마찬가지로 3rd lib 과 동작할 때는 여튼 null 처리를 해야 한다.



-

nullable type 은 compile time 에만 valid 하다 ( Runtime 에는 invalid )




6.1.3. Safe call operator: “?.”


-

?. 은 null check 와 method call 을 함께 한다.

// Kotlin

str?.toUpperCase() // 이 녀석의 값 assign 하는 경우에는 해당 변수는 nullable 이어야 한다


// Java

if( s != null )

    s.toUpperCase()




6.1.4. Elvis operator: “?:”


-

null 대신 default value 를 제공하는 operator 가 있으니 이것을 Elvis operator 라고 한다. ( null-coalescing operator 라고도 함 )

fun foo(s: String?){

    val t: String = s ?: “"

}



-

Elvis operator 는 function 의 precondition 을 확인할 때 사용하면 좋다.

fun printShippingLabel(person: Person){

    val address = person.comany?.address ?: throw IllegalArgumentException(“No address”)

    ...

}




6.1.5. Safe casts: “as?”


-

Casting 하려는 type 이 맞으면 casting, 아니면 null 을 assign 한다.

Elvis operator 와 함께 equals 같은 데서 쉽게 사용될 수 있다.

override fun equals(o: Any?): Boolean{

    val otherPerson = o as? Person ?: return false;

}




6.1.6. Not-null assertions: “!!”


-

!! 를 사용하면 non-null type 으로 변경하거나 exception 을 던진다.

fun ignoreNulls(s: String?){

    val sNotNull: String = s!!

    println(sNotNull.length)

}



-

!! 를 쓸 때는 same line 에 concat 해서 쓰는 것은 피해야 한다. stack trace 가 line # 로 나오기 때문이다.

person.company!!.address!!.country // 어떤 녀석이 null 인지 stack trace 로 찾기 어렵다




6.1.7. The “let” function


-

let 은 non-null param 을 받는 function 을 nullable argument 로 call 해야 할 때 사용될 수 있다.

fun sendEmailTo(email: String){ … }


val email: String? = …

sendEmailTo(email) // compile error 가 난다

// if(email != null) 후 호출하면 compile error 를 막을 수 있다.


let 은 호출부를 lambda 안에 넣는다. 이 때 lambda 의 param 은 nonnull 이 보장된다

email?.let { email -> sendEmailTo(email) } // it 으로 받을 수도 있다.

// email?.let { sendEmailTo(it) }

// ? 를 안 붙이고 let 을 호출하면 이 때는 it 이 null 일 수 있다.






6.1.8. Late-initialized properties


-

class MyService{

    fun performAction: String = “foo"

}


class MyTest{

    // android Activity 의 onCreate 나 Test의 setUp 케이스에 실제 null 이 아니게 되는데 constructor 가 아니라 nullable type 으로 지정해야 한다...

    private var myService: MyService? = null 


    @Before fun setUp(){

        myService = MyService()

    }


    @Test fun testAction(){

        Assert.assertEquals(“foo”, myService!!.performAction()) // nullable type 이라 myService 뒤에 !! 가 붙는다

    }

}


lateinit 키워드를 써주면 해결된다.

private lateinit var myService: MyService


lateinit 을 써줬어도 여전히 var 를 써야 하긴 하지만, non-null type 임에도 var 를  constructor 에서 initialize 해주지 않아도 compile error 가 나지 않는다

만약 init 전에 접근하면 “lateinit property myService has not been initialized” 라는 runtime error 가 난다.



-

lateinit 은 dependency injection framework 에 유용하게 쓰인다.




6.1.9. Extensions for nullable types


-

nullable type 에 extension function 을 정의하는 것은 null 을 다루는 아주 좋은 방법이다.

아래와 같이 ?. 와 함께 써주면 String 과 String? 모두에 적용할 수 있는 extension function 이 된다.

fun String?.isNullOrBlank(): Boolean = this == null || this.isBlank()

// 여기서는 nullable type 에 대해서 호출할 수 있으므로 this 는 null 일 수 있어 꼭 체크해주어야 한다



-

extension function 을 정의할 때는 nullable type 에 정의할 것인지 아닌지 고민해봐야 한다.

기본적으로는 nullable type 에 하는 것이 좋다.




6.1.10. Nullability of type parameters


-

기본적으로 모든 type parameter( generic 의 T 같은 녀석들) 는 ? 이 붙지 않아도 nullable 이다.

nullability 에 대한 유일한 예외적인 케이스이다.

fun <T> printHashCode(t: T){ // T 는 Any? 로 추정된다

    println(t?.hashCode())

}



-

type parameter 를 nonnull 로 만드려면 upper bound 를 세팅해줘야 한다.

fun <T: Any> printHashCode(t : T){

    ..

}



-

generic 에 대해서는 나중에 더 세밀하게 배운다.





6.1.11. Nullability and Java


-

Kotlin 에서 Java 코드를 이용할 때는 @nullable, @NonNull annotation 을 참조한다.

그래서 예를 들어 Java 의 @nullable String 의 경우 String? 으로 인식한다.


이 때 nullability annotation 은 여러 가지 version 을 지원한다.

javax.annotation, android.support.annotation, org.jetbrains.annotations



-

annotation 이 없을 때는 Kotlin 의 “platform type” 이라는 것이 된다.

Platform type 이란 nullbility 정보가 없는 type 을 이야기한다.

Platform type 은 Type? 으로도 Type 으로도 작동한다. 즉 호출자에게 모든 것이 달려있다. ( 엄밀히 얘기하면 nullble 이지만... )


여튼 platform type 을 사용할 때에는 NPE 발생 가능성이 높아진다.



-

Kotlin function 에 대해 compiler 는 function call 의 receiver, parameter 에 대해 체크하는 코드를 생성한다.

( 직접 해당 variable 들이 사용될 때가 아니다. )

그래서 해당 param 이 사용될 때까지 잘못된 코드가 수행되는 것을 막아준다.



-

Platform type 을 사용할 때에는 항상 document 를 확인하거나 내부 코드를 보고 nullable 인지 nonnull 인지 확인해야 한다.

전부 nullable 로 만들어 버리면 쓸데없이 null check 를 해야 하는 코드들이 들어갈 수 있다. ( 실제 동작은 nonnull 인데도 )

그래서 Platform type 이란 개념을 만들면서 programmer 에게 자유도를 주기로 했단다.



-

Platform type 은 ! 가 붙는다.

! 는 Error message 에서만 볼 수 있고, 코드에서 이 타입을 직접 쓸 순 없다.

>>> val i : Int = person.name

ERROR: Type mismatch: inferred type is String! but Int was expected


Platoform type 은 아래와 같이 두 가지 형태의 type 에 둘다 대입 가능하다.

>>> val s : String? = person.name

>>> val s : String = person.name



-

Java 의 것을 override 할 때는 nullability 는 어떨까?

/* Java */

interface StringProcessor{

    void process(String value );

}


아래와 같이 nullable 과 nonnull 모두 가능하다

class StringPrinter: StringProcessor{

    override fun process(value: String){

        println(value)

    }

}


class NullableStringPrinter: StringProcessor{

    override fun process(value: String?){

        value?.let{ println(it) }

    }

}





반응형

댓글