2018년 10월 29일
코루틴 릴리스
장기간 및 방대한 테스트를 거쳐 코루틴이 이제 릴리스되었습니다! 이것은 Kotlin 1.3부터 언어 지원과 API가 완전히 안정화되었다는 것을 의미합니다. 새로운 코루틴 개요 페이지를 확인하세요.
Kotlin 1.3에서는 suspend 함수에 대한 callable 참조와 리플렉션 API에서 코루틴 지원이 도입되었습니다.
Kotlin/Native
Kotlin 1.3은 Native 대상을 계속 개선하고 다듬고 있습니다. 자세한 내용은 Kotlin/Native 개요를 참조하십시오.
멀티플랫폼 프로젝트
1.3에서는 공통 코드를 공유하기 쉽도록 표현력과 유연성을 향상시키고 프로젝트를 더 쉽게 구성하기 위해 멀티플랫폼 프로젝트 모델을 완전히 재작성했습니다. 또한, Kotlin/Native이 여러 대상 중 하나로 지원됩니다!
기존 모델과의 주요 차이점은 다음과 같습니다:
- 기존 모델에서는 공통 코드와 플랫폼별 코드를 별도 모듈에 배치하고 expectedBy 종속성으로 연결해야 했습니다. 이제 공통 코드와 플랫폼별 코드는 동일한 모듈의 다른 소스 루트에 배치되어 프로젝트 구성이 더 쉬워졌습니다.
- 이제 다양한 지원 플랫폼에 대한 사전 설정된 플랫폼 구성이 많이 제공됩니다.
- 종속성 구성이 변경되었으며, 종속성은 이제 각 소스 루트마다 별도로 지정됩니다.
- 소스 세트는 임의의 플랫폼 하위 집합 간에 공유할 수 있습니다 (예를 들어 JS, Android 및 iOS를 대상으로 하는 모듈에서 Android 및 iOS 간에만 공유되는 소스 세트를 가질 수 있습니다).
- 멀티플랫폼 라이브러리를 게시하는 것도 지원됩니다.
더 자세한 정보는 멀티플랫폼 프로그래밍 문서를 참조하세요.
계약
Kotlin 컴파일러는 경고를 제공하고 보일러플레이트 코드를 줄이기 위해 정적 분석을 수행합니다. 그 중 가장 주목할만한 기능 중 하나는 스마트 캐스트입니다. 스마트 캐스트는 수행된 유형 확인을 기반으로 자동으로 캐스트를 수행할 수 있는 능력입니다.
fun foo(s: String?) {
if (s != null) s.length // 컴파일러는 's'를 'String'으로 자동 캐스트합니다.
}
그러나 이러한 확인 사항이 별도의 함수로 추출되면 스마트 캐스트가 즉시 사라집니다.
fun String?.isNotNull(): Boolean = this != null
fun foo(s: String?) {
if (s.isNotNull()) s.length // 스마트 캐스트가 적용되지 않습니다. :(
}
이러한 경우의 동작을 개선하기 위해 Kotlin 1.3에서는 계약(Contracts)이라는 실험적인 메커니즘이 도입되었습니다.
계약은 함수가 컴파일러에서 이해할 수 있는 방식으로 명시적으로 자신의 동작을 설명할 수 있게 합니다. 현재 두 가지 유형의 경우를 지원합니다.
- 함수 호출 결과와 전달된 인수 값 사이의 관계를 선언하여 스마트 캐스트 분석을 개선합니다.
fun require(condition: Boolean) {
// 컴파일러에게 다음과 같이 말하는 문법 형태입니다:
// "이 함수가 성공적으로 반환되면 전달된 'condition'은 true입니다"
contract { returns() implies condition }
if (!condition) throw IllegalArgumentException(...)
}
fun foo(s: String?) {
require(s is String)
// 여기서 's'는 'String'으로 스마트 캐스트됩니다. 왜냐하면
// 그렇지 않았다면 'require'가 예외를 throw했을 것이기 때문입니다.
}
고차 함수의 존재에서 변수 초기화 분석을 개선합니다.
fun synchronize(lock: Any?, block: () -> Unit) {
// 컴파일러에게 다음과 같이 말하는 문법 형태입니다:
// "이 함수는 여기에서 'block'을 호출하고, 딱 한 번 호출합니다"
contract { callsInPlace(block, EXACTLY_ONCE) }
}
fun foo() {
val x: Int
synchronize(lock) {
x = 42 // 컴파일러는 'synchronize'에 전달된 람다가 한 번만 호출되므로 재할당을 보고하지 않습니다.
}
println(x) // 컴파일러는 람다가 확실히 호출되며 초기화되므로 'x'를 여기에서 초기화된 것으로 간주합니다.
}
표준 라이브러리의 계약
표준 라이브러리는 이미 계약을 사용하여 위에서 설명한 분석을 개선하고 있습니다. 계약의 이 부분은 안정적이며, 추가로 설정할 필요없이 즉시 개선된 분석을 활용할 수 있습니다.
fun bar(x: String?) {
if (!x.isNullOrEmpty()) {
println("'$x'의 길이는 ${x.length}입니다") // 스마트 캐스트를 통한 널이 아닌 값으로 캐스트됨!
}
}
사용자 지정 계약
사용자 정의 함수에 대한 계약을 선언할 수 있지만, 이 기능은 실험적입니다. 현재의 구문은 초기 프로토 타입 상태이며 미래에 변경될 가능성이 높습니다. 또한 현재 Kotlin 컴파일러는 계약을 확인하지 않으므로 올바른 및 안전한 계약을 작성하는 것은 프로그래머의 책임입니다.
사용자 정의 계약은 contract 표준 라이브러리 함수를 호출하여 도입되며, DSL 스코프를 제공합니다.
fun String?.isNullOrEmpty(): Boolean {
contract {
returns(false) implies (this@isNullOrEmpty != null)
}
return this == null || isEmpty()
}
구문 및 호환성에 대한 자세한 내용은 KEEP 문서에서 확인하세요.
when 문의 대상을 변수로 캡처하기
Kotlin 1.3에서는 when 문의 subject(대상)을 변수로 캡처하는 것이 가능해졌습니다. 다음과 같이 사용할 수 있습니다:
fun Request.getBody() =
when (val response = executeRequest()) {
is Success -> response.body
is HttpError -> throw HttpException(response.status)
}
이전에도 when 바로 이전에 변수를 추출하는 것은 가능했지만, when 내부에서 val을 사용하면 해당 범위가 when의 본문 내로 제대로 제한되므로 네임스페이스 오염을 방지할 수 있습니다. when에 대한 자세한 설명은 여기에서 확인하실 수 있습니다.
@JvmStatic 및 @JvmField 인터페이스 동반 객체의 멤버
Kotlin 1.3에서는 인터페이스의 동반 객체(companion object) 멤버를 @JvmStatic 및 @JvmField 어노테이션으로 표시할 수 있습니다. 클래스 파일에서는 이러한 멤버가 해당 인터페이스로 들어가고 static으로 표시됩니다.
예를 들어, 다음과 같은 Kotlin 코드:
interface Foo {
companion object {
@JvmField
val answer: Int = 42
@JvmStatic
fun sayHello() {
println("Hello, world!")
}
}
}
다음과 같은 Java 코드와 동등합니다:
interface Foo {
public static int answer = 42;
public static void sayHello() {
// ...
}
}
어노테이션 클래스에서 중첩 선언
Kotlin 1.3에서는 어노테이션 내부에 중첩 클래스, 인터페이스, 객체 및 동반 객체를 가질 수 있습니다.
annotation class Foo {
enum class Direction { UP, DOWN, LEFT, RIGHT }
annotation class Bar
companion object {
fun foo(): Int = 42
val bar: Int = 42
}
}
파라미터가 없는 main
일반적으로 Kotlin 프로그램의 진입점은 main(args: Array<String>)과 같은 형식의 함수입니다. 여기서 args는 프로그램에 전달된 커맨드 라인 인수를 나타냅니다. 그러나 모든 애플리케이션이 커맨드 라인 인수를 지원하는 것은 아니므로 이 매개변수는 종종 사용되지 않게 됩니다.
Kotlin 1.3에서는 파라미터가 없는 더 간단한 형태의 main이 도입되었습니다. 이제 Kotlin으로 "Hello, World"를 출력하는 코드가 19글자 짧아졌습니다!
fun main() {
println("Hello, world!")
}
높은 아리티(arity)를 갖는 함수
Kotlin에서 함수형 타입은 다른 수의 매개변수를 사용하는 일반적인 클래스로 표현됩니다: Function0<R>, Function1<P0, R>, Function2<P0, P1, R>, ... 이 방식은 한계가 있으며, 현재로서는 Function22로 끝납니다.
Kotlin 1.3에서는 이 한계를 완화하고 더 큰 아리티를 갖는 함수를 지원합니다:
fun trueEnterpriseComesToKotlin(block: (Any, Any, ... /* 42개 더 */, Any) -> Any) {
block(Any(), Any(), ..., Any())
}
진보적인 모드
Kotlin은 코드의 안정성과 역방향 호환성에 많은 주의를 기울입니다. Kotlin 호환성 정책에 따르면 (예: 코드가 이전에 컴파일되었던 것을 이제는 컴파일할 수 없게 만드는 변경 사항) 파괴적인 변경 사항은 주요 버전 (1.2, 1.3 등)에서만 도입할 수 있습니다.
우리는 많은 사용자가 중요한 컴파일러 버그 수정이 즉시 적용되어 코드를 더 안전하고 올바르게 만들 수 있는 더 빠른 개발 주기를 사용할 수 있다고 생각합니다. 그래서 Kotlin 1.3에서는 진보적인 컴파일러 모드를 도입했습니다. 컴파일러에 -progressive 인수를 전달하여 진보적 모드를 활성화할 수 있습니다.
- 진보적 모드에서 어떤 언어 의미론의 수정 사항은 즉시 적용될 수 있습니다. 이러한 수정 사항은 두 가지 중요한 특징을 갖습니다:
- 이전 컴파일러와 소스 코드의 역방향 호환성을 유지합니다. 즉, 진보적 컴파일러로 컴파일 가능한 모든 코드는 비진보적 컴파일러에서도 잘 컴파일됩니다.
이러한 수정 사항은 어떤 의미에서 코드를 더 안전하게 만듭니다. 예를 들어, 일부 불안정한 스마트 캐스트를 금지할 수 있고, 생성된 코드의 동작을 더 예측 가능하고 안정적으로 변경할 수 있습니다.
진보적 모드를 활성화하면 코드를 다시 작성해야 할 수도 있지만, 이러한 수정 사항은 신중하게 선별되고 검토되며 도구 마이그레이션 지원이 제공됩니다. 진보적 모드는 가장 최신 언어 버전으로 빠르게 업데이트되는 모든 코드베이스에 대한 좋은 선택지가 될 것으로 예상됩니다.
인라인 클래스
인라인 클래스는 아직 알파 단계입니다. 이후에 호환되지 않게 변경될 수 있으며 수동으로 이전 작업이 필요할 수 있습니다. 이에 대한 피드백을 YouTrack에서 환영합니다. 자세한 내용은 참조 문서에서 확인하세요.
Kotlin 1.3에서는 새로운 종류의 선언인 인라인 클래스를 도입했습니다. 인라인 클래스는 일반 클래스의 제한된 버전으로 볼 수 있으며 특히 인라인 클래스는 정확히 하나의 프로퍼티를 가져야 합니다:
inline class Name(val s: String)
Kotlin 컴파일러는 인라인 클래스의 런타임 표현을 공격적으로 최적화하고 가능한 경우 인스턴스를 기본 프로퍼티 값으로 대체하여 생성자 호출, GC 압력 및 기타 최적화를 제거합니다.
fun main() {
// 다음 줄에서는 생성자 호출이 발생하지 않으며
// 런타임에서 'name'은 문자열 "Kotlin"을 포함합니다
val name = Name("Kotlin")
println(name.s)
}
자세한 내용은 인라인 클래스에 대한 참조 문서를 참조하세요.
부호 없는 정수
부호 없는 정수는 베타 단계입니다. 그들의 구현은 거의 안정적이지만 향후 이전 작업이 필요할 수 있습니다. 변경 사항을 최소화하기 위해 노력하겠습니다.
Kotlin 1.3에서는 부호 없는 정수 형식을 도입했습니다.
- kotlin.UByte: 0부터 255까지 범위의 8비트 부호 없는 정수
- kotlin.UShort: 0부터 65535까지 범위의 16비트 부호 없는 정수
- kotlin.UInt: 0부터 2^32 - 1까지 범위의 32비트 부호 없는 정수
- kotlin.ULong: 0부터 2^64 - 1까지 범위의 64비트 부호 없는 정수
부호 있는 형식에 대한 대부분의 기능은 부호 없는 대응 형식에서도 지원됩니다.
// 접미사 리터럴을 사용하여 부호 없는 형식을 정의할 수 있습니다.
val uint = 42u
val ulong = 42uL
val ubyte: UByte = 255u
// 표준 라이브러리 확장을 통해 부호 있는 형식을 부호 없는 형식으로 변환하거나 그 반대로 변환할 수 있습니다.
val int = uint.toInt()
val byte = ubyte.toByte()
val ulong2 = byte.toULong()
// 부호 없는 형식은 비슷한 연산자를 지원합니다.
val x = 20u + 22u
val y = 1u shl 8
val z = "128".toUByte()
val range = 1u..5u
자세한 내용은 참조 문서를 확인하세요.
@JvmDefault
@JvmDefault는 실험 단계입니다. 언제든지 삭제되거나 변경될 수 있습니다. 평가 목적으로만 사용하세요. 이에 대한 피드백을 YouTrack에서 환영합니다.
Kotlin은 Java 6 및 Java 7을 포함한 다양한 Java 버전을 대상으로 하며 인터페이스의 디폴트 메서드가 허용되지 않는 Java 버전도 포함됩니다. 편의를 위해 Kotlin 컴파일러는 이 제한을 우회합니다. 그러나 이 우회 방법은 Java 8에서 도입된 디폴트 메서드와 호환되지 않습니다.
이것은 Java 상호 운용성에 문제가 될 수 있으므로 Kotlin 1.3에서는 @JvmDefault 주석을 도입했습니다. 이 주석이 달린 메서드는 JVM을 위해 디폴트 메서드로 생성됩니다.
interface Foo {
// 'default' 메서드로 생성됩니다.
@JvmDefault
fun foo(): Int = 42
}
경고! API에 @JvmDefault를 추가하는 것은 이진 호환성에 심각한 영향을 미칩니다. 제품에서 @JvmDefault를 사용하기 전에 참조 문서를 주의 깊게 읽어보세요.
표준 라이브러리
멀티플랫폼 랜덤
Kotlin 1.3 이전에는 모든 플랫폼에서 랜덤 숫자를 생성하는 일관된 방법이 없었습니다. JVM에서는 java.util.Random과 같은 플랫폼별 솔루션을 사용해야 했습니다. 이 릴리스에서는 모든 플랫폼에서 사용 가능한 kotlin.random.Random 클래스를 도입하여 이 문제를 해결합니다.
val number = Random.nextInt(42) // number는 [0, limit) 범위의 숫자입니다.
println(number)
isNullOrEmpty 및 orEmpty 확장 함수
isNullOrEmpty 및 orEmpty 확장 함수는 일부 유형에 대해 이미 stdlib에 존재합니다. 첫 번째 함수는 수신자가 null 또는 비어 있으면 true를 반환하고, 두 번째 함수는 수신자가 null이면 빈 인스턴스로 대체됩니다. Kotlin 1.3에서는 이와 유사한 확장 함수를 컬렉션, 맵 및 객체 배열에 대해 제공합니다.
두 개의 기존 배열 간 요소 복사
기존 배열 유형에 대한 array.copyInto(targetArray, targetOffset, startIndex, endIndex) 함수를 사용하면 순수 Kotlin에서 기반 배열 컨테이너를 구현하는 것이 더 쉬워집니다.
val sourceArr = arrayOf("k", "o", "t", "l", "i", "n")
val targetArr = sourceArr.copyInto(arrayOfNulls<String>(6), 3, startIndex = 3, endIndex = 6)
println(targetArr.contentToString())
sourceArr.copyInto(targetArr, startIndex = 0, endIndex = 3)
println(targetArr.contentToString())
associateWith
키 목록이 있고 각 키를 어떤 값과 연결하여 맵을 구축하려는 상황은 상당히 일반적입니다. 이전에는 associate { it to getValue(it) } 함수를 사용하여 수행할 수 있었지만 이제 더 효율적이고 탐색하기 쉬운 대안인 keys.associateWith { getValue(it) }를 도입합니다.
val keys = 'a'..'f'
val map = keys.associateWith { it.toString().repeat(5).capitalize() }
map.forEach { println(it) }
ifEmpty 및 ifBlank 함수
컬렉션, 맵, 객체 배열, 문자 시퀀스 및 시퀀스에는 이제 비어 있을 때 수신자 대신 사용할 대체 값을 지정할 수 있는 ifEmpty 함수가 있습니다.
fun printAllUppercase(data: List<String>) {
val result = data
.filter { it.all { c -> c.isUpperCase() } }
.ifEmpty { listOf("<no uppercase>") }
result.forEach { println(it) }
}
printAllUppercase(listOf("foo", "Bar"))
printAllUppercase(listOf("FOO", "BAR"))
문자 시퀀스와 문자열에는 비어 있지 않고 모든 공백인지 확인하는 ifBlank 확장 함수도 있습니다.
val s = " \n"
println(s.ifBlank { "<blank>" })
println(s.ifBlank { null })
리플렉션에서 Sealed 클래스
kotlin-reflect에는 봉인 클래스의 직접 하위 유형을 나열하는 데 사용할 수 있는 새로운 API인 KClass.sealedSubclasses를 추가했습니다.
기타 변경 사항
- Boolean 유형은 이제 동반 객체(companion)를 가집니다.
- Any?.hashCode() 확장 함수는 null에 대해 0을 반환합니다.
- Char는 이제 MIN_VALUE 및 MAX_VALUE 상수를 제공합니다.
- 기본 유형 동반 객체에 SIZE_BYTES 및 SIZE_BITS 상수가 있습니다.
툴링
IDE에서 코드 스타일 지원
Kotlin 1.3에서는 IntelliJ IDEA에서 권장하는 코드 스타일을 지원합니다. 마이그레이션 가이드라인은 이 페이지에서 확인할 수 있습니다.
kotlinx.serialization
kotlinx.serialization은 Kotlin에서 객체를 (역)직렬화하는 데 대한 멀티플랫폼 지원을 제공하는 라이브러리입니다. 이전에는 별도의 프로젝트였지만, Kotlin 1.3부터는 다른 컴파일러 플러그인과 동등하게 Kotlin 컴파일러 배포판과 함께 제공됩니다. 주요 차이점은 이제 Kotlin IDE 플러그인 버전과 호환되는 Serialization IDE 플러그인을 수동으로 확인할 필요가 없다는 것입니다. 이제 Kotlin IDE 플러그인에 Serialization이 포함되어 있습니다!
세부 정보는 여기를 참조하세요.
kotlinx.serialization은 이제 Kotlin 컴파일러 배포에 포함되어 있지만 Kotlin 1.3에서는 여전히 실험적인 기능으로 간주됩니다.
스크립팅 업데이트
스크립팅은 실험적입니다. 언제든지 삭제되거나 변경될 수 있습니다. 평가 목적으로만 사용하십시오. 이에 대한 피드백은 YouTrack에서 환영합니다.
Kotlin 1.3은 스크립팅 API를 계속 발전 및 개선하며 스크립트 사용자 정의를 위한 일부 실험적인 지원을 도입합니다. 외부 속성 추가, 정적 또는 동적 종속성 제공 등과 같은 스크립트 사용자 정의를 위한 실험적인 지원을 도입합니다.
추가 세부 정보는 KEEP-75를 참조하세요.
스크래치 지원
Kotlin 1.3에서는 실행 가능한 Kotlin 스크래치 파일을 지원합니다. 스크래치 파일은 .kts 확장자를 가진 Kotlin 스크립트 파일로, 편집기에서 직접 실행하고 평가 결과를 얻을 수 있습니다.
세부 정보는 일반 스크래치 문서를 참조하세요.
원문
'Kotlin > What's new' 카테고리의 다른 글
[Kotlin 번역] What's new in Kotlin 1.4.30 (42) | 2023.09.16 |
---|---|
[Kotlin 번역] What's new in Kotlin 1.4.20 (3) | 2023.09.16 |
[Kotlin 번역] What's new in Kotlin 1.4.0 (3) | 2023.09.16 |
[Kotlin 번역] What's new in Kotlin 1.2 (28) | 2023.09.13 |
[Kotlin 번역] What's new in Kotlin 1.1 (46) | 2023.09.12 |
댓글