자바는 익숙한데 코틀린은 처음인 개발자를 위한 시리즈다. 편마다 개념 하나만 다루고, 같은 코드를 두 언어로 써서 맞댄다. 무엇이 컴파일되고 무엇이 에러가 나는지, 같은 식이 양쪽에서 무엇을 찍는지가 근거다. 코틀린이 자바와 갈리는 지점은 대개 컴파일러가 대신 해 주는 일이라, 소스만 봐서는 답이 안 나오는 대목이 몇 군데 있다 — 거기서만 결과물을 열어 확인한다(`javap -p` 로 선언 목록을, 디컴파일한 자바 코드로 메서드 안을). 바이트코드 명령어는 읽지 않는다. 앞쪽 편들은 첫 파일, 값, 흐름, 클래스 순서로 기초의 앞쪽 절반을 다루고, 널 안전성·함수·람다·컬렉션으로 이어지는 뒤쪽 절반은 차례로 더해진다.
- 1/26 KB 01 첫 파일 — fun main 한 줄이 JVM 에서 도는 법클래스도 static 도 없는 코틀린 파일 한 줄이 어떻게 자바 실행기 위에서 돌까. 같은 일을 하는 자바 코드를 옆에 놓고, 컴파일러가 몰래 만드는 HelloKt 클래스와 합성 main, 파일 바로 아래 쓴 함수와 값이 static 멤버로 바뀌는 규칙, 자바의 static final 과 코틀린 val 이 갈리는 곳, 그리고 JDK 21 실행기 규칙과 JDK 25 자바의 대조를 따라간다
- 2/26 KB 02 자바에서 부르고 실행하기 — 파일 이름, const, kotlin-stdlib코틀린 톱레벨 함수를 자바에서 부르면 const val 은 호출부에 값으로 복사되고, 파일 이름을 바꾸면 쓰는 쪽이 조용히 깨진다. 표준 라이브러리 jar 없이 돌리면 어떤 코드는 돌고 어떤 코드는 멈추는데, 그 경계를 inline 복사와 널 검사, 실행되는 줄로 가른다
- 3/26 KB 03 첫 파일의 모양 — 세미콜론, import, 패키지와 폴더자바라면 javac 가 여섯 번 거절할 코틀린 파일이 한 번에 컴파일된다. 줄바꿈이 문장을 끊어 생기는 return 함정, import 별칭과 기본 import, 한 파일에 public 클래스 여럿, package 와 다른 폴더를 자바와 나란히 실측해 자바가 강제하는 규칙과 관례를 가른다
- 4/26 KB 04 코틀린에서 자바 부르기 — 게터가 프로퍼티로 보인다자바 클래스의 getName() 을 코틀린에서는 왜 m.name 으로 읽을까. 게터와 세터가 프로퍼티로 보이는 이름 규칙과 그 규칙에서 빠지는 경우, static 멤버 부르기, 코틀린 하드 키워드와 겹치는 메서드 이름을 백틱으로 부르기, try 없이 불리는 체크 예외, 자바가 돌려준 값의 널 문제를 같은 일을 하는 자바 코드와 나란히 놓고 하나씩 짚는다
- 5/26 KB 05 val 과 var — final 과 무엇이 같고 다른가val 로 잡은 리스트에 add 가 되는 까닭은 무엇일까. val 과 var 가 자바 final 과 같은 대입 규칙이면서도 클래스 안에 두면 게터와 세터가 붙는다는 것, 타입 추론이 실패하는 경우, const val 의 조건, val 인데 값이 바뀌는 두 경우를 자바 선언과 나란히 놓고 확인한다
- 6/26 KB 06 숫자 타입 — Int 하나 뒤의 int 와 Integer자바에는 int 와 Integer 가 따로 있는데 코틀린에는 Int 하나뿐이다. 그 Int 가 언제 int 가 되고 언제 Integer 가 되는지, 왜 Int 변수를 Long 에 못 넣고 toLong() 을 손수 불러야 하는지, 리터럴·정수 연산·비트 연산·Char 의 문법이 자바와 어디서 갈리는지를 짚는다
- 7/26 KB 07 문자열 — 템플릿과 원시 문자열자바에서 + 로 잇던 문자열이 코틀린에서는 어떻게 한 줄이 되나. 문자열 템플릿과 $ 를 글자로 쓰는 법, 원시 문자열의 trimIndent 와 trimMargin, == 와 === 가 각각 무엇을 비교하는지, split 에 넘긴 String 이 정규식으로 읽히지 않는 차이, buildString 이 StringBuilder 틀을 줄이는 모습을 자바 코드와 나란히 놓고 돌려 본다
- 8/26 KB 08 문과 식 — if 와 try 가 값을 돌려준다코틀린에는 왜 삼항 연산자가 없을까. if 와 try 와 throw 가 값을 남기는 식이기 때문이다. 문과 식의 차이, else 가 빠질 때의 에러, try 식과 자바 final 변수의 충돌, 타입이 Nothing 인 throw 와 ?: throw 패턴, 그리고 void 를 대신하는 Unit 을 같은 코드의 자바판과 나란히 놓고 가른다
- 9/26 KB 09 when — break 없는 switch코틀린의 when 은 break 도 fall-through 도 없는 switch 다. 여러 값·임의의 식·인자 없는 조건·is 타입 검사·in 범위를 분기 조건으로 쓰는 법, 식으로 쓸 때 else 가 필요하고 enum 이면 생략되는 이유, 실행 시점에 없던 enum 상수가 오면 어떻게 되는지를 자바 switch 와 나란히 실측한다
- 10/26 KB 10 범위와 반복 — for 문에 세미콜론이 없다자바의 for (int i = 0; i < n; i++) 가 없는 코틀린은 무엇으로 반복할까. .., ..<, until, downTo, step 으로 만든 범위와 for in, indices, withIndex 를 자바와 나란히 돌리고, 범위를 바로 쓴 for 가 객체 없는 정수 반복이 되는 것과 변수에 담은 범위가 객체를 만드는 것을 가른다. 끝값이 Int.MAX_VALUE 일 때 자바는 안 끝나고 코틀린은 세 번 돌고 끝나는 까닭, while·do-while 과 라벨 break 도 대조한다
- 11/26 KB 11 동등성 비교 — == 와 === 가 자바와 뒤바뀌었다자바에서 객체를 == 로 비교하면 버그인데, 코틀린에서는 == 가 내용 비교의 기본이다. == 가 equals 를 부르고 === 는 부르지 않는 모습, 널을 먼저 가리는 규칙, Int? 둘을 === 로 비교할 때 127 과 128 이 갈리는 Integer 캐시 함정, 문자열 비교 버그가 사라진 이유, 배열에서만 == 가 참조 비교로 남는 예외를 자바와 나란히 실측해 확인한다
- 12/26 KB 12 클래스와 주생성자 — 한 줄 클래스가 만드는 것class User(val name: String) 한 줄은 클래스 파일에서 무엇이 될까. 주생성자 괄호 안의 val 이 필드·생성자·게터로 바뀌는 규칙, val 없는 인자와 본문 프로퍼티의 초기화 순서, field 를 쓰는 세터와 그 재귀 함정, 기본 인자가 생성자 오버로딩을 대신하는 방식을 같은 일을 하는 자바 문법과 한 줄씩 맞대어 본다
- 13/26 KB 13 init 과 부생성자 — 초기화는 선언 순서대로init 블록과 프로퍼티 초기화 식이 섞인 클래스는 무엇부터 실행할까. 둘이 적힌 순서대로 주생성자 하나에 합쳐지는 모습, 부생성자가 this(...) 로 반드시 주생성자를 거치는 규칙, 주생성자가 없는 클래스에서 init 블록이 생성자마다 복사되는 모습, 초기화 전 프로퍼티를 읽으면 null 과 0 이 보이는 함정을 자바의 인스턴스 초기화 블록·생성자 몸통과 나란히 놓고 문법으로 견준다
- 14/26 KB 14 상속 — 클래스는 기본이 final 이다자바라면 그냥 통과하는 상속 한 줄이 코틀린에서는 왜 컴파일 에러일까. 클래스와 멤버가 기본으로 final 인 규칙과 open, 콜론 문법과 override 필수, 프로퍼티 오버라이드, abstract 클래스, 스프링 프록시를 위한 allopen 플러그인, 생성자에서 open 함수를 부르는 함정을 자바 문법과 나란히 놓고 컴파일러가 어디서 막는지 확인한다
- 15/26 KB 15 가시성 — package-private 이 없다자바에서 수식어를 빼면 패키지 안에 숨던 클래스가 코틀린에서는 어디서나 보이는 public 이 된다. 코틀린의 가시성 넷이 각각 어디까지 열리는지, internal 이 자바에서는 이름이 바뀐 public 으로 불리는 모습, 파일 단위 private 과 같은 패키지에도 닫힌 protected, 세터에만 붙이는 private set 과 private 생성자까지 자바와 나란히 컴파일해 확인한다
- 16/26 KB 16 널 안전성 — String 과 String? 은 다른 타입이다자바에서 실행 중에 터지던 NullPointerException 을 코틀린은 왜 컴파일 때 막나. String 과 String? 이 대입이 한 방향만 되는 다른 타입이라는 것, 자바의 if (x != null) 몇 줄을 한 줄로 줄이는 ?. 와 ?:, 조심해서 써야 하는 !! 와 그것이 터질 때의 메시지, null 검사 뒤에 검사가 사라지는 스마트 캐스트와 var 프로퍼티에서 그것이 안 되는 까닭, 그리고 Int? 가 박싱된다는 성능 대목까지 자바와 나란히 컴파일하고 돌려 본다
- 17/26 KB 17 플랫폼 타입 — 자바에서 온 값은 검사를 건너뛴다널을 막아 준다는 코틀린이 자바 메서드가 돌려준 값에서는 왜 NullPointerException 을 내나. 컴파일러가 붙이는 String! 표시를 에러 문구로 캐내는 법, 자바 쪽 @Nullable·@NotNull 이 무엇을 바꾸는지, 자바가 코틀린 함수에 null 을 넘기면 어디서 멈추는지, 그리고 자바 값을 경계에서 한 번 받아 널 가능 타입으로 좁히는 실무 규칙과 컴파일러가 넣는 널 검사의 비용을 짚는다
- 18/26 KB 18 함수 — 기본인자와 명명인자가 오버로딩을 대신한다자바에서 오버로딩 셋으로 쓰던 메서드를 코틀린은 함수 하나로 쓴다. 함수 선언과 = 한 줄 함수, 기본인자가 부를 때마다 계산된다는 것, 명명인자가 파라미터 이름을 호출부에 박는다는 것, 자바에서 부를 때 필요한 @JvmOverloads, vararg 와 스프레드 * 가 배열을 복사한다는 것까지 컴파일 결과와 실행 출력으로 짚는다
- 19/26 KB 19 확장함수 — 남의 클래스에 메서드를 붙인다고칠 수 없는 String 에 코틀린은 어떻게 메서드를 붙이나. 확장함수를 선언하고 부르는 법이 자바의 static 유틸 메서드와 어떻게 같은지, 그래서 오버라이드가 안 되고 멤버에 지는 함정, 널이어도 부를 수 있는 확장함수, 필드 없는 확장 프로퍼티, 자바에서 부르는 법, 그리고 확장함수가 왜 공짜인지를 실행 출력으로 확인한다
- 20/26 KB 20 예외 — 체크 예외가 없다자바에서 컴파일이 막히는 IOException 코드가 코틀린에서는 왜 그냥 도나. 체크 예외가 없는 코틀린에서 던지고 잡는 문법, try 를 식으로 쓰는 법, try-with-resources 를 대신하는 use, 자바가 코틀린 함수를 부를 때 필요한 @Throws, 인자와 상태를 한 줄로 검사하는 require·check·error, 그리고 예외를 흐름 제어에 쓰면 안 되는 까닭을 자바 코드와 나란히 돌려 본다
- 21/26 KB 21 인터페이스 — 기본 구현과 프로퍼티를 담는다코틀린 인터페이스에는 프로퍼티와 기본 구현이 들어간다. 구현하는 쪽이 override 로 무엇을 채워야 하는지, 인터페이스 프로퍼티가 왜 값을 못 담는지, 두 인터페이스의 기본 구현이 겹칠 때 super<A> 로 고르는 법, 추상 클래스와 어디서 갈리는지를 컴파일 에러와 실행 출력으로 확인한다
- 22/26 KB 22 object 와 companion — static 이 없는 자리static 키워드가 없는 코틀린에서 싱글턴과 클래스 단위 멤버를 무엇으로 만드나. object 선언과 companion object 를 자바에서 부를 때 INSTANCE·Companion 이 끼는 까닭, @JvmStatic·const·@JvmField 로 자바 쪽 호출을 static 처럼 바꾸는 법, object 식과 자바 익명 클래스의 차이, 톱레벨 함수와 object 를 가르는 기준과 스프링 빈을 object 로 만들면 어긋나는 점, object 가 언제 한 번 만들어지는지를 짚는다
- 23/26 KB 23 data class — 한 줄이 만드는 다섯 가지클래스 앞에 data 한 낱말을 붙이면 equals·hashCode·toString·copy·componentN 이 생긴다. 다섯 가지를 출력으로 하나씩 확인하고, 본문 프로퍼티가 빠지는 것·copy 가 얕은 복사인 것·구조분해가 순서로 가는 것·var 키가 HashSet 에서 사라지는 것을 짚는다. 자바 레코드와 무엇이 같고 다른지, hashCode 가 모든 필드를 훑는 비용이 언제 문제인지도 본다
- 24/26 KB 24 sealed 와 enum — 경우를 타입으로 닫는다when 에서 else 를 지웠는데도 컴파일이 되는 타입이 있다. enum 과 sealed 가 경우의 수를 어떻게 닫는지, 하위 타입을 하나 늘렸을 때 어느 줄이 컴파일 에러로 드러나고 else 가 그 검사를 어떻게 꺼 버리는지, 둘 중 무엇을 고를지와 자바 17 sealed·data object 와의 차이, object 로 한 번만 만들기와 values() 대신 entries 를 쓰는 까닭을 실행으로 짚는다
- 25/26 KB 25 람다와 함수 타입 — 함수를 값으로 넘긴다자바에서 stream() 과 Function 인터페이스로 하던 일을 코틀린은 함수 타입과 람다로 한다. 람다 문법과 it, 괄호 밖으로 빼는 마지막 람다, 자바와 달리 var 까지 바꾸는 캡처, 함수 참조, 자바 인터페이스에 람다를 넘기는 SAM 변환과 fun interface, filter·map 이 inline 인 까닭을 쓰는 법 위주로 짚고, 람다가 언제 객체를 만드는지를 === 로 확인한다
- 26/26 KB 26 컬렉션 — List 와 MutableList 는 다른 타입이다listOf 로 만든 리스트에는 왜 add 가 없나. 코틀린이 리스트를 읽기 전용 List 와 변경 가능한 MutableList 두 타입으로 가르는 까닭과, 읽기 전용이 불변은 아니라는 함정, listOf·buildList·mapOf 로 만드는 법, filter·map·groupBy 같은 연산을 자바 스트림과 나란히 쓰는 법, 자바 메서드에 넘겼을 때 벌어지는 일을 실행으로 짚고, 체인이 만드는 중간 리스트와 asSequence 를 언제 고르는지를 원소 개수로 센다