ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • kotlin (코틀린) 시작해보기 (1)
    카테고리 없음 2023. 4. 21. 15:27
    필자가 코틀린은 처음 알게 된 계기는 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에 올려놨다. 좀 더 차근차근 알아보면서 포스팅을 해보자.

    댓글

Designed by Tistory.