최근 수정 시각 : 2024-11-25 23:29:32

Kotlin

코틀린에서 넘어옴

파일:나무위키+유도.png  
코틀린은(는) 여기로 연결됩니다.
이 단어의 다른 뜻에 대한 내용은 코틀린(동음이의어) 문서
번 문단을
부분을
, 에 대한 내용은 문서
번 문단을
번 문단을
부분을
부분을
, 에 대한 내용은 문서
번 문단을
번 문단을
부분을
부분을
, 에 대한 내용은 문서
번 문단을
번 문단을
부분을
부분을
, 에 대한 내용은 문서
번 문단을
번 문단을
부분을
부분을
, 에 대한 내용은 문서
번 문단을
번 문단을
부분을
부분을
, 에 대한 내용은 문서
번 문단을
번 문단을
부분을
부분을
, 에 대한 내용은 문서
번 문단을
번 문단을
부분을
부분을
, 에 대한 내용은 문서
번 문단을
번 문단을
부분을
부분을
, 에 대한 내용은 문서
번 문단을
번 문단을
부분을
부분을
참고하십시오.
프로그래밍 사이트 선정 프로그래밍 언어 순위 목록
{{{#!wiki style="margin: 0 -10px -5px; word-break: keep-all"
{{{#!wiki style="display: inline-table; min-width: 25%; min-height: 2em;"
{{{#!folding [ IEEE Spectrum 2024 ]
{{{#!wiki style="margin: -5px 0"
<rowcolor=#fff> 스펙트럼 부문 상위 10개 프로그래밍 언어 직업 부문 상위 10개 프로그래밍 언어
1 Python 1 SQL
2 Java 2 Python
3 JavaScript 3 Java
4 C++ 4 TypeScript
5 TypeScript 5 SAS
6 SQL 6 JavaScript
7 C# 7 C#
8 Go 8 HTML
9 C 9 Shell
10 HTML 10 C++
}}}
}}}
}}}
[ Stack Overflow 2024 ]
[ TIOBE 2024 ]
||<tablewidth=100%><width=9999><-4><bgcolor=deepskyblue><tablebgcolor=#fff,#222> 2024년 8월 기준 검색어 점유율 상위 20개 프로그래밍 언어 ||
1 Python 11 MATLAB
2 C++ 12 Delphi / Object Pascal
3 C 13 PHP
4 Java 14 Rust
5 C# 15 Ruby
6 JavaScript 16 Swift
7 SQL 17 Assembly language
8 Visual Basic 18 Kotlin
9 Go 19 R
10 Fortran 20 Scratch
{{{#!wiki style="margin: 0 -10px -5px; min-height: calc(1.5em + 5px);"
{{{#!folding [ 21위 ~ 50위 펼치기 · 접기 ]
{{{#!wiki style="margin: -5px -1px -11px"
21 COBOL 36 Scala
22 Classic Visual Basic 37 Transact-SQL
23 LISP 38 PL/SQL
24 Prolog 39 ABAP
25 Perl 40 Solidity
26 (Visual) FoxPro 41 GAMS
27 SAS 42 PowerShell
28 Haskell 43 TypeScript
29 Dart 44 Logo
30 Ada 45 Wolfram
31 D 46 Awk
32 Julia 47 RPG
33 Objective-C 48 ML
34 VBScript 49 Bash
35 Lua 50 Elixir
}}}}}}}}} ||
[ PYPL 2024 ]

}}} ||
프로그래밍 언어 목록 · 분류 · 문법
코틀린
Kotlin
파일:Kotlin_2024.svg 파일:Kotlin_2024.svg
파일:홈페이지 아이콘.svg | 파일:X Corp 아이콘(블랙).svg | 파일:유튜브 아이콘.svg | | 파일:레딧 아이콘.svg

1. 개요2. 문법3. 특징
3.1. 간결한 문법3.2. 안전성3.3. 그 외
4. 통합 개발 환경5. 기타6. 관련 문서7. 외부 링크

[clearfix]
#!syntax kotlin
fun main() {
    println("Hello, World")
}

1. 개요



IntelliJ IDEA의 개발사 JetBrains에서 2011년에 공개한 오픈 소스 프로그래밍 언어. JVM 기반의 언어이며, Java와 유사하지만 더 간결한 문법과 다양한 기능을 추가하였다. Java와의 상호 운용이 100% 지원된다.[상호운용] JVM 바이트코드가 기본이지만, Kotlin/Native 컴파일러를 사용하여 기계어로 컴파일할 수 있다. 안드로이드, 스프링 프레임워크, Tomcat[2], JavaScript, Java EE, HTML5, iOS, 라즈베리 파이 등을 개발할 때 사용할 수 있다.

2017년에 구글 안드로이드의 공식 언어로 Kotlin을 추가하였다. 이는 오라클과의 Java API 저작권 분쟁의 여파로 보는 시각이 많다. 2018년 카카오 카카오톡 메시징 서버에 Kotlin을 사용해 본 결과, 코드량이 비약적으로 감소하고 생산성이 대폭 향상되었다고 한다. # 2019년 1월부터는 구글 내부의 안드로이드 프로젝트에서도 Java 대신 Kotlin을 사용한다. # 안드로이드 공식 문서의 샘플 코드도 Java보다 Kotlin으로 작성된 예제가 먼저 나온다. 예시

이름은 Kotlin 개발 당시 JetBrains의 R&D 센터가 위치했던 상트페테르부르크 근처의 코틀린(Ко́тлин) 섬에서 따왔다. Andrey Breslav(Андрей Бреслав)의 발표 참고 Java가 인도네시아의 섬이라는 것을 생각해보면 다분히 Java를 의식한 듯한 이름.

2. 문법

파일:상세 내용 아이콘.svg   자세한 내용은 Kotlin/문법 문서
번 문단을
부분을
참고하십시오.

3. 특징

  • 장황했던 Java와 비교하면 눈물날 정도로 간결한 문법을 제공한다. 간결한 문법을 제공하면서도 런타임 오버헤드가 거의 없다.[3]
  • 오버헤드 없는 널 안전성을 제공한다. Kotlin의 변수는 Nullable(널 값 사용 가능)과 NotNull(널 값 사용 불가)로 나뉘는데, 변수 선언 시 '?'를 붙여 Nullable로 만들 수 있다. Swift에서도 유사한 기능을 쓰는데 Kotlin 쪽이 제약이 좀 덜해서 사용하기 편하다.
  • 예외 처리를 강제하지 않는다. Java에서는 RuntimeException이나 Error가 아닌 것은 예외 처리가 강제되지만, Kotlin에서는 그런 것 없이 그저 @Throws annotation의 사용이 권장되는 게 전부이다. JSON Object를 만들 때 눈물이 난다.
  • 모든 함수가 리턴값을 가진다. Java에서의 void 메서드는 싱글톤 Unit을 반환하는 메서드로 바뀐다. Java의 Void 클래스와 같이 개체 생성이 불가능한 Nothing은 예외 처리 등 특수한 목적 외로는 사용되지 않는다.
  • Java의 'Integer'나 'Double'처럼 primitive type을 위한 별도의 wrapper class가 존재하지 않으며, primitive type 역시 객체로 취급된다. 따라서 Int 같은 변수는 객체에 할당된 toString() 함수 등을 바로바로 이용할 수 있다.
  • 확장 함수를 지원한다. 이미 final이어서 추가적인 상속이 불가능한 클래스에 새로운 메서드를 추가할 수 있다. 다만 JVM 기준으로는 별개 메서드 호출로 취급되므로, 메서드 및 필드 접근 시에는 해당 클래스에 명시된 접근 수준의 제약을 받는다.
  • Java에서는 메서드를 클래스 등의 밖(Top Level)에서 선언할 수 없지만, Kotlin에서는 가능하다.
  • infix 키워드를 이용해 함수를 연산자 처럼 사용할 수 있다. 확장 함수와 병용하는 것도 가능해 infix fun Int.power() 식으로 선언하여 i = 3 power 4 같은 식으로 호출하는 것도 가능하다. 후술할 operator와는 다소 차이가 있다.
  • Java에서 제공하는 대부분의 연산자들은 아예 operator로 구현되어 있어 이를 통한 축약이 가능하다. 다만 축약이 가능한 연산자는 제약이 있으며, infix와는 엄연히 다르다. 따라서 operator에 없는 연산자는 infix로 따로 정의해야 한다.
    • times() 연산자는 곱셈, plus()는 덧셈에 대응되는 등으로 구현되어 있다. 따라서 a * 3으로도, a.times(3)으로도 쓸 수 있다.
    • ==, != 연산자가 객체의 identity가 아닌 내용물을 비교한다. 정확히 말하자면 Java의 equals() 메서드를 내부에서 호출하는 식. Java에서는 String i와 j가 같은 문자열을 담고 있어도 그 참조가 다르기에 i == jfalse가 될 수 있지만, Kotlin에서는 i.equals(j)를 암묵적으로 호출해 i == jtrue가 된다.
    • Java 처럼 identity equality를 비교하려면 ===, !==를 쓰면 된다. 이 연산자는 Java의 ==, !=와 동일하며, 오버라이딩은 불가능하다.
    • ComparablecompareTo() 메서드는 부등호 연산자로 축약할 수 있다. Java에서 a.compareTo(b) > 0로 사용했다면, Kotlin에서는 a > b를 사용하면 된다. 하지만 ==!=compareTo()아닌 equals()를 호출하기에 단순 숫자 비교 시에는 명시적으로 a.compareTo(b) == 0를 사용해야 한다. 이렇게 비교해야 하는 클래스로는 BigDecimal 등이 있다. [4]
    • Java에서의 비트 연산자는 Kotlin에서 operator로 정의되지 않는다. 따라서 비트 연산자는 모두 함수로 호출해야 한다. |or로, &and로, ~inv()로 명시해야 하며, 여기에 xor 함수가 추가되어 있다. 시프트 연산자인 <<, >>, >>>는 각각 shl, shr, ushr로 바뀐다. 단항 연산자인 inv()를 제외한 나머지는 모두 infix 함수이다.
  • API 문서에 Java의 HTML 대신 Markdown을 사용한다.
  • static 메서드 및 필드가 존재하지 않으며, object를 통해 싱글톤으로 만들어야 한다. 클래스 내부인 경우 companion object을 지정할 수도 있는데, 이 경우는 Kotlin에서 static 메서드 호출하듯이 사용할 수 있다. companion object은 다른 JVM 언어에서 Companion이라는 이름으로 노출되며, 이름을 명시해 바꿀 수도 있다. 다른 클래스에서 상속받거나 인터페이스를 구현하는 것도 가능. 그러나 companion object는 상위 자료형 내에서 1개만 지정할 수 있으며, companion object 내에서는 클래스 등 다른 자료형의 선언이 불가능하다.
    static을 원하는 메서드 및 필드에 @JvmStatic annotation을 지정해 이를 Java에서 사용할 수도 있다. 단, 이 경우는 하위 클래스가 만들어지는 것이 아니라 처음부터 static으로 고정된다. @JvmStatic이 지정된 것은 JVM 언어에서 상속 대상이 아니므로 override가 필요한 경우 등 일부 케이스에서는 사용할 수 없다.
    {{{#!syntax kotlin
// 1. 기본 companion object
// - Java에서는 Companion으로 노출
// - Kotlin에서는 static 메서드 처럼 사용할 수 있으며, 상속이 가능하다.
companion object { /* ... */ }

// 2. 이름이 지정된 companion object
// - Java에서는 지정된 이름으로 노출, 여기서는 Static으로 노출된다.
// - Kotlin에서는 static 메서드 처럼 사용할 수 있으며, 상속이 가능하다.
companion object Static { /* ... */ }

// cf. 이름이 지정된 object
// - Java에서는 지정된 이름으로 노출, 여기서는 Static으로 노출된다.
// - Kotlin에서는 하위 싱글톤 참조로 사용, 상속은 가능하다.
object Static { /* ... */ }

// cf. @JvmStatic annotation 사용
// - Java에서는 static으로 노출, 여기서는 public static int get()으로 노출된다.
// - 상속은 불가능하다.
@JvmStatic fun get(): Int
}}}
  • Java 8에 호환된다.
  • Java와의 상호 운용이 100% 지원된다.[상호운용]
  • Java와 달리 기본 접근자 등 여러 특성이 다르다.
    • 상속이 가능한 Java와는 달리 기본 선언이 상속이 불가능한 final이다. Kotlin에서의 class는 Java에서 final class와 같은 의미. 때문에 부모 클래스로부터 바로 상속을 받을 수는 없고, open 키워드를 명시해야 상속을 할 수 있다. 메서드와 속성 역시 기본이 final이므로 상속을 위해서는 open을 명시해야 한다. Java에서의 class는 Kotlin에서 open class라는 의미. 상속된 메서드 및 속성의 추가 override를 방지하려면 Java와 똑같이 앞에 final을 붙이면 된다. 인터페이스는 애초에 final로 지정할 수 없으므로 기본이 open이다.
    • 모든 자료형, 필드, 메서드 등에 대한 기본 접근자가 public이며, 이는 기본이 Package-private[6]인 Java와 대조된다. Package-private로 만들기 위해서는 internal을 명시해야 한다.
    • Java와는 정반대로, 중첩 클래스는 기본이 static이다. Java와 동일하게 non-static으로 선언할 때에는 inner class로 명시하면 된다.
  • 코루틴을 지원한다. 코루틴 메서드는 suspend라는 키워드로 만들 수 있다. 다만, 이를 직접 호출하기 위해서는 해당 메서드 역시 suspend여야 하며, suspend 없이 호출하기 위해서는 CoroutineScope라는 인터페이스를 경유해야 한다. suspend를 람다 식으로 전달하는 경우에는 해당 호출 함수는 inline으로 처리할 수도 있다.

3.1. 간결한 문법

  • getter와 setter를 배제하고 죄다 속성으로 대체했다. 그냥 쓰지 말라고 IntelIiJ의 코드 제너레이트(Alt+Insert)에서 빼버렸다. Java에서 쓰던 getter와 setter는 Kotlin에서 속성으로 대체된다. 물론 getter/setter를 그대로 쓸 수는 있는데, 구글은 Java에서도 메모리와 속도 면에서 저 둘을 쓰지 말고 그냥 변수를 public으로 하라고 권장해 왔다. [7]
  • 속성으로 만들 때에는 속성으로 만들 내용에 get(), set(value)를 쓰면 된다. val 속성에는 get만 사용할 수 있으며, var 속성에는 둘 다 가능, 나아가 필드에 set을 지정해 접근 범위를 제한시키는 것도 가능하다. 단, 속성에는 반드시 타입을 명시해야 한다. 또한 속성은 getter/setter와 동일하게 on-demand로 참조되므로, 객체 생성 시 호출되는 Initializer(init())와는 차이가 있다. 코드 작성 시 헷갈리기 쉽다.
    {{{#!syntax kotlin
var isDone: Boolean = false // 필드: isDone이라는 필드의 값에 false가 대입된다.

val isDone: Boolean get() = false // 속성: isDone 참조 시 on-demand로 false 반환
}}}
  • 필드에 set을 지정해 접근 범위를 제한시키는 것도 가능하다. 그러나 필드와 동일하거나 좁은 접근 제한을 설정해야 한다. get을 명시하는 것은 가능하지만 필드와 동일한 접근 범위로 설정해야 하기에, 실질적으로 불필요한 코드로 취급된다.
    {{{#!syntax kotlin
var isDone: Boolean = false // 필드: isDone이라는 필드의 값에 false가 대입, get은 public이다
private set // set는 private로 제한

internal var isDone: Boolean = false
internal get // 불필요한 코드, 필드와 getter의 접근 범위가 동일하다
internal set // 불필요한 코드, 필드와 setter의 접근 범위가 동일하다

internal var isDone: Boolean = false
private get // 오류, getter에는 필드와 동일한 접근 범위를 부여해야 한다.
public set // 오류, setter는 필드의 접근 범위를 넘을 수 없다.
}}}
  • 코틀린에서 신규로 추가된 data 클래스를 사용한다면, 클래스의 getter, setter, equals(), hashCode(), toString()copy()한 줄로 선언할 수 있다! Lombok의 @Data annotation과 같은 의미. 변수(var) 및 상수(val)로 지정된 생성자 파라미터는 클래스의 멤버 변수/상수로 동작한다. private 키워드 같이 생성자 접근 수준을 제한하는 키워드를 사용할 수는 있지만, 그렇게 하면 copy() 메서드 때문에 컴파일 경고가 발생한다. copy() 메서드를 통해 일부 값만 복제하거나 equals(), hashCode()를 오버라이드 해 일부 값만 대상으로 삼는 것은 가능하다.
    {{{#!syntax kotlin
data class Customer(var name: String, var email: String, var company: String) // OK

data class Customer private constructor(var name: String, var email: String, var company: String) // 경고
}}}
  • 람다 식을 지원한다. 람다 식이 함수의 마지막 인자인 경우에는 그 블록을 밖으로 빼내어 쓸 수 있다.
  • 싱글톤 방식의 객체를 object로 만들 수 있다. 클래스에서 상속받는 것도 가능하다. open이 불가능하므로 싱글톤에서 추가로 상속하는 것은 불가능. object 키워드는 인터페이스를 구현하거나 abstract, open 클래스에서 바로 상속해 익명 클래스 개체를 생성할 때에도 사용된다.
    {{{#!syntax kotlin
object ThisIsASingleton { // 싱글톤 개체 선언
val companyName: String = "JetBrains"
}

val callback = object : Callback { /* ... */ } // 인터페이스에서 익명 클래스 개체 생성

val adapter = object : Adapter() { /* ... */ } // 클래스에서 상속해 익명 클래스 개체 생성
}}}
  • in과 Range문이 있으며, incontains()를, x .. yx.rangeTo(y)를 암묵적으로 호출한다. 실제로는 ClosedRange라는 인터페이스에 기술되어 있으며, Range 생성은 Char, Int, Long에 한해 확장 함수로 구현되어 있다. Comparable인 객체를 대상으로 하는 것이라면 클래스 및 확장 함수 정의를 통해 만드는 것도 가능하다. in의 부정 연산자인 !in도 존재한다.
    나아가 untilinfix 메서드로 rangeTo()를 호출하며, 이에 따라 i in x .. yx <= i && i <= y를, i in x until yx <= i && i < y와 같은 의미를 갖는다. for문에서도 in과 Range문의 사용이 가능하다.
\1.9 버전부터는 ..< 연산자로 until을 대신할 수 있다. 실제로는 until이 아닌 rangeUntil()을 호출하지만, 그 기능은 같다.
{{{#!syntax kotlin
if (i in 1..10) { // equivalent of 1.rangeTo(10)
println(i)
}

for (i in 0..12 step 3) print("$i ") // 0 3 6 9 12 출력
for (i in 12 downTo 0 step 2) print("$i ") // 12 10 8 6 4 2 0 출력
}}}
  • switch/casewhen->으로 대체되었다. 대상 변수를 지정하지 않으면 그냥 if ~ else if ~ else 체인으로 사용할 수 있고, 대상 변수를 지정하면 in과 Range 등을 사용할 수 있다. 실행 순서는 코드 상에서 나열된 순서를 그대로 따르며, Java에서 쓰던 break문은 when/->에서는 암묵적으로 삽입된다.
    {{{#!syntax kotlin
when (x) {
0, 1 -> print("boolean") // 0과 1이 여기에서 처리됨
2 -> print("콩\n콩") // 2가 여기에서 처리됨
!in 1..9 step 2 -> print("홀수 아님") // 4, 6, 8 처리, 0과 2는 여기에서 처리되지 않음
in 0..9 -> print("한자릿수") // 3, 5, 7, 9 처리, 1은 여기에서 처리되지 않음
else -> print("otherwise") // 나머지 숫자들 처리
}
}}}
  • is 연산자가 있어 타입 체크를 키워드 상으로 할 수 있다. Java에서는 Class 클래스의 isInstance() 메서드에 대응된다. 자동 형변환도 가능하며, when문에 쓸 수도 있다. 부정 연산자인 !is도 존재한다.
    {{{#!syntax kotlin
npcContainer.forEach { npc ->
if (npc is Visible) npc.drawBody(gc, g) // npc가 Visible형으로 자동 변환되었다.
if (npc is Glowing) npc.drawGlow(gc, g) // npc가 Glowing형으로 자동 변환되었다.
}

when (expr) {
is Num -> expr.value // expr이 Num형으로 자동 변환되었다.
is Sum -> eval(expr.left) + eval(expr.right) // expr이 Sum형으로 자동 변환되었다.
else -> throw IllegalArgumentException("퉤에엣")
}
}}}
  • if, when문을 표현식으로 사용할 수 있다. 이 때 마지막 문장이 반환 값이 된다. 이 때 when은 가능한 모든 경우의 수를 커버하거나 else를 명시해 나머지 경우를 처리해야 한다.
    {{{#!syntax kotlin
val max = if (a > b) {
print("Choose a")
a // a라는 값이 if 문에서 반환됨}else {
print("Choose b")
b // b라는 값이 else 문에서 반환됨}
}}}

3.2. 안전성

  • Null 객체를 참조하는 경우를 알아서 보호해준다. Java에서 가져온 클래스 등도 가능하다.
  • Optional Chaining 연산자를 사용하는 경우 변수 안의 메소드나 변수 (ex. length)를 불러올때 변수가 null이면 null을 반환한다. 런타임 NullPointerException의 고통을 덜어주는 고마운 기능이다.
    {{{#!syntax kotlin
return b?.length // b가 null이면 null 리턴
}}}
  • 오류 방지로 null-safe 연산자를 쓸 수 있다. (ex. s as String?) null이 아니면서 변환이 불가능한 경우는 예외가 발생한다.
  • 안전한 형변환 키워드인 as? 가 존재한다. (ex. s as? String) null이거나 변환이 불가능한 경우는 null이 반환된다.
    {{{#!syntax kotlin
/*
null이거나 null은 아니지만 대상 타입으로 형변환될 수 없는 경우와 같은
형변환이 실패하는 모든 경우에 대해 안전하게 null을 반환해준다.
*/
null as? String // null
"string" as? File // null

/*
반면 as는 경우에 따라 kotlin.TypeCastException이나
java.lang.ClassCastException 예외를 발생시킬 수 있다
*/
null as File // kotlin.TypeCastException 예외 발생
null as File? // null
"string" as File // java.lang.ClassCastException 예외 발생
"string" as File? // java.lang.ClassCastException 예외 발생
}}}

3.3. 그 외

  • new 키워드가 없다. 객체 생성 시에는 그냥 Something() 처럼 호출하면 된다. 상술했듯 상속 개체를 직접 생성할 때에는 object 키워드를 사용하면 된다.
  • 세미콜론이 필요없다. 동일한 코드 줄 내에서 문장을 구분하기 위한 용도로 쓰이기에, 넣어도 오류는 나지 않는다.
  • Function parameter가 immutable한 val이다. Java에서는 SonarQube 사용 등에 따라 경고가 발생할 수 있지만, Kotlin에선 무조건 컴파일 오류이다.
  • System.out.println/printf 등이 println/printf 등으로 줄었다. 이 역시 정확하게는 Top-level 함수를 통한 호출이다.
  • String에서 변수의 값을 쉽게 표현할 수 있는 interpolation($)을 지원한다. 범위를 명확하게 구분하거나 인덱싱, 함수 호출 등을 사용할 때에는 {, }로 감싸면 된다. 객체 자체를 넘겨서 암시적으로 toString()을 호출하는 것도 가능하다.
    {{{#!syntax kotlin
val name: String = "Wikineet"
val greet_en: String = "Hello, $name!"
val greet_ko: String = "${name}님 안녕하세요!"
println(greet_en) // 출력: Hello, Wikineet!
println(greet_ko) // 출력: Wikineet님 안녕하세요!
println("이름은 ${name.length}자이네요.") // 출력: 이름은 8자이네요.
println("소문자로는 ${name.lowercase()}이고요.") // 출력: 소문자로는 wikineet이고요.
}}}
  • Nullable이 아닌 변수에 null을 대입할 수 없다. 시도 시 컴파일 오류가 발생한다. Nullable을 위해서는 ?를 붙여야 한다.
    {{{#!syntax kotlin
var a: String = "abc"
a = null // 컴파일 에러
var b: String? = "abc"
b = null // OK
}}}
  • Nothing이라는 특수 클래스가 존재한다. 최상위 클래스인 Any와는 정반대로 Nothing은 최하위 클래스.[8] Nothing에서 객체를 생성하는 것은 당연히 안 되고 제너릭에서도 사용할 수도 없으며, Nullable로 사용 시에는 null을 제외한 어떠한 값도 대입할 수 없다.
  • Elvis 연산자(?:)가 존재하여, null이 나왔을 때 사용할 기본 값을 지정할 수 있다. 기본 값 대신 예외를 throw하는 것도 가능하다.
    {{{#!syntax kotlin
val testee_hometown: String? = null // 여기에 적절한 값을 넣으면 '' 대신 넣은 값이 표시된다.
val hometown_display = testee_hometown ?: "" // testee_hometown이 null인지 검사한다. null이면 연산자의 우변에 있는 값이 대신 대입된다.
println("이 장치는 ${hometown_display}에 사는 모든 사람의 소득 금액을 합친 것보다 더 비쌉니다.")
val notNullRequired = testee_hometown
?: throw IllegalArgumentException() // testee_hometown이 null이면 예외 발생
}}}

4. 통합 개발 환경

JetBrains에서 개발하고 구글이 밀어주는 언어인 만큼 이쪽에 특화된 통합 개발 환경에서 주로 지원된다. 대표적인 것이 IntelliJ IDEA 안드로이드 스튜디오. 이클립스에서도 Kotlin 플러그인을 설치해 사용할 수 있다.

5. 기타

  • 가상머신을 이용하지 않는 모바일 환경이나 임베디드, IoT 환경을 위해 개발된 Kotlin/Native 컴파일러로 기계어 최종 컴파일을 할 수 있다. LLVM에 기반하고 있으며, 참조 횟수 카운팅 방식의 가비지 컬렉션을 사용한다. iOS Raspberry Pi 개발도 가능하다.
    Kotlin Native는 다음 플랫폼들을 지원한다. 출처
    • macOS
    • iOS, tvOS, watchOS
    • Linux
    • Windows ( MinGW)
    • Android NDK
  • 이 외에도 자바스크립트로 컴파일되는 Kotlin/JS, WebAssembly를 타겟으로 한 Kotlin Wasm, 멀티플랫폼 개발이 가능한 Kotlin/Multiplatform(Kotlin/Multiplatform Mobile) 등의 프로젝트도 존재한다. 다만 후자의 경우 UI 중심인 React Native Flutter과는 달리 비즈니스 로직을 공유하는 데 초점을 맞추고 있다.
  • 최신 언어에서 개발된 언어 디자인 컨셉들을 많이 활용했지만, 이해하기 힘들거나 사용빈도가 떨어지는 기능들을 제거해 언어 자체를 단순하게 만들고 툴링을 강화한 언어라는 평가를 받는다. 예를 들면, 본문에 소개된 Kotlin의 ?(null 안전성)를 제외한 모든 기능은 Scala에 이미 존재했던 기능들이고, 반대로 Scala에 존재하지만 Kotlin에 존재하지 않는 기능은 정말 많다.[9]
  • 스택 오버플로우를 검색해보면 Kotlin 질문들은 거의 없는 것을 볼 수 있는데, Java와 Kotlin은 서로 완벽하게 호환되기 때문에 답이 안 나온다면 Java로 질문된 문제를 참고하면 된다.
  • Kotlin과 IntelliJ IDEA를 만든 JetBrains에서 제작한 코틀린 전용 웹 프레임워크 Ktor가 존재한다. ORM으로는 Exposed[10], Ktorm가 있다.
  • Java와 상호 호환되는 언어인만큼 JetBrains의 Java, Kotlin IDE인 IntelliJ IDEA에 Java 코드를 Kotlin 코드로 바꿔주는 기능이 존재한다.[11]
  • 구글에서 안드로이드 앱 개발 공식 언어로 채택한 덕에 안드로이드 앱 개발에만 쓰이는 언어로 이미지가 굳어졌지만, 위에서 언급했듯 Java와 상호 호환이 되기에 기존에 Java로 구축한 프로젝트를 Kotlin으로 이전하거나, 일부 모듈에만 Kotlin을 사용하는 식으로 혼용이 가능하다. Java를 사용하는 대부분의 프로젝트( Spring을 사용하는 경우 등)는 Kotlin으로도 충분히 가능하다는 이야기이다. 코루틴 등 코틀린의 장점을 차용해야 하는 프로젝트에서 먼저 많이 사용되었고 IT 서비스업 위주로 신규 프로젝트 시작 시 자바에 비해 간결한 코틀린으로 옮겨가는 추세이다.
  • 문법을 보면 Scala 2.x와 유사한 것을 확인할 수 있다. 실제 JetBrains에서 밝힌 언어 탄생 비화에 따르면 Scala를 사용하다가 언어 특징인 암묵성 등으로 인해 컴파일 시간이 길어질 수밖에 없고 러닝 커브가 높아지는 등 불편함을 느끼게 되자 Scala 언어 특징에서 암묵성을 빼고 그 자리에 명시성을 넣는 등 자유도보다도 빠른 실사용에 적합하도록 직접 개발한 언어라고 밝힌 적이 있다. 주요 차이점은 다음과 같다.
    • 암묵성을 버리고 명시성을 채택함에 따라 자동 형변환을 지원하거나 타입클래스를 짧은 코드로 사용하는 등의 이점은 포기해야 하지만 다른 사람이 작성한 코드를 이해하기가 쉬워지고 컴파일 속도가 비약적으로 빨라졌다. 또한 Scala에서 암묵성을 이용해 구현되었던 일부 기능은 C#에서와 비슷한 확장 함수 등의 개념을 새로 도입하는 등 Kotlin에서도 비슷하게 사용할 수 있다.
    • Scala는 함수형 패러다임을 따라 null 안정성을 option 형식으로 처리하지만 Kotlin은 C#과 비슷한 null 연산자로 처리한다. 이는 다른 모나드 연산과 통일시키기는 어렵지만 모나드 연산보다 C++, Java 등을 사용해왔던 프로그래머들이 이해하기 쉽고 Java와 호환하여 사용할 때 null 안정성이 크게 증가한다.
    • Scala는 Scala만의 기능을 위해서라면 Java에서 상호운용하기가 거의 불가능함에도 불구하고 기능을 추가하기도 하는데, Kotlin은 Java와의 상호운용성이 담보되지 않으면 기능을 추가하지 않는 경우가 있다.

6. 관련 문서

7. 외부 링크



[상호운용] Kotlin에서 Java 코드를 사용할 때에는 그냥 사용할 수도 있는데다 사용법 역시 Kotlin에 맞춰 할 수 있다. 하지만 Java에서 Kotlin 코드를 사용할 때에는 @Jvm* 계열의 annotation을 사용해야 하는 등의 불편함이 있다. [2] JVM 환경에서도 돌아간다. [3] 이는 Java가 C언어의 객체 시스템이라고 할 수 있는 구조체 라이브러리를 기저기반으로 제작된 언어이기 때문이다. 물론 초기에나 그랬지 지금은 그 시절의 흔적을 크게 찾아볼 순 없지만... 문법에 그 흔적들이 아직 많이 남아있어 장황했던 것. [4] https://stackoverflow.com/questions/63339605/what-is-the-correct-way-to-do-bigdecimal-comparison [상호운용] [6] 같은 패키지 내에서만 접근 가능한 범위 [7] getter/setter 메서드와 public 필드 접근은 JVM이 동일한 바이트코드로 변환해주기 때문에 전혀 성능적인 차이가 없다. 물론, IDE에서 만들어주는 일반적인 getter/setter 가 아닌 추가적인 로직이 들어간 경우는 예외다. [8] '모든 것을 상속하는 객체'의 개념으로 이해하면 되는데, 메서드의 이름과 인자는 동일한데 반환형만 다르게 지정되는 등 충돌하는 구현이 존재할 수 밖에 없다. 당연히 이러한 객체는 이론상으로 존재할 수 없으며, 그러한 이유로 Nothing이 붙은 것이다. [9] ?는 편리한 기능임과 동시에 타입시스템 상의 논리적인 오류이기 때문에 타입 안정성에 극도로 공을 들이는 Scala 같은 언어에서 도입할 가능성은 없다. Scala 2에서 null에 대처하는 일반적인 방법은 단순한 빌트인 타입 중 하나일 뿐인 Option 타입이고, 언어 자체에 null 안정성과 관련된 기능은 없다. 하지만 Scala 3에서는 `-Yexplicit-nulls` 컴파일러 옵션으로 타입 위계를 완전히 바꾸어서 레퍼런스 타입이라도 기본적으로 nullable하지 않게 할 수 있다. 이 경우, Null이 가능하게 하려면 `String|Null` 로 타입을 지정해야 한다. 타입시스템에 논리적 오류를 도입하지 않으면서도 null 안정성을 얻은 연구 성과이다. [10] JetBrains에서 제작했다. [11] 이 기능은 IntelliJ IDEA를 기반으로 만들어진 IDE인 Android Studio에도 존재한다.