코틀린 기초 문법 - 1

용재·2025년 9월 22일
post-thumbnail

코틀린은?

코틀린은 자바의 문법과 비슷합니다.
기본적인 문법. 즉, 작성 방법이 다를 뿐 내부 클래스, 모듈의 동작은 유사하며
자바 <-> 코틀린 간 높은 호환성을 보여줍니다.

서로 다른 언어가 어떻게 높은 호환성을 보여줄까요?
우선 자바는 프로그램을 실행시키기 위해, JVM 위에서 실행될 수 있도록
.class 타입의 실행 파일을 컴파일합니다.

이것과 동일하게 코틀린 또한 JVM 위에서 실행될 수 있도록
.class 타입의 실행 파일을 동일하게 컴파일합니다.

즉, 서로 다른 언어이지만, 같은 .class 실행 파일로 컴파일 되기 때문에,
높은 호환성을 보일 수 있는 것입니다.

변수 선언

fun main() {	
    var num: Int = 0
    num = 123

    val constNum: Int = 7
    // constNum = 123 <- 변경 불가 !!!
}

코틀린에서 변수를 선언하는 방법은 위와 같습니다.
자바와 달리 var 혹은 val로 변수 선언을 시작하고, 변수의 타입은 변수명 뒤에 지정해주면 됩니다.

var은 변경 가능한 변수를 선언하기 위해 사용하고, val은 변경이 불가능한 변수를 선언할 때 사용하게 됩니다.


Nullable 타입 (널 허용)

fun main() {
    val nullableNum: Int? = null // null 괜찮아요 ~~
    
    val num: Int = null // Non-null 에러 발생 ! (컴파일 에러)
}

우리는 코틀린 코드를 읽을 때, 타입 뒤에 ?(물음표)가 붙는 경우를 자주 목격하게 됩니다.

이 문법은 타입을 잘 모르겠을 때 모른다고 표현하는 방법일까요?


코틀린은 그렇게 애매한 자세의 프로그래밍 언어가 아닙니다.
이건 코틀린의 특징이자 자바에 비해 가장 강력한 장점으로 꼽히는 Nullable 문법입니다.

?(물음표)가 붙은 타입의 변수는 null이 들어갈 수 있는 변수이고,
붙지 않은 변수 (Ex. var a: Int)는 null이 허용되지 않는 변수로 사용됩니다.
?를 붙이지 않고 선언한 변수에 null을 할당하려고 하면, 컴파일 에러가 발생하게 됩니다.

우리는 개발하며 수많은 NPE (Null Pointer Exception)에 지난 과오를 반성하며 무수히 많은 null-check 구문을 만들어야 합니다.

하지만 코틀린은 이러한 Nullable 타입을 제공함으로써 변수 선언시부터 null에 대한 허용 여부를 강제로 지정하게 함으로써 초반엔 귀찮고 힘들지만 예기치 못한 NPE를 줄일 수 있도록 도와줍니다.


출력

fun main() {
    print("하이 세상")
    println("하이 세상 줄바꿈") //print("하이 세상 줄바꿈\n")
}

print 문법입니다.
println을 사용하면 자동으로 줄바꿈이 가능합니다.


조건문

fun main() {
    val test: Int = 3

    if (test == 3) {
        print("삼")
    } else {
        print("낫삼")
    }
}

코틀린의 if 조건문은 다른 언어들과 매우 비슷합니다.

fun main() {
    val test: Int = 3

    when (test) {
        1 -> print("1")
        2 -> print("2")
        3 -> print("3")
        else -> print("Else")
    }
}

이건 코틀린에 존재하는 when 조건문 입니다.
자바의 switch 문과 유사하게, case 별 조건을 설정할 수 있습니다.


제가 가장 좋아하는 신기한 점은 when 문을 사용해 바로 변수에 값을 지정할 수 있다는 점 입니다.

fun main() {
    val test: Int = 3

    val num = when (test) {
        1 -> 1
        2 -> 2
        3 -> 3
        else -> 0
    }
}

위처럼 num에 when 조건문을 사용하여 값을 설정할 수 있습니다.


반복문

fun main() {
	// while 반복문
    var i: Int = 0
    while (i < 3) {
        println(i)
        i++
    }
	// for 반복문
    for (j in 0..3) {
        println(j)
    }
}

반복문 입니다. 다른 언어와 크게 다르지 않습니다.
while엔 조건식을 넣어주고, for 문엔 반복 가능한 변수(Range, List)를 넣어주면 됩니다.


함수 선언

fun main() {
    val returnMsg = fun1(1)
    val returnMsg2 = fun2(2)

    println(returnMsg)
    println(returnMsg2)
}

fun fun1(a : Int): String {
    return "return"
}

fun fun2(a : Int): String = "return direct"

코틀린에서 함수를 선언하는 방법은 위와 같습니다.
fun1 함수를 예시로, 파라미터의 타입을 변수 선언하듯 타입을 지정해주고,
함수의 리턴 타입 또한 같은 형식으로 지정해줍니다.

하지만 함수가 매우 짧은 경우엔 굳이 중괄호를 사용하지 않아도 됩니다.
fun2처럼 '=' 기호를 사용하여 return 구문을 생략하고 함수를 매우 간결하게 선언하는 것 또한 가능합니다.

fun main() {
    val returnMsg = fun1(1)
    val returnMsg2 = fun1(2, 3)

    println(returnMsg)
    println(returnMsg2)
}

fun fun1(a : Int): String {
    return "return"
}

fun fun1(a : Int, b : Int): String {
    return  "overloaded function's return"
}

코틀린은 '메서드 오버로딩' 또한 지원합니다.
같은 함수명을 사용하여도 파라미터가 다르다면 다른 동작을 하도록 구현할 수 있습니다.


코틀린에서 Null을 다루는 방법

엘비스 연산자

fun main() {
    var nullableNum: Int? = null

    print(nullableNum ?: "null이라서 등장")
}

위 문법은 특정 변수가 null인 경우 정해진 값을 반환시키기 위해 사용할 수 있습니다.
따라서 위 코드의 출력 결과는 'null이라서 등장' 입니다.


왜 이 문법을 엘비스 연산자라고 부를까요?

이 연산자( ?: ) 가 생긴게 마치 로큰롤의 왕 '엘비스 프레슬리' 의 앞머리가 누워있는 것 처럼 생겼기 때문입니다. (진짜에요)

!! 문법

fun main() {
    var nullableNum: Int? = null
    func1(nullableNum)
}

fun func1(a: Int) {
    print(a)
}

위에서 언급했듯이, 코틀린엔 Nullable 타입과 Null을 허용하지 않는 타입이 있습니다.
만약 내가 갖고 있는 변수는 Nullable 한데, 호출하고자 하는 함수의 파라미터가 Null을 허용하지 않는다면 어떻게 될까요?


코틀린에선 이를 컴파일 에러로 인식하여 빌드가 성공하지 않습니다.
이를 위해 다음의 방법을 시도해볼 수 있습니다.

  1. Nullable 변수에 값을 할당 or null-check
fun main() {
    var nullableNum: Int? = null
    nullableNum = 1
    func1(nullableNum)
    
    nullableNum = null
    
    // null-check
    if (nullableNum != null) {
        func1(nullableNum)
    }
}

fun func1(a: Int) {
    print(a)
}

위처럼 코드를 작성하게 되면, nullable 변수였던 'nullableNum'에 값이 할당 혹은 null이 아님을 검증되어 더이상 nullable 하지 않다고 코틀린에서 자동으로 판단하게 됩니다.

따라서 정상적인 함수 호출이 가능합니다.

하지만 이는 같은 영역에서 선언되고 할당되는 변수의 경우에만 허용되며 영역이 달라지는 경우엔 어디서든 값이 다시 null이 될 수 있기 때문에, 허용되지 않는 방법입니다.

이 영역의 개념은 추후 포스팅에서 다루도록 하겠습니다. 지금은 서로 구분된 파일 정도로만 알고있으면 충분합니다.

  1. !! 문법 사용
fun main() {
    var nullableNum: Int? = null
    func1(nullableNum!!) // <- !! 문법 사용
}

fun func1(a: Int) {
    print(a)
}

본 단락의 제목인 '!! 문법' 입니다.
이 문법은 변수 뒤에 '!!' 을 사용함으로써, 개발자가 이 변수는 null이 아니다! 라고 지정할 때에 사용하게 됩니다.

1번 내용에서 설명했듯, 영역이 달라지는 경우 코틀린은 해당 변수가 null인지 아닌지 확인할 수 없습니다. 따라서 함수를 호출하기 위해 개발자가 강제로 !!를 사용하여 null이 아님을 명시할 수 있습니다.

하지만 이는 null에 대한 처리를 한다기 보단, 강제로 프로그램이 돌아갈 수 있도록 하는 것 이기에 권장되지 않습니다.

실제로 위 코드를 실행해보면, 런타임에서 NPE가 발생하게 됩니다.


이번 포스팅에선 코틀린의 기초 문법 중, class와 연관된 내용은 전부 덜어내고 포스팅을 진행했습니다.
이후 후속 포스팅에선 객체지향의 의미와 코틀린에서의 class 사용에 대하여 포스팅하도록 하겠습니다.

0개의 댓글