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

[Kotlin Tutorial] Kotlin 의 Type system #2

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

 [Kotlin Tutorial] Kotlin 의 Type system #2



참조 : Kotlin in action

!!, 0b, 0x, ?., ?:, any, any?, arithmetic operator, array, array filter, array map, arraylistof, arrayof, arrayofnulls, arrays, as?, asynctask return null, autobox, autoboxing, basic type, binary literal, boolean, boolean type, boolean?, booleanarray, byte, byteArray, Cast, char, character literal, character type, charrray, CLASS, collection, collection interface, collection modify, collection nullable, Collections, collections platform type, Compile, compile time, Compiler, ConcurrentModificationException, conversion, Double, elements nullable, elvis opeator, equals, extension function, F, Filter, filternotnull, float, floating-point number, foreach, foreachindexed, functional lnaguage, Generic, hashcode, hashmapof, hashsetof, hexadecimal, immutable collection, implicit type conversion, iNT, int?, intarray, integer, integer[], Java, java primitive type, java 함수 override, Kotlin, kotlin any, kotlin basics, kotlin tutorial, L, let function, linkedmapof, linkedsetof, list, listof, long, map, mapof, muitablecollection interface, mutable collection, mutable version, mutablelistof, mutablemapof, mutablesetof, non-null, non-null asssertion, not-null assertion, Nothing, nothing type, Notify, NPE, nullability and collections, nullable, nullable primitive types, nullable type, number conversion, number-range overflow, NumberFormatException, object, object array, only one instance, platform type, prefix, primitive array, Primitive type, primitive type array, primitive type conversion, read-only collection, Return, safe call, safe cast, Set, setof, setp, Short, sortedmapof, sortedsetof, spread operator, suffix, super type, thread-safe, toboolean, tobyte, tochar, toint, toIntArray, tointornull, tolong, toshort, toString, totypedarray, type, Unit, unit type, unknown mutability collection, unmodifiable, val var, vararg, void, Wait, wrapper type, wrapping, [Kotlin Tutorial] Kotlin 의 Type system #2, 주의사항, 코틀린, 코틀린 강좌


6.2. Primitive and other basic types


6.2.1. Primitive types: Int, Boolean, and more


-

Kotlin 은 primitive type 과 wrapper type 을 구분하지 않는다.



-

그렇다면 Int 가 object 라면 Kotlin 은 모든 primitive type 을 실제로 object 로 만드는가?

당연히 그렇게 안 했다.

compiler 가 대부분의 Int type 을 Java 의 primitive type 으로 변형시킨다.

generic, collection 등은 원래 Java 의 Integer 형태만 담을 수 있으므로 이 경우는 Int 가 Object 로 남는다.


즉 runtime 관점에서 Java 의 primitive int 가 될 수 있는 녀석은 모두 그 형태라고 보면 된다.



-

Integer types - Byte, Short, Int, Long

Floating-point number types - Float, Double

Character type - Char

Boolean type - Boolean




6.2.2. Nullable primitive types: Int?, Boolean?, and more


-

Kotlin 의 nullable type 은 Java 의 primitive type 이 될 수 없다. Java 에서는 reference 만 null 을 받을 수 있기 때문.

그래서 primitive type 들의 nullable version 을 사용하면 그것들은 wrapper type 으로 compile 된다.



-

Collection 이나 Generic 도 마찬가지로 wrapper 를 사용한다.




6.2.3. Number conversions


-

Kotlin 은 Java 와 달리 implicit type conversion 을 지원하지 않는다.

val myInt = 1

val myLong: Long = myInt // compile error

val.myLong : Long = myInt.toLong()



-

Boolean 을 제외한 모든 primitive type 은 toByte(), toShort(), toChar() 등의 conversion function 이 정의되어 있다.

smaller to larger, larger to smaller 보두 지원한다.



-

val x = 1

val list = listOf(1L, 2L, 3L)

x in list // false

x.toLong() in list // true



-

Long 은 L 을 suffix 로 붙여줘야 한다.

일반적인 floating-point number 는 Double

Float 은 f 를 suffix 로 붙여줘야 한다.

Hexadecimal 은 똑같이 0x 나 0X 를 prefix 로 붙여준다.

binary literal 은 0b 나 0B 를 prefix 로 붙여준다.

character literal 은 java 와 동일



-

val b: Byte = 1

val l = b + 1L


fun foo(l:Long) = println(l)

foo(42) // 42 를 42L 로 치부한다.


이 녀석들은 또 compile error 없이 잘 된다.. ( 왜… 왜 그런거야..? ㅠ )

arithmetic operator 의 number-range overflow는 Java 와 동일하다.



-

String 에 대해서도 toInt, toByte, toBoolean 등의 primitive type conversion 함수들이 제공된다.

치환할 수 없는 경우 Java 와 마찬가지로 NumberFormatException 이 발생한다.

( 1.1 부터는 toIntOrNull 함수가 추가되어 이 녀석을 쓰면 더 좋다 )




6.2.4. “Any” and “Any?”: the root types


-

Java 의 Object 처럼 Any 는 Kotlin 의 nonnull type 의 supertype 이다.

Java 의 Object 는 primitive type 을 못 담는 것에 비해 Kotlin 의 Any 는 primitive type 모두를 담을 수 있다.

val answer: Any = 42 // auto boxing



-

Any 는 null 을 가질 수 없다.

null 까지 담으려면 Any? 를 써야 한다.



-

Java 에서의 param 으로 쓰이는 또는 return 되는 Object 는 Any 로 보인다. (사실 platform type 의 Any)

Any 는 Compile 되면 Java 의 Object 가 된다.



-

Kotlin 의 toString, equals, hashCode 는 Any 에 있는 method 들이다.

Object 에 있는 wait, notify 는 Any 에 정의되어 있지 않다.

쓰려면 Object 로 manual conversion 해서 써야 한다.




6.2.5. The unit type: Kotlin’s “void”


-

Kotlin 의 Unit type 은 Java 의 void 와 같다.

fun f(): Unit { … } // 안 써도 그만~



-

그럼 void 를 안 쓰고 왜 Unit type 이란 걸 만들었냐?

Unit type 은 제대로된 type 의 하나이다.

Generic parameter 를 return 하는 함수를 override 할 때 유용하다.

interface Processor<T> {

    fun process(): T

}


class NoResultProcessor: Processor<Unit> {

    override fun process() {

        ...

    }

}


Java 에서는 이것이(Return 타입이 있고 없고를 이런 문법방식으로 해결하는 것) 어렵기 때문에 다른 interface 를 쓰던가 ( 예를 들면 Callable and Runnable ), 아니면 Void type 을 써야 하는데, Void 를 사용할 경우 return null; 을 무조건 해줘야 한다. ( AsyncTask 생각해보세요 여러분 )



-

그럼 왜 Void 대신 Unit 을 사용하느냐?

보통 기존의 functional language 에서Unit 은 “only one instance” 를 이야기한다.

그리고 Kotlin 에는 “Nothing” 이라는 type 도 있다. 그래서 void 와 헷갈린다.

그래서 Unit 을 사용하기로 결정!




6.2.6. The Nothing type: “This function never returns”


-

test library 에서 exception 등을 던지는, 무조건 fail 하는 function 이라던지,

함수 내부에서 infinite loop 를 돌아서 절대 return 하지 않는 경우가 있다.

이렇게 절대 성공적으로 function 이 끝나지 않는 경우에 Nothing 을 return type 으로 두어 가독성을 주기로 했단다.

fun fail(message: String): Nothing{

    throw IllegalStateException(message)

}


val address = company.address ?: fail(“No address”)

println(address.city)


위의 코드에서 address 는 nullable 로 주지 않아도 된다.

왜냐하면 fail 이 Nothing type 이기 때문에 address 는 nonnull 일 수밖에 없음을 compiler 가 알 수 있기 때문이다.







6.3. Collections and Arrays


6.3.1. Nullability and collections


-

collection 에서도 null 을 element 로 가질 수 있는지 control 할 수 있다.

val result = ArrayList<Int?>()



-

Kotlin 에서는 null 을 수용할 수 있는 collection 에서 nonnull 인것들만 filter 해내는 filterNotNull function 이 있다.




6.3.2. Read-only and mutable collections


-

Kotlin 에서는 Collection 과 Mutable-Collection interface 를 나누어 두었다.

Collection 은 read only feature 만 가지고 있고, MutableCollection 은 Collection 을 extends 하며 modify 속성이 추가되었다.



-

val 과 var 같이 대부분의 경우 Collection interface 를 사용하는 것이 좋고, 필요한 경우에만 MutableCollection 을 사용하는 것이 좋다.



-

fun <T> copyElements(source: Collection<T>, target: MutableCollection<T>){

    ...

}


val source : Collection<Int> = arrayListOf(3, 5, 7)

val target: MutableCollection<Int> = arrayListOf(1)

copyElements(source, target)

copyElements(source, source) // compile error 


실제 arrayListOf 는 MutableCollection 이지만, val 의 type 을 Collection 으로 받으면 Collection type 으로 여겨 compile error 가 난다.



-

read-only collection 으로 지정되었다고 해도, 실제 실체는 MutableCollection 일 수 있기 때문에 ConcurrentModificationException 은 발생할 수 있다.

이 점을 주의해야 한다. ( thread-safe 는 좀 다른 관점으로 봐야 한다. )




6.3.3. Kotlin collections and Java


-

Kotlin collection 과 Java collection 은 완벽히 상호 호환되는 같은 녀석이다.

wrapping 이나 conversion 등이 전혀 필요없다.

그러나 Kotlin 만의 특징이 있다면, read-only 와 mutable 의 두 가지 형태의 interface 로 나뉜다는 데 있다.

( list, set, map 등 모든 collection )





-

Collection Type

 Read-only

 Mutable

 List

 listOf

 mutableListOf, arrayListOf

 Set

 setOf

 mutableSetOf, hashSetOf, linkedSetOf, sortedSetOf

 Map

 mapOf

 mutableMapOf, hashMapOf, linkedMapOf, sortedMapOf


Kotlin 1.0 에서 setOf 나 mapOf 는 실제로는 mutable version 을 return 하고 있지만, 추후 Kotlin 버전에서는 언제 진짜 immutable 이 return 될지 모른다.

(unmodifiable 은 overhead 가 있어 호출하고 있지 않다고 한다.)



-

Kotlin 에서 Collection 을 다루는 Java 코드를 호출할 떄는 read-only 를 넘겨도 modify 를 할 수 있다.

public class CollectionUtils{

    public static List<String> uppercaseAll(List<String> items){

        for (int i=0; i < items.size(); i++){

            items.set(i, items.get(i).toUppercase());

        }

        return items;

    }

}


마찬가지로 nonnull collection 인데도 Java 안에서는 null 을 추가할 수 있다.


그래서 이 부분들에 대해서는 (Java function 에 Collection 을 넘기는 경우) 주의해야 한다.




6.3.4. Collections as platform types


-

Collection 에도 platform type 은 적용된다.

platform type 의 collection 은 기본적으로 unknown mutability collection 이라고 보면 된다.



-

Java 의 함수를 override 할 때는 다음을 고려해야 한다.


1. collection 이 nullable 인가

2. elements 들이 nullable 인가

3. collection 을 modify 할 것인가

/* Java */

interface FileContentProcessor {

    void processContents(File path, byte[] binaryContents, List<String> textContents);

}


interface DataParser<T>{

    void parseData(String input, List<T> output, List<String> errors);

}


/* Kotlin */

class FileIndexer : FileContentProcessor{

    override fun processContents(path:File, binaryContents:ByteArray?, textContents:List<String>?){

        ...

    }

}


class PersonParser : DataParser<Person>{

    override fun parseData(input:String, output:MutableList<Person>, errors:MutableList<String?>){

        ...

    }

}




6.3.5. Arrays of objects and primitive types


-

기본적으로 collection 을 쓰는 것이 array 를 쓰는 것보다 선호되지만, Java API 들에서 아직 array 를 쓰는 곳들이 있다.

그리고 vararg parameter 일 때도 쓰인다.


Kotlin 에서 array 는 type parameter 가 있는 class 이다.



-

다음을 통해 array 들을 만들 수 있다.

arrayOf(1,2,3)

arrayOfNulls(5) // 5개의 element 를 null 로 갖는 array

Array<String>(26) { i -> (‘a’ + i).toString() } // alphabet 을 갖는 array, i 는 index 가 들어온다



-

Collection 을 array 로 전환하려면 toTypedArray 를 호출해주면 된다.

val strings = listOf(“a”, “b”, “c”)

println(“%s/%s/%s”.format(*strings.toTypedArray()))

// * 는 array 를 spread 해준다고 해서 spread operator 이다. vararg parameter 가 들어가는 곳에 array 를 대입할 때 쓰인다.



-

array 에 들어가는 type 들은 모두 object type 이다.

그래서 primitive type 의 array 를 만들려면 특별한 class 를 써야 한다.


IntArray, ByteArray, CharArray, BooleanArray, 등등..

( Kotlin 의 Array<Int> 는 Java 의 Integer[] 에 mapping 된다 )



-

primitive type array 를 만드는 방법은 아래와 같다.

IntArray(5) // 5개의 element 는 default value 로 채워짐

intArrayOf(0,0,0,0,0)

IntArray(5) { i -> (i+1) * (i+1) }



-

Boxing 된 array 는 toIntArray 와 같은 함수로 primitive type array 로 만들 수 있다.



-

array 들도 collection 과 같은 extension function 들을 가지고 있다.

그래서 filter, map, 등을 array 에도 쓸 수 있다.

primitive array 도 이것들이 적용되는데, 주의할 점은 이들의 return 값은 list 이다. ( array 가 아니다 !! )



-

fun main(args: Array<String>){

    args.forEachIndexed { index, element ->

        println(“Argument $index is: $element”)

    }

}





6.4. Summary


-

Kotlin 은 compile time 에 NPE 를 찾기 위해 nullable type 이 지원된다.



-

Kotlin 은 safe call ( ?. ), Elvis operator( ?: ), not-null assertions( !! ), nullable type 을 다루기 위한 let function 등이 지원된다.



-

as? operator 는 cast 에 대해 유연하게 처리할 수 있게 해준다.



-

Java 로부터 오는 Type 은 Kotlin 에서는 platform type 으로 매핑된다.

개발자가 알아서 nullable 혹은 non-null 로 매핑해야 한다.



-

Int 와 같은 녀석들은 일반적인 class 처럼 보이고, 작동하지만 실제 compile 이 되면 대부분 Java primitive type 으로 치환된다.



-

“Any” type 은 Java 의 Object 처럼 모든 것의 supertype 이다.

Unit 은 void 와 비슷한 녀석이다.



-

“Nothing” type 은 정상 종료되지 않는 function (exception 만 던지거나, infinite loop 등) 의 return type 으로 사용될 수 있다.



-

Kotlin 에서는 Java 의 standard collection class 를 그대로 사용하며, 여기서 extension function 으로 기능 강화만 시켰다.

그리고 read-only 와 mutable collection 을 나누어, compile time 의 에러 체크를 강화하였다.



-

Java 의 함수를 override 할 때는 nullability 와 mutability 를 잘 고려해야 한다.

특히나 collection 일 때는 더 복잡하다.



-

Kotlin 의 Array class 는 generic class 로 보인다. 그러나 Java 의 array 로 compile 된다.



-

primitive array type 은 IntArray  등으로 표현된다.





반응형

댓글