2017년 11월 28일
Table of contents
- 멀티플랫폼 프로젝트
- 다른 언어 기능
- 표준 라이브러리
- JVM 백엔드
- JavaScript 백엔드
멀티플랫폼 프로젝트 (실험적 기능)
멀티플랫폼 프로젝트는 Kotlin 1.2에서 제공되는 새로운 실험적인 기능으로, Kotlin에서 지원하는 대상 플랫폼인 JVM, JavaScript 및 (미래에는) Native 간에 코드를 재사용할 수 있게 해줍니다. 멀티플랫폼 프로젝트에서는 세 가지 종류의 모듈이 있습니다.
- 공통 모듈: 어떤 특정 플랫폼에 특정하지 않은 코드와 플랫폼 종속 API의 구현 없이 선언을 포함하는 모듈입니다.
- 플랫폼 모듈: 공통 모듈의 플랫폼 종속 선언을 특정 플랫폼에 대한 구현과 함께 포함하며, 다른 플랫폼 종속 코드도 포함할 수 있습니다.
- 일반 모듈: 특정 플랫폼을 대상으로 하며, 플랫폼 모듈의 종속성이 될 수도 있고 플랫폼 모듈에 종속될 수도 있습니다.
멀티플랫폼 프로젝트를 특정 플랫폼으로 컴파일할 때 공통 및 플랫폼별 부분의 코드가 모두 생성됩니다.
멀티플랫폼 프로젝트 지원의 주요 기능 중 하나는 공통 코드가 플랫폼별 부분에 대한 의존성을 기대된 및 실제 선언을 통해 표현할 수 있는 기능입니다. 기대 선언은 API (클래스, 인터페이스, 주석, 최상위 선언 등)를 지정합니다. 실제 선언은 해당 API의 플랫폼 종속 구현이거나 외부 라이브러리의 기존 구현을 참조하는 타입 별칭입니다. 다음은 예시입니다.
공통 코드에서:
// 기대된 플랫폼별 API:
expect fun hello(world: String): String
fun greet() {
// 기대된 API 사용:
val greeting = hello("멀티플랫폼 월드")
println(greeting)
}
expect class URL(spec: String) {
open fun getHost(): String
open fun getPath(): String
}
JVM 플랫폼 코드에서:
actual fun hello(world: String): String =
"안녕하세요, $world, JVM 플랫폼에서!"
// 기존 플랫폼별 구현 사용:
actual typealias URL = java.net.URL
멀티플랫폼 프로젝트를 빌드하는 자세한 내용과 단계는 멀티플랫폼 프로그래밍 문서를 참조하십시오.
다른 언어 기능
어노테이션에서 배열 리터럴
Kotlin 1.2부터 어노테이션의 배열 인수를 arrayOf 함수 대신 새로운 배열 리터럴 구문을 사용하여 전달할 수 있습니다:
@CacheConfig(cacheNames = ["books", "default"])
public class BookRepositoryImpl {
// ...
}
배열 리터럴 구문은 어노테이션 인수에 제한됩니다.
Top-level 프로퍼티 및 로컬 변수에 lateinit 사용
lateinit 수식어는 이제 top-level 프로퍼티 및 로컬 변수에 사용할 수 있습니다. 후자는 예를 들어, 람다가 다른 객체를 참조해야 하고 이 객체는 나중에 정의되어야 할 때 사용할 수 있습니다:
class Node<T>(val value: T, val next: () -> Node<T>)
fun main(args: Array<String>) {
// 세 개의 노드로 이루어진 순환:
lateinit var third: Node<Int>
val second = Node(2, next = { third })
val first = Node(1, next = { second })
third = Node(3, next = { first })
val nodes = generateSequence(first) { it.next() }
println("Values in the cycle: ${nodes.take(7).joinToString { it.value.toString() }}, ...")
}
lateinit var가 초기화되었는지 확인
lateinit var가 초기화되었는지를 확인하기 위해 property 참조의 isInitialized를 사용할 수 있습니다:
println("isInitialized before assignment: " + this::lateinitVar.isInitialized)
lateinitVar = "value"
println("isInitialized after assignment: " + this::lateinitVar.isInitialized)
기본 함수 매개변수가 있는 인라인 함수
인라인 함수에서 기본 함수 매개변수를 가질 수 있습니다:
inline fun <E> Iterable<E>.strings(transform: (E) -> String = { it.toString() }) =
map { transform(it) }
val defaultStrings = listOf(1, 2, 3).strings()
val customStrings = listOf(1, 2, 3).strings { "($it)" }
명시적 캐스트의 정보를 타입 추론에 사용
Kotlin 컴파일러는 이제 타입 캐스트의 정보를 타입 추론에 사용할 수 있습니다. 특히, 제네릭 메서드를 호출하고 반환 값을 특정 타입 Foo로 캐스트하는 경우 컴파일러는 이 호출을 위해 T가 Foo 타입에 바인딩되어야 함을 이해합니다.
안드로이드 개발자들에게 특히 중요한데, 컴파일러는 이제 Android API 레벨 26에서의 일반적인 findViewById 호출을 올바르게 분석할 수 있게 됩니다.
val button = findViewById(R.id.button) as Button
.
스마트 캐스트 개선
변수가 안전한 호출 표현식에서 할당되고 null 여부를 확인할 때, 스마트 캐스트는 안전한 호출 수신자에도 적용됩니다:
val firstChar = (s as? CharSequence)?.firstOrNull()
if (firstChar != null)
return s.count { it == firstChar } // s: Any is smart cast to CharSequence
val firstItem = (s as? Iterable<*>)?.firstOrNull()
if (firstItem != null)
return s.count { it == firstItem } // s: Any is smart cast to Iterable<*>
또한, 람다에서 스마트 캐스트가 적용되는 로컬 변수는 람다 이전에만 수정되는 경우에도 허용됩니다:
val flag = args.size == 0
var x: String? = null
if (flag) x = "Yahoo!"
run {
if (x != null) {
println(x.length) // x is smart cast to String
}
}
this::foo를 사용하는 바인딩된 호출 가능한 참조
this의 멤버에 대한 바인딩된 호출 가능한 참조를 명시적 수신자 없이 작성할 수 있습니다. 이는 외부 수신자의 멤버를 참조하는 람다에서 호출 가능한 참조를 더 편리하게 사용할 수 있게 합니다.
중요 변경: try 블록 이후에 사운드 스마트 캐스트
이전에 Kotlin은 try 블록 내에서 수행된 할당을 try 블록 이후의 스마트 캐스트에 사용했는데, 이로 인해 타입 및 null 안전성이 깨질 수 있고 런타임 오류로 이어질 수 있었습니다. 이 릴리스에서는 스마트 캐스트를 더 엄격하게 만들어 이러한 스마트 캐스트에 의존하는 코드 일부가 깨질 수 있지만, 이러한 스마트 캐스트에 의존하는 코드가 수정되었습니다.
이전 스마트 캐스트 동작으로 전환하려면 컴파일러 인수로 fallback 플래그 -Xlegacy-smart-cast-after-try를 전달하십시오. 이는 Kotlin 1.3에서는 사용이 중단될 예정입니다.
폐기 예정: data 클래스에서 copy 재정의
동일한 시그니처를 가진 copy 함수를 이미 가진 유형에서 파생된 데이터 클래스는 데이터 클래스의 생성된 copy 구현에서 상위 유형의 기본값을 사용하여 예상치 못한 동작을 야기하거나, 상위 유형에 기본 매개변수가 없는 경우 런타임 오류가 발생할 수 있었습니다.
이러한 copy 함수 충돌을 유발하는 상속은 Kotlin 1.2에서 경고와 함께 폐기되었으며 Kotlin 1.3에서는 오류가 발생할 것입니다.
폐기 예정: enum 항목 내의 중첩 형식
enum 항목 내에서 inner 클래스가 아닌 중첩 형식을 정의하는 것은 초기화 로직의 문제로 인해 폐기되었습니다. 이로 인해 Kotlin 1.2에서 경고가 발생하며 Kotlin 1.3에서는 오류가 발생합니다.
폐기 예정: vararg에 대한 단일 명명된 인수
어노테이션의 배열 리터럴과 일관성을 유지하기 위해 단일 항목을 vararg 매개변수에 명명된 형태로 전달하는 것(foo(items = i))이 폐기되었습니다. 대신 해당 배열 팩토리 함수와 함께 전파 연산자를 사용하십시오:
foo(items = *arrayOf(1))
이러한 경우 중복 배열 생성을 제거하는 최적화가 있어 성능 저하를 방지합니다. 단일 인수 형식은 Kotlin 1.2에서 경고를 발생시키며 Kotlin 1.3에서는 사용되지 않게 됩니다.
폐기 예정: Throwable을 확장하는 제네릭 클래스의 inner 클래스
Throwable을 상속하는 제네릭 유형의 inner 클래스는 throw-catch 시나리오에서 타입 안전성을 위반할 수 있으므로 폐기되었습니다. Kotlin 1.2에서는 경고가 발생하며 Kotlin 1.3에서는 오류가 발생합니다.
폐기 예정: 읽기 전용 프로퍼티의 백킹 필드 변경
읽기 전용 프로퍼티의 백킹 필드를 커스텀 getter에서 field = ...와 같이 변경하는 것은 폐기되었습니다. Kotlin 1.2에서 경고가 발생하며 Kotlin 1.3에서는 오류가 발생합니다.
표준 라이브러리
Kotlin 표준 라이브러리 아티팩트 및 분할 패키지
Kotlin 표준 라이브러리는 이제 Java 9 모듈 시스템과 완벽하게 호환되며, 분할 패키지 (동일한 패키지에서 클래스를 선언하는 여러 JAR 파일)를 금지합니다. 이를 지원하기 위해 기존의 kotlin-stdlib-jre7 및 kotlin-stdlib-jre8을 대체하는 새로운 아티팩트 kotlin-stdlib-jdk7 및 kotlin-stdlib-jdk8이 도입되었습니다.
새로운 아티팩트의 선언은 Kotlin 관점에서 동일한 패키지 이름으로 볼 수 있지만 Java에서는 다른 패키지 이름을 가집니다. 따라서 새 아티팩트로 전환하려면 소스 코드를 변경할 필요가 없습니다.
새 모듈 시스템과의 호환성을 보장하기 위해 또 다른 변경 사항은 kotlin-reflect 라이브러리의 kotlin.reflect 패키지에서 폐기된 선언을 제거하는 것입니다. 이들을 사용하고 있었다면 kotlin.reflect.full 패키지의 선언을 사용하도록 전환해야 합니다. 이 기능은 Kotlin 1.1부터 지원됩니다.
windowed, chunked, zipWithNext
새로운 Iterable<T>, Sequence<T> 및 CharSequence에 대한 확장 함수는 버퍼링 또는 일괄 처리 (chunked), 슬라이딩 창 및 슬라이딩 평균 계산 (windowed), 연속 항목 쌍 처리 (zipWithNext)과 같은 사용 사례를 다룹니다:
val items = (1..9).map { it * it }
val chunkedIntoLists = items.chunked(4)
val points3d = items.chunked(3) { (x, y, z) -> Triple(x, y, z) }
val windowed = items.windowed(4)
val slidingAverage = items.windowed(4) { it.average() }
val pairwiseDifferences = items.zipWithNext { a, b -> b - a }
fill, replaceAll, shuffle/shuffled
fill, replaceAll, shuffle/shuffled
리스트를 조작하기 위한 fill, replaceAll 및 MutableList용 shuffle 및 읽기 전용 List용 shuffled의 확장 함수 집합이 추가되었습니다:
val items = (1..5).toMutableList()
items.shuffle()
println("Shuffled items: $items")
items.replaceAll { it * 2 }
println("Items doubled: $items")
items.fill(5)
println("Items filled with 5: $items")
kotlin-stdlib의 수학 연산
오랜 기간 동안 요청되었던 대로 Kotlin 1.2에서는 JVM 및 JS에서 공통인 수학 연산을 다루는 kotlin.math API를 추가하였으며 다음과 같은 내용을 포함합니다:
- 상수: PI 및 E
- 삼각 함수: cos, sin, tan 및 그 역함수: acos, asin, atan, atan2
- 쌍곡선 함수: cosh, sinh, tanh 및 그 역함수: acosh, asinh, atanh
- 지수 연산: pow (확장 함수), sqrt, hypot, exp, expm1
- 로그 함수: log, log2, log10, ln, ln1p
- 반올림 함수:
- ceil, floor, truncate, round (반올림 반올림) 함수
- roundToInt, roundToLong (정수 반올림) 확장 함수
- 부호 및 절댓값 함수:
- abs 및 sign 함수
- absoluteValue 및 sign 확장 프로퍼티
- withSign 확장 함수
- 두 값의 최대 및 최소
- 이진 표현:
- ulp 확장 프로퍼티
- nextUp, nextDown, nextTowards 확장 함수
- toBits, toRawBits, Double.fromBits (이들은 kotlin 패키지에 있습니다)
동일한 함수 세트 (상수를 제외하고)가 Float 인수에 대해서도 사용 가능합니다.
BigInteger 및 BigDecimal에 대한 연산자 및 변환
Kotlin 1.2에서는 BigInteger 및 BigDecimal과 함께 작업하기 위한 함수 집합을 도입하였으며 다른 숫자 유형에서 BigInteger 및 BigDecimal을 만드는 데 사용할 수 있습니다. 이것들은 다음과 같습니다:
- Int 및 Long에 대한 toBigInteger
- Int, Long, Float, Double 및 BigInteger에 대한 toBigDecimal
- 산술 및 비트 연산자 함수:
- 이진 연산자 +, -, *, /, % 및 중위 함수 and, or, xor, shl, shr
- 단항 연산자 -, ++, -- 및 함수 inv
부동 소수점을 비트로 변환하는 함수
Double 및 Float를 비트 표현으로 변환하고 해당 비트 표현에서 부동 소수점 수를 만들기 위한 새로운 함수가 추가되었습니다:
- Double 및 Float의 비트를 반환하는 toBits 및 toRawBits (Double의 경우 Long을 반환, Float의 경우 Int를 반환)
- 비트 표현에서 부동 소수점 수를 생성하기 위한 Double.fromBits 및 Float.fromBits 함수
정규식이 이제 직렬화 가능
kotlin.text.Regex 클래스는 이제 직렬화 가능하며 직렬화 가능한 계층 구조에서 사용할 수 있습니다.
Closeable.use에서 Throwable.addSuppressed를 호출합니다.
Closeable.use 함수는 리소스를 닫을 때 예외가 발생하면 해당 예외 이후 리소스를 닫을 때 Throwable.addSuppressed를 호출합니다.
이 동작을 활성화하려면 종속성에 kotlin-stdlib-jdk7이 있어야 합니다.
JVM 백엔드
생성자 호출 정규화
1.0 버전 이후로 Kotlin은 try-catch 표현식 및 인라인 함수 호출과 같은 복잡한 제어 흐름을 가진 표현식을 지원했습니다. 이러한 코드는 Java Virtual Machine 명세에 따라 유효한 코드입니다. 그러나 일부 바이트코드 처리 도구는 생성자 호출의 인수에 이러한 표현식이 포함되어 있을 때 이러한 코드를 처리하지 못하는 경우가 있습니다.
이러한 바이트코드 처리 도구를 사용하는 사용자들을 위해 이 문제를 완화하기 위해 컴파일러 옵션인 (-Xnormalize-constructor-calls=MODE)을 추가했습니다. 이 옵션은 생성자 호출에 대해 더 Java와 유사한 바이트코드를 생성하도록 컴파일러에 지시합니다. 여기서 MODE는 다음 중 하나입니다:
- disable (기본값) – Kotlin 1.0 및 1.1과 동일한 방식으로 바이트코드를 생성합니다.
- enable – 생성자 호출에 대해 Java와 유사한 바이트코드를 생성합니다. 이는 클래스가 로드되고 초기화되는 순서를 변경할 수 있습니다.
- preserve-class-initialization – 생성자 호출에 대해 Java와 유사한 바이트코드를 생성하며 클래스 초기화 순서를 보존합니다. 이는 여러 클래스 간에 공유되는 복잡한 상태가 있고 클래스 초기화 시 업데이트되는 경우에만 사용하세요. 이는 애플리케이션 전체 성능에 영향을 미칠 수 있습니다.
"수동" 해결책은 제어 흐름이 있는 하위 표현식의 값을 직접 계산하는 대신 호출 인수 내에서 변수에 값을 저장하는 것입니다. 이것은 -Xnormalize-constructor-calls=enable과 유사합니다.
Java-default 메서드 호출
Kotlin 1.2 이전에 JVM 1.6을 대상으로 하는 동안 Java-default 메서드를 오버라이드하는 인터페이스 멤버는 super 호출에 대한 경고를 생성했습니다. "Super calls to Java default methods are deprecated in JVM target 1.6. Recompile with '-jvm-target 1.8'." Kotlin 1.2에서는 이에 대한 오류가 대신 발생하므로 해당 코드를 JVM 대상 1.8로 컴파일해야 합니다.
중요 변경 사항: 플랫폼 형식의 null에 대한 x.equals(null)의 일관된 동작
Java 기본형 (Int!, Boolean!, Short!, Long!, Float!, Double!, Char!)에 매핑된 플랫폼 형식에 대한 x.equals(null) 호출은 x가 null인 경우에도 잘못된 true를 반환했습니다. Kotlin 1.2부터는 플랫폼 형식의 null 값에 대한 x.equals(...) 호출은 NPE(NullPointerException)을 throw합니다 (그러나 x == ...은 그렇지 않습니다).
1.2 이전 동작으로 돌아가려면 컴파일러에 -Xno-exception-on-explicit-equals-for-boxed-null 플래그를 전달하십시오.
중요 변경 사항: 인라인 확장 수신자를 통해 플랫폼 null이 이탈되는 버그 수정
플랫폼 형식의 null 값을 대상으로 호출되는 인라인 확장 함수는 수신자를 null로 확인하지 않고 null을 다른 코드로 이탈시키는 것을 허용했습니다. Kotlin 1.2는 이 호출 위치에서 이 확인을 강제하고 수신자가 null인 경우 예외를 throw합니다.
이전 동작으로 전환하려면 컴파일러에 -Xno-receiver-assertions 폴백 플래그를 전달하십시오.
JavaScript 백엔드
기본적으로 TypedArrays 지원 활성화
이전에 선택적으로 활성화되었던 JS typed arrays 지원은 이제 기본적으로 활성화되었습니다. 이 기능은 Kotlin 기본 배열(IntArray, DoubleArray 등)을 JavaScript typed arrays로 변환합니다.
도구
경고를 오류로 취급하는 옵션 추가
컴파일러는 이제 모든 경고를 오류로 취급하는 옵션을 제공합니다. 명령줄에서 -Werror를 사용하거나 다음 Gradle 스니펫을 사용하십시오:
compileKotlin {
kotlinOptions.allWarningsAsErrors = true
}
원문
'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.3 (114) | 2023.09.13 |
[Kotlin 번역] What's new in Kotlin 1.1 (46) | 2023.09.12 |
댓글