소소한 개발이야기

[Kotlin #02] Java 개발자를 위한 Kotlin 기본 문법 — 변수, 함수, 조건문 본문

Kotlin

[Kotlin #02] Java 개발자를 위한 Kotlin 기본 문법 — 변수, 함수, 조건문

앙몬드_ 2026. 10. 29. 22:06

 

지난 글에서는 Kotlin이 왜 탄생했는지, 그리고 Java와 무엇이 다른지 큰 그림을 살펴봤습니다. 이번 글에서는 예고한 대로 변수, 함수, 조건문이라는 가장 기본적인 문법을 Java와 하나씩 비교하며 정리했습니다. 매일 쓰는 문법이라 처음에 확실히 잡아 두면 이후 내용이 훨씬 편해집니다.

목차

  1. 변수 선언: val과 var
  2. Kotlin의 타입 시스템
  3. 함수 선언
  4. 조건문: if와 when
  5. 한눈에 비교하기
  6. 마무리

1. 변수 선언: val과 var

✅ (1) val은 불변, var는 가변

Kotlin의 변수 선언 키워드는 두 가지뿐입니다. 한 번 값을 넣으면 바꿀 수 없는 val과, 언제든 다시 대입할 수 있는 var입니다. Java로 치면 val은 final이 붙은 변수와 같습니다.

Java

final String name = "Kotlin"; // 불변 변수
int count = 0;                // 가변 변수
count = 1;

Kotlin

val name = "Kotlin" // 불변 변수 (Java의 final)
var count = 0       // 가변 변수
count = 1

name = "Java"       // 컴파일 에러! val은 재할당 불가

💡 Kotlin에서는 기본적으로 val을 쓰고, 꼭 필요할 때만 var를 쓰는 것이 관례입니다. 값이 바뀌지 않는다는 것이 보장되면 코드를 읽기도, 디버깅하기도 훨씬 쉬워집니다.

✅ (2) 타입 추론과 명시적 타입

타입은 대입하는 값으로 추론되기 때문에 대부분 생략합니다. 필요하다면 변수 이름 뒤에 : 타입 형태로 직접 적을 수 있습니다. Java와 달리 타입이 이름 뒤에 온다는 점이 처음엔 조금 어색하게 느껴집니다.

Kotlin

val language = "Kotlin"      // String으로 추론
val version: Int = 2         // 타입을 직접 명시
val price: Double            // 선언만 먼저 하고
price = 19.99                // 나중에 한 번만 초기화해도 된다

참고로 Java 10부터 var 키워드로 지역 변수 타입 추론이 가능해졌지만, Kotlin은 지역 변수뿐 아니라 프로퍼티와 함수 반환 타입에서도 추론을 지원합니다.

✅ (3) 상수는 const val

컴파일 시점에 값이 정해지는 상수는 const val로 선언합니다. Java의 static final 상수에 해당합니다.

Java

public static final int MAX_COUNT = 100;

Kotlin

const val MAX_COUNT = 100 // 최상위(top-level)나 object 안에서만 선언 가능

2. Kotlin의 타입 시스템

✅ (1) 원시 타입이 따로 없다

Java는 int와 Integer처럼 원시 타입과 래퍼 클래스가 나뉘어 있습니다. Kotlin에는 이런 구분이 없고 모든 것이 객체입니다. 대신 컴파일러가 가능한 경우 내부적으로 원시 타입으로 바꿔 주기 때문에 성능 걱정은 하지 않아도 됩니다.

Java

int a = 10;
Integer b = 10;              // 박싱된 타입
String s = Integer.toString(a);

Kotlin

val a: Int = 10              // 바이트코드에서는 int로 컴파일
val b: Int? = 10             // nullable이면 Integer로 컴파일
val s = a.toString()         // Int에서 바로 메서드 호출 가능

✅ (2) 자동 형 변환이 없다

Java에서는 int를 long 변수에 그대로 넣을 수 있지만, Kotlin은 숫자 타입 사이의 암묵적 변환을 허용하지 않습니다. 반드시 toLong() 같은 변환 함수를 명시해야 합니다. 처음엔 번거롭지만, 의도치 않은 형 변환으로 생기는 버그를 막아 줍니다.

Java

int i = 10;
long l = i; // 자동으로 확장 변환

Kotlin

val i: Int = 10
val l: Long = i          // 컴파일 에러!
val l2: Long = i.toLong() // 명시적으로 변환해야 한다

✅ (3) 문자열 템플릿

문자열 안에 $변수 또는 ${표현식}을 넣으면 값이 바로 들어갑니다. Java에서 +로 이어 붙이거나 String.format()을 쓰던 코드가 훨씬 간결해집니다.

Java

String msg = "안녕하세요, " + name + "님! 장바구니에 " + items.size() + "개가 있습니다.";

Kotlin

val msg = "안녕하세요, ${name}님! 장바구니에 ${items.size}개가 있습니다."

3. 함수 선언

✅ (1) fun 키워드와 기본 형태

Kotlin의 함수는 fun 키워드로 선언합니다. 변수와 마찬가지로 매개변수 타입과 반환 타입이 뒤에 옵니다. 또한 Java와 달리 클래스 밖에 함수를 바로 선언할 수 있어서, 유틸리티 메서드를 위해 static 클래스를 따로 만들 필요가 없습니다.

Java

public class MathUtils {
    public static int add(int a, int b) {
        return a + b;
    }
}

Kotlin

// 클래스 없이 파일 최상위에 바로 선언 가능
fun add(a: Int, b: Int): Int {
    return a + b
}

✅ (2) 단일 표현식 함수

함수 본문이 한 줄짜리 식이라면 중괄호와 return을 생략하고 =로 바로 연결할 수 있습니다. 이때는 반환 타입도 추론됩니다.

Kotlin

fun add(a: Int, b: Int) = a + b

fun isAdult(age: Int) = age >= 19

✅ (3) 기본값 인자와 이름 붙인 인자

Java에서는 매개변수 조합이 다양하면 오버로딩 메서드를 여러 개 만들어야 했습니다. Kotlin은 매개변수에 기본값을 줄 수 있어서 함수 하나로 해결됩니다. 호출할 때 매개변수 이름을 붙이면 순서와 상관없이 원하는 값만 넘길 수 있습니다.

Java

public void createOrder(String item) {
    createOrder(item, 1, false);
}

public void createOrder(String item, int quantity) {
    createOrder(item, quantity, false);
}

public void createOrder(String item, int quantity, boolean express) {
    // 주문 생성 로직
}

Kotlin

fun createOrder(item: String, quantity: Int = 1, express: Boolean = false) {
    // 주문 생성 로직
}

createOrder("노트북")                          // quantity = 1, express = false
createOrder("노트북", 3)                       // express = false
createOrder("노트북", express = true)          // 이름 붙인 인자로 중간 값 건너뛰기

이름 붙인 인자는 true, false처럼 의미를 알기 어려운 값을 넘길 때 특히 가독성이 좋아집니다.

✅ (4) 반환값이 없을 때는 Unit

Java의 void에 해당하는 타입은 Unit입니다. 반환값이 없는 함수에서는 보통 생략합니다.

Kotlin

fun printLog(message: String): Unit { // ": Unit"은 생략 가능
    println("[LOG] $message")
}

4. 조건문: if와 when

✅ (1) if는 '문'이 아니라 '식'이다

Kotlin의 if는 값을 반환하는 식(expression)입니다. 그래서 Kotlin에는 삼항 연산자(? :)가 없습니다. if-else 자체가 그 역할을 하기 때문입니다.

Java

String grade = score >= 60 ? "합격" : "불합격";

Kotlin

val grade = if (score >= 60) "합격" else "불합격"

블록을 쓰는 경우에는 블록의 마지막 줄이 반환값이 됩니다.

Kotlin

val discount = if (isMember) {
    println("회원 할인 적용")
    0.1 // 이 값이 discount에 대입된다
} else {
    0.0
}

✅ (2) switch를 대체하는 when

Java의 switch는 break를 빠뜨리면 다음 case로 넘어가는(fall-through) 실수가 잦았습니다. Kotlin의 when은 break가 필요 없고, if와 마찬가지로 값을 반환하는 식으로 쓸 수 있습니다.

Java

String status;
switch (code) {
    case 1:
        status = "주문 접수";
        break;
    case 2:
        status = "배송 중";
        break;
    case 3:
    case 4:
        status = "배송 완료";
        break;
    default:
        status = "알 수 없음";
}

Kotlin

val status = when (code) {
    1 -> "주문 접수"
    2 -> "배송 중"
    3, 4 -> "배송 완료"   // 여러 값을 콤마로 묶기
    else -> "알 수 없음"   // 식으로 쓸 때는 else가 필수
}

✅ (3) when의 다양한 조건

when은 단순한 값 비교뿐 아니라 범위(in), 타입(is), 임의의 조건식까지 받을 수 있습니다. 지난 글에서 소개한 스마트 캐스트도 when과 함께 쓰면 특히 깔끔합니다.

Kotlin

// 범위 검사
val level = when (score) {
    in 90..100 -> "A"
    in 80..89 -> "B"
    in 70..79 -> "C"
    else -> "F"
}

// 타입 검사 + 스마트 캐스트
fun describe(obj: Any): String = when (obj) {
    is String -> "문자열, 길이 ${obj.length}" // String으로 자동 캐스팅
    is Int -> "정수, 두 배는 ${obj * 2}"
    else -> "알 수 없는 타입"
}

// 인자 없이 if-else if 체인 대신 사용
val shipping = when {
    weight > 30 -> "화물 배송"
    weight > 5 -> "일반 택배"
    else -> "소형 택배"
}

💡 Java 14부터 도입된 switch 표현식(case 1 -> ...)도 비슷한 방향으로 발전했지만, Kotlin의 when은 범위·타입·임의 조건까지 한 문법 안에서 다룰 수 있다는 점에서 훨씬 유연합니다.


5. 한눈에 비교하기

구분 Java Kotlin
불변 변수 final String s = "a"; val s = "a"
가변 변수 int n = 0; var n = 0
상수 static final int MAX = 10; const val MAX = 10
타입 위치 이름 앞 (int a) 이름 뒤 (a: Int)
원시 타입 int, Integer 구분 구분 없음 (Int)
문자열 결합 "a" + b "a$b"
함수 선언 public int f(int a) fun f(a: Int): Int
반환값 없음 void Unit (생략 가능)
오버로딩 대체 메서드 여러 개 기본값 인자
삼항 연산자 a ? b : c if (a) b else c
분기문 switch + break when (식으로 사용 가능)

6. 마무리

이번 글에서 살펴본 문법들은 하나하나 보면 작은 차이지만, 공통된 방향이 있습니다. 불변을 기본으로 하고, 반복되는 코드를 줄이며, 문(statement)보다 식(expression)으로 표현한다는 것입니다. Java 개발자 입장에서는 타입을 뒤에 쓰는 것과 세미콜론을 생략하는 것만 손에 익으면, 나머지는 금방 자연스럽게 느껴질 겁니다.

다음 글에서는 반복문(for, while)과 컬렉션(List, Set, Map)을 Java와 비교하면서, Kotlin의 범위(.., until, step)와 읽기 전용/변경 가능 컬렉션의 차이를 정리해 보겠습니다.

궁금한 점이나 잘못된 내용이 있다면 댓글로 남겨 주세요. 읽어 주셔서 감사합니다! 😊

Comments