코틀린은 자바 기반이지만, 단순히 "간결한 문법"을 목적하지 않는다.
언어 자체에 안정성, 생산성, 표현력 강화를 위한 언어적 장치들을 마련해 두었고,
이는 개발자들의 일상적인 고충을 직접 겨냥하고 있다.
구체적으로 알아가보자.
1. 안정성 기능
1. Null Safety
자바 개발자라면 누구나 한 번쯤 NullPointerException을 마주한다.
자바에서는 null 가능성을 코드 레벨에서 강제할 방법이 없어, 결국 런타임에서 버그가 터지곤 한다.
코틀린은 타입 시스템에 null 가능성 자체를 포함한다.
즉, String(null 불가)와 String?(null 허용)을 변수 선언 단계에서부터 구분한다.
즉 변수 선언 단계에서 String / String? 인지 구분하여
"여기서 null이 올 수 있다"라고 사전에 정의한다.
-> 결과적으로 *“여기서 null이 올 수 있다”*라는 사실을 컴파일 타임에 강제한다.
버그를 실행 중 발견하는 게 아니라, 애초에 발생할 가능성을 막아버리는 것.
ex)
fun getLength(str: String?): Int {
// 안전 호출 연산자 (?.)
return str?.length ?: 0 // null이면 0 반환
}
val a: String = "Kotlin"
val b: String? = null
println(getLength(a)) // 출력: 6
println(getLength(b)) // 출력: 0
2. Data Class
객체지향 프로그래밍에서 “데이터 전달용 클래스”는 필수적이지만,
자바에서는 항상 보일러플레이트 코드(게터, 세터, equals, hashCode, toString 등)를 반복 작성해야 했다.
코틀린은 이를 언어 차원에서 표준화했다.
data class 키워드 하나로 필요한 메서드를 자동 생성해 주며, 클래스에는 “데이터”라는 본질만 남는다.
-> 데이터 중심 클래스와 행위 중심 클래스를 명확히 구분하는 방향으로 진화한 것.
ex)
// 선언
data class User(val name: String, val age: Int)
val user1 = User("Alice", 25)
val user2 = User("Alice", 25)
// toString()
println(user1)
// 출력: User(name=Alice, age=25)
// equals()
println(user1 == user2)
// 출력: true
// copy()
val user3 = user1.copy(age = 30)
println(user3)
// 출력: User(name=Alice, age=30)
2. 생산성 기능
1. 확장 함수
자바에서는 기존 클래스에 메서드를 추가할 수 없어, 보통 “유틸 클래스 + static 메서드”로 우회한다.
하지만 이 방식은 객체지향적으로 어색하고 코드도 흩어지게 만든다.
코틀린은 확장 함수(extension function라는 개념을 도입해,
기존 클래스에 “덧입히는” 방식으로 함수를 추가할 수 있게 했다.
-> 개발자가 사용하는 객체 맥락 안으로 로직을 재배치 → 코드 흐름이 훨씬 자연스러워진다.
ex)
// String 클래스 확장
fun String.isValidEmail(): Boolean {
return this.contains("@") && this.contains(".")
}
val email = "test@example.com"
println(email.isValidEmail())
// 출력: true
2. Default Parameter & Named Argument
- Default Parameter: 함수 파라미터에 기본값(default value)을 지정할 수 있음.
- Named Argument: 함수 호출 시 인자의 이름을 명시해서 전달할 수 있음.
자바에서는 파라미터에 기본값을 줄 수 없기 때문에, 다양한 호출 방식을 지원하려면 오버로딩 메서드를 만들어야 함
-> 자바의 오버로딩 남발 문제를 언어 차원에서 해결
ex)
// Java 예시
public class Greeting {
public void sayHello(String name) {
System.out.println("Hello, " + name);
}
public void sayHello(String name, String prefix) {
System.out.println(prefix + " " + name);
}
public void sayHello(String name, String prefix, String suffix) {
System.out.println(prefix + " " + name + " " + suffix);
}
}
// Kotlin 예시
fun sayHello(
name: String,
prefix: String = "Hello",
suffix: String = "!"
) {
println("$prefix $name$suffix")
}
// 호출 방식
sayHello("Tom")
// 출력: Hello Tom!
sayHello("Tom", "Hi")
// 출력: Hi Tom!
sayHello("Tom", "Hi", "^^")
// 출력: Hi Tom^^
// named argument
sayHello(name = "Tom", suffix = "!!!")
// 출력: Hello Tom!!!
sayHello(prefix = "Good morning", name = "Alice")
// 출력: Good morning Alice!
3. 표현력 강화 기능
1. when 표현식
자바의 switch문은 제어문에 불과하다.
코틀린의 when은 한 단계 더 나아가 값을 반환하는 표현식으로 동작한다.
조건 분기를 값 결정으로 연결하고, 범위/타입/조건식까지 활용 가능하다.
-> 조건문이 아닌 조건식으로 제어문과 표현식의 경계를 허묾
ex)
fun describe(obj: Any): String =
when (obj) {
1 -> "숫자 1"
in 2..10 -> "2 이상 10 이하 숫자"
is String -> "문자열"
else -> "알 수 없음"
}
println(describe(1)) // 출력: 숫자 1
println(describe(5)) // 출력: 2 이상 10 이하 숫자
println(describe("Kotlin")) // 출력: 문자열
println(describe(100)) // 출력: 알 수 없음
4. 동시성 기능
1. 코루틴
스레드나 콜백 기반 비동기 처리 방식은 항상 복잡성과 가독성 문제를 낳는다.
코틀린은 코루틴(coroutine을 통해 “중단 가능한 함수”를 언어 차원에서 정의했다.
suspend 키워드를 사용하면 함수 실행을 잠시 멈추고, 이후 다시 이어갈 수 있다.
비동기 로직을 마치 동기 로직처럼 순차적으로 작성할 수 있다는 점이 핵심.
-> 복잡한 비동기 코드를 읽기 좋은 형태로 바꾸고, 안정성 + 생산성을 모두 챙김
ex)
import kotlinx.coroutines.*
fun main() = runBlocking {
launch {
delay(1000L) // 1초 대기 (스레드 블로킹 없이)
println("World!")
}
println("Hello,") // 즉시 실행됨
}
// 출력 순서:
// Hello,
// (1초 후)
// World!
결론:
코틀린 특장점 더 추가할거임 ㅅㄱ
'공부 > 코틀린' 카테고리의 다른 글
| Kotlin vs Java (2) | 2025.08.18 |
|---|