kotlin (코틀린) 시작해보기 (2)

오늘은 코틀린의 기초에 대해 살펴보도록 하자! 필자가 생각하기에 자주 쓰이는 문법들을 살펴보겠다. 이 외에 문법들은 코틀린 공식문서를 살펴보는게 좋을 듯 싶다.

기본적인 number들은 자바와 bit 길이가 같다. 그러므로 생략한다.

명시적 변환

자바와 달리 코틀린은 자동 형변환을 지원하지 않는 듯하다.
예를들어 보자.

int i = 10;
long l = i;
System.out.println(l);

위의 코드는 자바의 자동 형변환 코드이다. 작은 범위의 타입은 큰 범위 타입으로 자동 형 변환이 가능하다. 이건 자바 기초이므로 다 알거라고 생각한다. 하지만 코틀린의 경우 다르다.

val i :Int = 10
val l : Long = i //컴파일 에러
println(l)

위는 코드는 아무 이상 없어 보이지만 컴파일 에러가 발생한다. 이유는 코틀린의 Int 는 자바의 Integer로 오토박싱 된다. 그러므로 자동 형변환은 지원되지 않는다.
하지만 명시적 변환은 지원되니 모든 숫자 타입은 아래와 같은 방법으로 변환해주면 된다.
– toByte(): Byte
– toShort(): Short
– toInt(): Int
– toLong(): Long
– toFloat(): Float
– toDouble(): Double
– toChar(): Char

Strings

자바와 동일하게 String타입은 불변의 객체이다. for문은 통해 각각의 문자를 가져올 수 있다.

val str = "hello"
for(s in str){
    print("$s ")
}
//h e l l o 

String Literals

이거 또한 요즘 나오는 언어에는 대부분 지원하는 듯 하다. 자바는 흠..

val hello = "hello\nworld"
val hello1 =
"""
hello
world
"""

기존의 뉴라인은 \n를 써왔다. 하지만 이제 """ 쌍따움표 세개 안에 String 문자열을 그대로 사용할 수 있다. text를 json으로 만들 때 우리는 다음과 같이 했다.

val json = "{\"key\":\"value\"}"

귀찮게 빽슬래스를 사용해서 key value들을 어사인 할 수 있었지만 이제는 그럴 필요 없다.

val json1 =
"""
{"key":"value"}
"""

위와 같이 하면 \ 빽슬래시를 사용하지 않아도 간단하게 json을 만들 수 있다. 히지만 String을 그대로 출력하므로 에디터에서도 제일 앞부분으로 옮겨야 한다는 단점도 있다. 이 문제를 해결하기 하려면 아래와 같이 하면 된다.

val json1 = """
    |{"key":"value"},
    |{"key1":"value1"}
    """.trimMargin()

앞부분에 |을 사용하고 trimMargin() 메서드를 사용하면 된다.

import

기본적으로는 자바와 import방식은 동일하다.

import java.util.Arrays

몇가지 다른점이 있는데 코틀린에서는 static import가 존재 하지 않는다. 하지만 그에 맞게 적당한 대응책은 존재한다.

import java.util.Arrays.*

위와 같이 asterisk를 사용하여 해당 범위 모든 패키지 클래스 오브젝트에 접근할 수 있다.

asList(1, 2, 3, 4, 5)

또한 import에 alias를 선언할 수 있다.

import kotlin.collections.listOf as of
...
val list = of(1, 2, 3, 4, 5)

흐름 제어

if 문

코틀린에서는 자바와는 다르게 if문은 expression으로 사용할 수 있다. 스칼라도 동일하다.

val a = 10
val b = 20
val max: Int
if (a > b)
    max = a
else
    max = b
println(max)

기존의 우리는 위와 같은 방식으로 사용해 왔다. a 가 b보다 크면 max에다 a를 넣고 아니면 max에다 b를 넣는 그런 코드이다.
하지만 이제부터는 식으로 사용할 수 있다

val max1 = if (a > b) a else b
println(max1)

기존의 코드보다 좀 더 짧은 코드로 작성 할 수 있다. 만약 a를 리턴전에 추가적으로 할일이 있다면 컬리브레이스로 감싸주면 된다.

val max2 = if (a > b) {
    println("a")
    a
} else {
    println("b")
    b
}
println(max2)

위와 같이 컬리브레이스를 사용하면 되고 return 키워드는 사용하지 않아도 된다.

When 문

첫 번째 시간에서도 언급을 했는데 java의 switch 문과 비슷한 문법을 갖고 있으나 더욱 강력하다. 스칼라의 패턴매칭보다는 조금 아쉽지만 그래도 자바보단 낫다.

가장 간단한 예제를 보자.

val text = "hello"

when(text){
    "hello" -> println("text == hello")
    "world" -> println("text == world")
    else -> {
        println("no matching")
    }
}

일반 자바의 switch 문과 문법은 비슷해 보인다. 여기서는 특정 문자에 매칭이 되면 해당되는 문자를 출력한다.

fun whenExpression(any: Any) {
    when (any) {
        in 0..10 -> println("0 ~ 10")
        is String -> println("String type")
        else -> {
            println("no matching")
        }
    }
}

좀더 다양한 방법으로는 in 키워드로 범위를 지정할 수 있으면 is 키워드로 타입을 체크 할 수 있다.

fun whenExpression1(any: Any): Boolean? {
    return when (any) {
        is String -> any.startsWith("when")
        else -> {
            false
        }
    }
}

위와 같이 String 타입을 경우에는 형변환 할 필요 없이 자동으로 형변환을 해준다. java의 switch case문 보다는 좀 더 강력한 기능을 갖고 있다.

마무리

필자는 예전에 스칼라를 조금 공부한적이 있었다. 물론 아주 세밀한건 아니고 그냥 한번 살펴본 정도? 그래도 그것 때문인지 그래도 좀더 이해가 빠르게 되는거 같다. 물론 oop를 오랫동안 한 개발자들은 함수형 언어가 조금 낯설게 느껴질 지도 모른다. 필자도 처음에는 함수형 언어가 조금 어려웠다. 아직도 어렵고 잘 이해 안가는 부분이 아주 많다. 자바 개발자 중 java8을 사용하거나 공부를 했다면 코틀린과 스칼라를 공부해도 될 것 같지만 아직 java8을 사용하지 않거나 잘 모른다면 일단 java8의 람다, stream, 메서드 레퍼런스 등 java8 에서 새롭게 추가된 문법들 부터 공부해보자.

오늘은 이것으로 기본 문법들을 살펴 봤다. 다음 시간에는 코틀린의 클래스에 대해서 알아보자!

kotlin (코틀린) 시작해보기 (1)

필자가 코틀린은 처음 알게 된 계기는 intellij 를 사용하면서 알게 되었다. intellij를 사용한지는 얼마 되지 않았다. (작년 가을쯤?) 그러면서 코틀린이라는 언어를 알게 되었는데 실제 코틀린이 발표된지는 꽤 된 언어이다. 하지만 우리나라에서는 그렇게 인지도가 높은 편은 아니다.

코틀린을 간단하게 소개하자면 intellij를 만든 회사 Jetbrains이 만든 언어로 JVM 위에 올라간다. Jetbrains가 만들어서 intellij에 기능이 빵빵하게 지원해준다. 예를들어 intellij에 자바코드를 코틀린 파일에 복붙하면 알아서 코틀린 문법으로 변환도 해준다. 그리고 또한 java와 호환이 아주 좋다. 필자 생각에는 스칼라보다 더 좋은 듯하다. 물론 스칼라도 좋은 언어이다. 그런데 자바 개발자가 스칼라로 바로 넘어 가는 거 보다는 코틀린을 거쳐서 스칼라로 가는 것도 나쁘지 않다. 자바가 100% 함수형 프로그래밍은 아니지만 java8이 나오면서 어느정도 함수형 프로그래밍을 할 수 있다. 코틀린이 자바 보다는 함수형 프로그래밍이지만 스칼라보다는 조금 부족해 보인다. 그래서 자바 개발자가 바로 스칼라로 배우는 것 보다 조금 더 자바에 가까운 코틀린을 먼저 배우는 것도 좋은 생각인 듯하다.

코틀린언어로 안드로이드 앱을 만들 수도 있고 서버 개발도 할 수 있다. 실제로 Spring에서도 코틀린을 지원하기 시작했다.(필자의 회사 프로젝트 중에 Test코드를 코틀린으로 작업한게 있다. Test코드만 한 이유는 아직 잘 모르고 유지보수때문에 Test 코드에만 작성했다. )

오늘은 간단하게 기본 문법정도만 살펴보고 차후에 차근차근 살펴보도록 하자. 필자도 예전에 한번 살펴본거와 코틀린 공식 문서에 있는 것을 참고 하였다.

함수 정의

fun double(number: Int) : Int {
    return number * 2
}

기본 함수는 위와 같다. 스칼라는 해봤으면 익숙한 문법이다. 위의 코드를 스칼라로 해보면 아래와 같다.

def double(number: Int) : Int = {
  number * 2
}

거의 비슷하다. 코틀린의 경우에는 함수 정의를 fun으로 시작하며 파라미터 경우에는 변수가 앞에 자료형이 뒤에 붙는 형식이다. 그리고 리턴 타입을 정의하면 된다. 또한 세미콜론(;)이 없다. 세미콜론은 옵션이다. 써줘도 무방하다. 스칼라 경우에는 return을 굳이 명시하지 않아도 맨 아래의 코드로 추론을 한다. 간단하게 스칼라와 비교 해봤다. 이제 스칼라는 접어두고 코틀린에 집중하자!

위의 함수를 expression 으로도 만들어 사용할 수 있다.

fun double1(number :Int) = number * 2

expression으로 사용할 경우에는 return타입을 명시해주지 않아도 되며 return이라는 키워드도 쓰지 않아야 된다. 만약 return 키워드를 쓸 경우에는 컴파일 에러가 발생한다.

fun printlnDouble(number: Int): Unit {
    println(number * 2)
}

위의 함수를 보면 Unit이라는 object이 있다. 간단하게 보자면 java의 void와 비슷한 개념이다. 리턴 타입이 없는 그럼 object이다. 물론 위의 코드도 expression으로 사용할 수 있다. 그건 한번씩 해보자.

변수 정의

val one: Int = 1
val one1 = 1    

위와 같이 변수를 정의 할 수 있다. val 라는 키워드를 사용하면 된다. 첫 번째와 같이 정의를 할 때 이것은 어떤 타입이라고 명시 해줘도 되지만 두번째와 같이 명시 해주지 않아도 컴파일러가 타입 추론을 한다. 물론 자바도 타입추론은 아니지만 컴파일러가 알고는 있다. 아래는 자바 코드이다.

String name = 1

위의 코드는 컴파일 에러가 난다. 왜냐하면 name이라는 변수는 String 타입인데 어사인을 int로 했기때문에 에러가 난다. 자바 컴파일러도 알고 있다.

val one: Int = 1
one = 100 //컴파일 에러

위의 코드는 컴파일 에러가 난다. 스칼라와 동일하다. val는 java의 final 이므로 다시 어사인을 할 경우에는 컴파일 에러가 난다. 만약 다시 어사인을 하고 싶다면 var 키워드를 사용하면 된다.

var two = 2
two = 200

위의 코드는 컴파일 에러가 나지 않는다.

String Interpolation

이번에는 String Interpolation 이다. 요즘 나오는 거의 모든 언어에 String Interpolation이 있다. 심지어 자바스크립트(es6)까지에도 있다. 자바는 모하노.

val hello = "hello"
println("${hello} world")

문법은 위와 같다. 위의 경우에는 컬리블레이스를 생략해도 된다. 변수가 한개 뿐이라면 생략가능하다. 아래의 경우에는 컬리브레이스가 꼭 있어야 한다.

val a = 10
val b = 20
println("a + b = ${a + b}")

java와 다르게 String을 조금 편하게 사용할 수 있다. 위의 경우에는 간단한 예제지만 정말 String 변수와 String 문자열을 같이 사용해야 된다면 귀찮은 작업이다. + 를 넣고 빼고 귀찮다.

Elvis Operator

Elvis Operator 는 원래 자바에 맨 처음에 도입 될려고 했다. 말은 c#보다 먼저 나왔다고 하던데.. 그럼 뭐하나 지금 없으면 없는거지. Elvis Operator는 null과 관련 된 문법이다. null을 만든 사람이 자기의 최대 실수가 null을 만들었다고 하는…

fun message(message :String) : String {
    if(message.length < 15){
        return message
    }
    return null //컴파일 에러
}

우리는 흔히 이런 코드를 자주 짠다. 특정 조건에 만족하지 않으면 null을 리턴하곤 한다. 하지만 위의 코드는 컴파일 에러가 난다. 왜냐하면 null을 직접 다루기 때문이다. null을 다루는건 좋지 않으나 어쩔수 없다. 최대한 안쓰는것이 좋긴하지만 오히려 null을 쓰지 않을려다 스트레스 받는다. ㅜㅜ 위의 코드를 정상작동 시키려면 Elvis Operator 를 사용하면된다.

fun message(message :String) : String? {
    if(message.length < 15){
        return message
    }
    return null 
}

리턴타입의 ?(Elvis Operator) 넣어 주면 된다. 왜 Elvis Operator냐 ?(물음표)를 돌려보면 Elvis의 머리와 비슷하다고 그래서 그런다고 하는데.. 비슷한가?

println(message(null))

만약 위와 같이 호출 할 경우에는 위에서 말했듯이 컴파일 에러가 난다. 왜냐하면 null을 썼기 때문이다. 그래서 파리미터에도 Elvis Operator를 넣어 줬다.

fun message(message :String?) : String? {
    if(message.length < 15){
        return message
    }
    return null 
}

위와 같이 했는데도 컴파일 에러가 발생한다. 왜냐하면 message 라는 파라미터에 null이 들어 올 수 있다는 Elvis Operator라는 선언 때문에 message.length 에서 에러가 발생한다. 조금 귀찮을 수는 있지만 그래도 버그가 나오는 것보다는 낫지 않을까?
위의 코드는 아래와 같이 수정 하면 된다.

fun message(message: String?): String? {
    if (message != null && message.length < 15) {
        return message
    }
    return null
}

나중에 좀더 기회가 된다면 더 살펴보도록하자.

자동 캐스팅

fun getStringLength(obj: Any): Int {
    if (obj is String) {
        return obj.length //자동 캐스팅
    }
    return 0
}

위의 코드를 자세히 살펴보자. Any는 java의 Object에 해당하는 그런 object 같다. 스칼라에서도 Any가 있는데.. 거의 비슷하다. 일단 is는 자바의 instanceof 와 비슷한 키워드 인 듯 하다. 하지만 여기서 자바와 다른점은 is로 검사를 하고 굳이 캐스팅을 하지 않아도 컴파일러가 알아서 String이라고 판단을 한다.
((String)obj).length() 자바에서는 원래 이런코드가 맞겠지만 코틀린에서는 is로 검사한 후에 컴파일러가 아 저것은 무조건 String 이겠구나 판단해서 자동으로 형 변환을 해준다. 흠 나쁘지 않다.

for each

val numbers = listOf(1, 2, 3, 4, 5, 6)

for(number in numbers){
    print("$number ")
}

뭐 일반 for each와 비슷하다. in이라는 키워드를 사용하면 된다. es6 에서도 of 였나? 아무튼 자바스크립트에도 있던 걸로 기억한다.

for(i in numbers.indices){
    print("${numbers[i]} ")
}

index를 가져 오고 싶다면 위와 같이 하면 된다. for문 간단하니 이정도만 이야기 해도 될 듯 싶다.

when expression

java의 switch case문 보다는 강력하지만 스칼라의 패턴매칭보다는 조금 아쉬운 그런 expression이다.

fun whenCase(obj: Any) {
    when (obj) {
        1 -> println("One")
        "2" -> println("String two")
        is Long -> println("is long type")
        else -> println("Unknown")
    }
}

위와 같이 자바의 switch case문과 비슷한 문법을 가지고 있다. 강력한 이유는 여러 타입을 마구잡이로 써도 된다는 것이다. java의 경우에는 특정 타입만(그래봤자 int String enum 정도?) 가능하지만 when expression에는 특정 값 특정 타입 등등으로 체크를 할 수 있다.

collections

오늘의 마지막으로 간단하게 collection을 살펴보자.
어느 언어와 마찬가지로 List, Map, Set 등의 자료구조가 있다.
변경가능한 MutableList가 있고 변경할 수 없는 List가 있다. 위에서 봤던 listOf() 메서드는 변경 할 수 없는 List이다. 실제로 add메서드나 set메서드가 존재 하지 않는다.

val mutableList = mutableListOf(1, 2, 3, 4, 5)
mutableList.add(6)

변경 가능한 list를 생성해야 될 경우에는 mutableListOf()를 사용하면 된다. map, set등 비슷한 맥략인 듯 싶다.

자바의 Stream을 할 줄 안다면 아주 쉽게 따라 할 수 있는 그런 collection이 있다.

mutableList
    .filter { number -> number > 3 }
    .map { number -> number * 2 }
    .forEach { number -> print("$number ") }

Stream에서 많이 봤던 filter, map, forEach 등이 코틀린에도 존재한다. 굳이 Stream을 만들 필요도 없고 나중에 List로 반환할 때 toList() 라는 메서드도 호출할 필요가 없다. 스칼라와 많이 닮았다. 컬리 브레이스만 뺀다면 거의 동일하다.

mutableList
        .filter { it > 3 }
        .map { it * 2 }
        .forEach { print("$it ") }

위의 코드를 조금더 간단하게 할 수 있는데 그건 it 이라는 키워드를 사용하면 된다. 이것 또한 스칼라의 _(언더바) 와 비슷해 보인다.

우리는 이렇게 코틀린에 대해서 살짝 알아봤다. 자바 보다는 조금 나아 보인다. 나아 보이긴 하지만 익숙한건 자바라.. 언젠간 쓸날이 오겠지.

간단하게 위의 코드를 gitub에 올려놨다.
좀 더 차근차근 알아보면서 포스팅을 해보자.