Java vs Kotlin — 무엇이 다른가

Kotlin은 자바의 장황함을 덜고 널 안전성을 타입에 새긴 현대적 JVM 언어입니다. 간결한 데이터 클래스, 널 안전성, 확장 함수, 코루틴 같은 핵심 차이를 자바와 나란히 비교하고, 둘 중 무엇을 선택할지 판단 기준을 정리합니다.

· 9 min read · PALDYN Team

지난 글에서 JVM 언어 생태계 전체를 조망했습니다. 그중에서도 자바 개발자가 가장 자주 마주하게 되는 언어가 Kotlin입니다. 안드로이드의 공식 언어이자, 수많은 백엔드 팀이 자바에서 옮겨 가거나 자바와 섞어 쓰는 언어이기 때문입니다. 이번 글에서는 Kotlin을 자바와 나란히 놓고, 무엇이 어떻게 다른지, 그리고 둘 중 무엇을 고를지를 구체적으로 들여다봅니다.

같은 무대, 다른 문법

먼저 분명히 해 둘 것이 있습니다. Kotlin은 자바를 대체하려는 적대적 언어가 아니라, 같은 JVM 무대 위에서 더 편한 문법을 추구하는 언어입니다. 지난 글에서 봤듯 둘 다 같은 바이트코드로 컴파일되고, 같은 라이브러리를 공유합니다. 그래서 비교의 초점은 “어느 쪽이 더 우월한가”가 아니라 “각자 무엇을 더 편하게, 더 안전하게 만들어 주는가”에 있습니다.

간결함 — 데이터 클래스로 보는 차이

Kotlin이 가장 먼저 체감되는 부분은 분량입니다. 자바에서 단순한 데이터 보관용 클래스를 만들려면 필드, 생성자, getter, equals, hashCode, toString을 모두 써야 했습니다. Java 16의 record가 이를 크게 줄였지만, Kotlin은 그보다 먼저 data class로 같은 일을 해 왔습니다.

같은 데이터 클래스, 다른 분량

data class User(val name: String, val age: Int)

val u = User("홍길동", 30)
val older = u.copy(age = 31)   // 일부만 바꾼 복사본
println(u)                     // User(name=홍길동, age=30)

data class 한 줄로 getter, equals/hashCode, toString, 그리고 자바 record에는 없는 copy까지 자동 생성됩니다. 이런 간결함은 데이터 클래스에 그치지 않습니다. 타입 추론(val/var), 세미콜론 생략, 문자열 템플릿("$name") 등 곳곳에서 군더더기를 덜어내, 같은 로직을 더 적은 코드로 표현하게 해 줍니다.

널 안전성 — Kotlin의 결정적 무기

Kotlin이 자바와 가장 근본적으로 다른 지점은 널 안전성 입니다. 자바에서는 모든 참조가 null일 수 있고, 컴파일러는 그것을 막아 주지 않습니다. 그래서 NullPointerException(NPE)은 자바에서 가장 흔한 런타임 오류입니다.

널 안전성 — 컴파일러가 NPE를 막아준다

Kotlin은 타입 시스템 자체에 null 가능성을 새겼습니다. String은 절대 null일 수 없는 타입이고, null이 될 수 있는 값은 String?처럼 물음표를 붙인 별개의 타입입니다. null일 수 있는 값을 안전 확인 없이 사용하려 하면 컴파일 에러 가 납니다.

val name: String? = findName()    // null일 수 있는 타입
val len = name?.length ?: 0       // 안전 호출 + 기본값
// val bad = name.length          // 컴파일 에러! ?. 강제

?.(안전 호출)은 앞이 null이면 전체를 null로 만들고, ?:(엘비스 연산자)는 null일 때 기본값을 줍니다. NPE를 런타임이 아니라 컴파일 단계에서 잡아내는 이 설계는, 자바 개발자가 Kotlin으로 옮길 때 가장 크게 체감하는 안전망입니다.

확장 함수와 함수형 친화성

Kotlin은 기존 클래스에 메서드를 덧붙이는 확장 함수 를 지원합니다. 원본 코드를 건드리지 않고도 마치 그 클래스의 메서드인 것처럼 새 기능을 추가할 수 있습니다.

// String에 새 메서드를 붙이는 것처럼
fun String.isEmail(): Boolean = this.contains("@")

println("a@b.com".isEmail())   // true

또한 함수가 일급 시민이라 람다와 고차 함수를 더 매끄럽게 다루고, let·apply·run 같은 스코프 함수로 객체를 다루는 코드를 간결하게 표현합니다. 자바도 람다와 스트림으로 함수형을 지원하지만, Kotlin은 언어 설계 단계에서 함수형 친화성을 더 깊이 녹였습니다.

코루틴 — 비동기를 동기처럼

비동기 처리에서도 둘은 접근이 다릅니다. 자바가 CompletableFuture와 최근의 가상 스레드로 비동기를 다룬다면, Kotlin은 코루틴(coroutine) 이라는 언어 차원의 기능을 제공합니다. suspend 함수를 통해 비동기 코드를 마치 동기 코드처럼 순차적으로 읽히게 쓰는 것이 특징입니다. 콜백 지옥 없이 비동기 흐름을 직관적으로 표현할 수 있어, 안드로이드와 서버 양쪽에서 널리 쓰입니다.

무엇을 선택할까

그렇다면 자바와 Kotlin 중 무엇을 골라야 할까요? 정답은 상황에 따라 다릅니다. 안드로이드 개발 이라면 구글이 공식 권장하는 Kotlin이 사실상 표준입니다. 간결함과 널 안전성 을 중시하고 새 프로젝트를 시작한다면 Kotlin이 매력적입니다. 반대로 가장 넓은 인력풀, 최대한의 안정성, 풍부한 학습 자료 가 중요하거나 기존 자바 코드베이스가 거대하다면 자바를 유지하는 편이 합리적입니다.

중요한 것은 둘이 양자택일이 아니라는 점입니다. 같은 프로젝트에서 자바와 Kotlin을 섞어 쓸 수 있으므로, 기존 자바 코드를 유지하면서 새 모듈만 Kotlin으로 작성하는 점진적 도입이 얼마든지 가능합니다. 이 상호운용의 실제 작동 방식은 이어지는 글에서 더 자세히 다룹니다.

정리

Kotlin은 같은 JVM 무대 위에서 자바의 장황함을 덜고 안전성을 높인 현대적 언어입니다. data class로 보일러플레이트를 줄이고, 타입에 새긴 널 안전성으로 NPE를 컴파일 단계에서 막으며, 확장 함수·함수형 친화성·코루틴으로 표현력을 더합니다. 자바는 안정성·인력풀·생태계의 성숙함에서 강하고, Kotlin은 간결함·안전성·현대적 문법에서 앞섭니다. 둘은 섞어 쓸 수 있으므로 점진적 도입도 가능합니다. 다음 글에서는 또 다른 강력한 JVM 언어, 함수형의 무게를 깊이 끌어안은 Scala를 자바와 비교합니다.


지난 글: JVM 언어 생태계 개관

다음 글: Java vs Scala — 함수형의 무게


읽어주셔서 감사합니다. 😊