본문 바로가기
Kotlin/Release Notes

[Kotlin Release Notes] Kotlin 1.0 Beta Candidate is Out!

by 노력남자 2023. 8. 29.
반응형

2015년 10월 22일

 

Kotlin 1.0 베타 후보 버전을 소개해 기쁩니다. 곧 공식 1.0 베타 버전이 출시될 예정입니다. 현재까지는 이진 형식이 확정되었으며, 주요 언어 변경 사항은 계획되지 않았으며 표준 라이브러리에는 몇 가지 변경 사항만이 있을 예정입니다.

이 게시물에서는 M14 이후의 변경 사항을 설명합니다. 이 포스트에서는 다음과 같은 내용을 다루고 있습니다.

- 객체로부터의 임포트

 

- 새로운 안전한 컬렉션 인터페이스

 

- Java 상수의 인라이닝

 

- Java 정적 멤버에 대한 더 나은 지원

 

- 등등

 

언어 변경 사항


우리는 일부 중요한 새로운 기능과 함께 일부 깨진 변경 사항을 도입하고 있습니다.


연산자와 중위 함수


M14부터 연산자 오버로딩에 사용되는 함수에는 operator 수정자가 필요합니다. 이제 동일한 규칙이 중위 함수에도 적용됩니다.

 

operator Foo.plus(other: Foo): Foo { ... }
fun testPlus() = Foo() + Foo()

infix fun Foo.bar(other: Foo): Foo { ... }
fun testInfix() = Foo() bar Foo()

 

또한 Java 함수에 대해서도 이 요구 사항을 완화했습니다. 적절한 시그니처를 가진 어떤 Java 함수라도 연산자로 사용할 수 있지만 중위 함수로는 사용할 수 없습니다.

일부 연산자 이름은 모호성을 피하기 위해 변경되었습니다.

- 이제 단항 함수에 대해서는 unaryPlus와 unaryMinus를 사용해야 합니다. 예를 들어, -Foo() 대신 Foo().unaryMinus()를 사용합니다.

 

- 위임된 프로퍼티의 경우 get과 set 대신 getValue와 setValue를 사용해야 합니다.

 

코드 정리 작업을 통해 코드를 마이그레이션할 수 있습니다.

 

또한, 이제 컴파일러에서 연산자 시그니처를 선언 위치에서 검사합니다. 이러한 검사 중 일부는 나중에 완화될 수 있지만 현재 상태가 꽤 좋은 시작점이라고 믿습니다.

 

객체로부터의 임포트


Kotlin은 이제 객체의 개별 멤버를 이름별로 가져올 수 있습니다. (*를 이용한 객체에서의 임포트는 지원하지 않습니다.)

 

import mypackage.MyObject.foo
val test = foo("...")

 

이 예제에서는 mypackage.MyObject라는 이름의 객체에서 모든 foo라는 이름의 멤버를 가져왔습니다.

클래스의 동반 객체로부터 임포트하기 위해서는 그들의 전체 이름을 지정해야 합니다.

 

import mypackage.MyClass.Companion.foo

 

@Deprecated의 강화


우리는 최근 많은 코드를 마이그레이션하고 있습니다. 그래서 Kotlin의 @Deprecated 어노테이션은 매우 강력해졌습니다. 이제 메시지를 필수로 요구하며, ReplaceWith("...")를 통해 대체할 수 있을 뿐만 아니라 레벨(WARNING, ERROR, HIDDEN)을 설정할 수 있습니다.

- WARNING은 기본값이며 일반적인 사용되는 방식과 같이 동작합니다. 호출 위치에서 경고가 발생하고 IDE에서 해당 부분을 취소선으로 표시합니다.


- ERROR는 같은 동작을 하지만 경고 대신 컴파일 오류가 보고됩니다.


- HIDDEN은 이전에 @HiddenDeclaration으로 알려졌던 것과 같이 컴파일 타임에 해당 선언을 클라이언트에게 보이지 않게 만듭니다.

 

캡처된 지역 변수에 대한 스마트 캐스트

 

스마트 캐스트는 이제 람다 내에서 캡처된 로컬 변수에 대해서도 동작합니다. 단, 해당 변수가 람다 내에서 변경되지 않는 경우에 한합니다.

 

var a: Any? = ...
val mapped = users.map {
    "${it.name}: $a"
}
if (a is String) {
    println(a.length) // 이제 동작합니다
}

 

동일한 패키지 내에서 여러 개의 main() 함수 정의

 

이제 표준 시그니처를 가진 main() 함수를 파일마다 정의할 수 있습니다. 단, @file:JvmMultileClass의 경우에는 예외입니다. 이 기능은 코드 실험 시 매우 유용합니다.

 

// 파일: a.kt
package foo
fun main(args: Array<String>) {
    println("a.kt")
}
// 파일: b.kt
package foo
fun main(args: Array<String>) {
    println("b.kt")
}

 

가변인자와 스프레드 연산자


가변인자 함수를 호출할 때, 스프레드 연산자를 사용하여 배열을 가변인자로 변환할 수 있습니다.

 

fun foo(vararg args: String) { ... }
fun bar(vararg args: String) { 
    foo(*args) // 스프레드 연산자
}

 

스프레드 연산자의 의미론이 수정되어 이제 항상 foo가 볼 배열은 "외부 세계"에 의해 수정되거나 관찰되지 않음을 보장합니다. 스프레드 연산자가 사용될 때마다 방어적 복사본이 만들어진다고 가정할 수 있습니다(실제로 나중에 메모리 트래픽을 줄이기 위해 몇 가지 최적화가 구현될 수 있습니다).

결과적으로, Kotlin 라이브러리의 저자들은 방어적 복사 없이도 가변인자 배열을 안전하게 저장할 수 있다고 가정할 수 있습니다.

참고: 이 보장은 Java에서 Kotlin 함수를 호출할 때는 충족되지 않습니다. 왜냐하면 거기에서 스프레드 연산자가 사용되지 않기 때문입니다. 따라서 함수가 Java와 Kotlin에서 모두 사용될 예정인 경우 Java 클라이언트를 위해 배열을 전달하기 전에 복사해야 함을 명시하는 메모가 포함된 Java용 계약이 있어야 합니다.

 

"setparam" 주석 대상이 "sparam"에서 "setparam"으로 변경되었습니다

 

프로퍼티의 setter 매개변수에 주석을 달려면 sparam 대신 setparam use-site 대상을 사용하십시오.

 

@setparam:Inject
var foo: Foo = ...

 

@UnsafeVariance 주석


때때로 우리는 클래스에서 선언 사이트 변위 검사를 억제해야 할 수 있습니다. 예를 들어 읽기 전용 세트를 공변으로 유지하면서 Set.contains를 타입 안전하게 만들기 위해 다음과 같이 해야 했습니다.

 

interface Set<out E> : Collection<E> {
    fun contains(element: @UnsafeVariance E): Boolean
}

 

이는 contains의 구현자에게 책임을 지우기 때문에 요소의 실제 유형이 실행 시간에 아무 것이든 될 수 있으며, 편리한 시그니처를 얻기 위해 때로는 필요할 수 있습니다. 컬렉션의 타입 안전성에 대한 자세한 내용은 아래에서 확인할 수 있습니다.

그래서 우리는 이 목적을 위해 @UnsafeVariance 주석을 도입했습니다. 이 주석은 의도적으로 길게 만들어져 남용을 경고하도록 디자인되었습니다.

 

기타 여러 체크 및 제한 사항들


많은 체크가 추가되었고, 이러한 제한 사항 중 일부는 나중에 완화될 수 있습니다.

 

타입 매개변수 선언. 우리는 모든 이러한 선언이 일관되도록 타입 매개변수 선언의 구문을 제한하기로 결정했습니다. 따라서 다음과 같은 선언은 더 이상 권장하지 않습니다.

 

- fun foo<T>() // 대신에 fun <T> foo():

 

- 모든 타입 매개변수에 대한 제약 조건은 "where" 또는 "<...>" 내부에서 발생해야 합니다:

 

fun <T: Any> foo() {} // OK
fun <T> foo() where T: Serializable, T: Comparable<T> {} // OK
fun <T: Serializable> foo() where T: Comparable<T> {} // 금지됨

 

배열에 대한 동적 타입 검사. 배열 요소 유형은 Java에서 재현됩니다. 그러나 Kotlin 특정 속성인 nullability는 그렇지 않습니다. 그래서 a is Array<String>과 같은 검사를 허용하는 배열에 대한 특별한 처리를 제거했습니다. 이제 배열은 다른 일반적인 제네릭 클래스와 마찬가지로 작동합니다. a is Array<*>를 확인할 수 있으며 a as Array<String>와 같은 캐스트는 확인되지 않은 것으로 표시됩니다. 우리는 Java에서 주어진 배열이 유형 T의 요소를 포함할 수 있는지 확인하는 JVM 특정 함수인 isArrayOf<T>()를 추가했습니다:

 

val a: Any? = ...
if (a is Array<*> && a.isArrayOf<String>()) {
    println((a as Array<String>)[0])
}

 

위임된 프로퍼티. 위임된 프로퍼티에 대한 관례는 이제 getValue와 setValue에서 PropertyMetadata 대신 KProperty<*>을 사용합니다.

 

fun Foo.getValue(thisRef: Bar, property: KProperty<*>): Baz? {
    return myMap[property.name]
}

 

코드 정리는 마이그레이션을 도와줄 것입니다.

 

호출 가능한 참조. 현재 일부 :: 사용법은 금지되어 나중에 바운드 참조를 구현할 때 활성화될 예정입니다. 특히 foo가 클래스의 멤버인 경우 현재 사용하지 않아야 합니다. MyClass::foo만 사용해야 합니다. 객체의 멤버에 대한 참조도 일시적으로 지원되지 않습니다(그들도 바운드 참조로 작동할 것입니다). 임시로 람다를 해결 방법으로 사용할 수 있습니다.

If-표현식. 우리는 if와 when의 의미론을 통일하기 위해 if가 표현식으로 사용될 때는 항상 else가 필요하도록 요구합니다:

 

val foo = if (cond) bar // ERROR: else가 필요합니다

 

Nothing을 반환하는 함수. 함수가 예외를 throw하거나 영원히 루프에 빠진다는 것이 알려져 있는 경우, 해당 함수의 반환 유형은 Nothing일 수 있습니다. 이는 해당 함수가 정상적으로 반환되지 않음을 의미합니다. 도구가 더 똑똑하게 만들어지기 위해 이제 이러한 함수는 항상 명시적으로 반환 유형을 지정해야 합니다.

 

fun foo() = throw MyException() // 반환 유형을 명시적으로 지정해야 함
fun bar(): Nothing = throw MyException() // OK
fun baz() { throw MyExcepion() } // OK
fun goo(): Goo { throw MyExcepion() } // OK

 

이제 이는 마이그레이션을 위한 코드 정리와 함께 경고가 되며 나중에 오류로 승격될 것입니다.

가시성 검사가 제한되어, 예를 들어 public 선언은 지역, private 또는 internal 유형을 노출시키지 못하도록 변경되었습니다. 내부 선언에 대한 액세스는 컴파일러 및 IDE에서 확인됩니다.

더 많은 정보는 여기에서 확인하실 수 있습니다.

 

컬렉션


이 버전에서 주요 변경 사항은 컬렉션과 기타 핵심 API를 정리하여 size는 이제 속성이 되었으며, contains는 형 안전성을 가지고 있습니다. 이제 Any? 대신 E를 사용합니다. 이것은 라이브러리가 Kotlin과 호환되면서 Kotlin 느낌을 유지하도록 하는 주요 노력이었습니다. 이에 대한 많은 컴파일러 마법이 숨겨져 있지만, 결과물은 만족스럽습니다.

 

예시:

 

val strs = setOf("1", "abc")
if (1 in strs) { // 'strs'는 문자열 집합이므로 Int를 포함할 수 없음
    println("No!")
}

 

유사한 코드가 Java에서도 작동합니다. Set<E>.contains (which in은 컴파일되어)는 E가 아닌 set의 요소 유형인 Object를 사용합니다. 이것은 오류가 발생하기 쉬웠으므로 Kotlin 컬렉션 인터페이스를 안전하게 만들기로 결정했습니다(Java 컬렉션과의 완전한 호환성을 유지하면서). 따라서 contains는 E를 사용하며 위의 예제는 Kotlin에서 올바르지 않습니다.

현재 Kotlin 컴파일러는 위의 예제에서 in에 대한 사용에 대해 사용 중지 경고를 보고합니다. 왜냐하면 우리는 모두가 마이그레이션을 돕기 위해 표준 라이브러리에 과도기적인 확장 함수를 제공했기 때문입니다. 그러나 곧 이는 오류가 될 것입니다. 코드 정리는 여기에서 우리의 친구입니다: 1 in strs를 strs.containsRaw(1)로 대체할 것입니다. containsRaw는 새로운 표준 라이브러리 함수로서 우리가 Java와 유사한 동작이 필요한 경우 사용할 수 있습니다. 어떤 객체가 어떤 집합에 속해 있는지를 확인하기 위해 containsRaw를 사용할 수 있습니다.

 

요점:

- Collection.contains, Map.get 등 일부 다른 컬렉션 메서드는 이제 더 안전하게 작동합니다.

 

- 무타입 동작을 얻기 위해 containsRaw, getRaw 등을 사용할 수 있습니다.

 

- Collection.size, Array.size, String.length, Map.Entry.key 등은 이제 속성입니다.

 

- List.remove(Int)은 충돌을 피하기 위해 removeAt(int)로 이름이 변경되었습니다. 이것은 List<Int>.remove가 항목이 아닌 인덱스로 제거하는 것과 충돌하지 않도록 하기 위한 것입니다.

 

- 코드 정리는 모든 코드를 마이그레이션할 것입니다.

 

모든 일반적인 Java 컬렉션은 변경 없이 작동합니다. 컴파일러는 java.util.ArrayList에서 "property" size를 찾을 수 있는 방법을 알고 있습니다.

 

Java 상호 운용성


Kotlin 선언이 Java에서 어떻게 볼 수 있는지 및 그 반대로의 가시성에 관한 중요한 변경 사항이 많이 있었습니다.


라이브러리에서 정의된 상수 인라인


이제부터는 라이브러리에서 나온 Java 상수(public static final primitive 및 String 유형의 필드)를 인라인합니다. 이것은 API 호환성 문제로 고생한 Android 개발자들에게 도움이 될 것입니다.

 

if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) { ... }

 

이제 Lollipop보다 어린 런타임에서도 작동합니다(이전에 Lollipop보다 어린 런타임에서 충돌이 발생했습니다).

더 작은 런타임


우리는 이곳에서 시작한 것 뿐이지만, kotlin-runtime 라이브러리의 크기를 줄이는 작업을 위한 기반을 마련했습니다. 이제 M14에서 200K만 더 작아졌을 뿐이지만, 더 작아지도록 더 많은 작업을 할 것입니다(그리고 호환성이 깨지지 않을 것입니다).


정적 메서드, 필드 및 클래스


Kotlin은 이제 Java statics에 대해 매우 친숙합니다:


- Java 클래스 내에서 Kotlin 하위 클래스에서 상속된 중첩 클래스, 정적 메서드 및 필드를 사용할 수 있습니다.

 

- Kotlin 하위 클래스 내에서 상속된 Java 정적 메서드 및 필드에는 하위 클래스 이름을 통해 액세스할 수 있습니다: SubClass.SUPER_CLASS_CONSTANT

 

- Kotlin 하위 클래스 내에서 상위 클래스의 동반 객체 멤버에 액세스할 수 있습니다.

 

- 다만 클래스에 액세스할 수 있는 정규화된 이름은 정규 이름, 즉 SubClass.SupersInnerClass를 사용할 수 없습니다.

 

이로써 Android와 같은 큰 상속 기반 프레임워크에서 종종 발생하던 많은 문제가 해결되었습니다.


인터페이스 상속 규칙은 Java 8과 호환됩니다

 

Kotlin을 미래 지향적으로 만들기 위해 Java 8의 규칙을 따르도록 몇 가지 요구 사항을 추가하여 나중에 Kotlin 인터페이스의 함수 본문을 Java 기본 메서드로 컴파일할 수 있도록 했습니다.

어떤 경우에는 Kotlin이 이제 이전보다 명시적인 오버라이드를 요구하기 때문에 더 많은 명시적인 오버라이드가 필요할 수 있습니다. 불행하게도 Any의 메서드는 더 이상 인터페이스에서 구현될 수 없습니다(이는 Java 8에서 작동하지 않습니다).

참고: 인터페이스 메서드의 기본 구현은 MyIntf.DefaultImpls의 정적 멤버로서 Java에서 액세스할 수 있습니다.


부울값에 대한 더 편리한 getter 이름


Kotlin의 속성이 예를 들어 isValid로 이름이 지정된 경우, 이제 해당하는 Java getter는 isValid()가 되며 getIsVaild()가 아닙니다.

@JvmField 및 객체


순수한 필드(쌍으로 되지 않은 get/set) 생성 전략을 더 예측 가능하게 만들었습니다. 이제부터는 @JvmField, lateinit 또는 const로 주석이 달린 속성만 Java 클라이언트에게 필드로 노출됩니다. 이전 버전은 휴리스틱을 사용하여 무조건 객체 내에서 정적 필드를 생성했습니다. 이것은 기본적으로 바이너리 호환성을 유지하기 위한 초기 디자인 목표에 반하는 것입니다.

또한 싱글톤 인스턴스는 이제 INSTANCE라는 이름으로 액세스할 수 있습니다(INSTANCE$ 대신).

인터페이스에서 @JvmField 사용을 금지했습니다. 왜냐하면 우리는 그들에 대한 적절한 초기화 의미론을 보장할 수 없기 때문입니다.


Int는 Serializable입니다


이제 Int 및 기타 기본 유형은 JVM에서 Serializable입니다. 이는 많은 프레임워크에 도움이 될 것입니다.


"패키지 파사드" 없음


KotlinPackage와 같은 클래스는 사라졌습니다. 새로운 클래스 파일 레이아웃으로의 전환을 완료하고 이전에 폐기된 "패키지 파사드"가 제거되었습니다. FileNameKt와/또는 @file:JvmName(선택적인 @file:JvmMultifileClass와 함께)를 사용하세요.


내부 요소의 이름이 이제 변경되었습니다


Java에 내부 가시성이 없기 때문에 내부 선언의 이름을 변경하여 다른 모듈에서 클래스를 확장할 때 오버라이드에서 예기치 않은 충돌을 피하기 위해 이름을 바꾸었습니다. 기술적으로 내부 멤버는 Java 클라이언트에게 사용 가능하지만, 예쁘지 않아 보이며, 이는 라이브러리 진화의 예측 가능성을 위해 최소한의 대가입니다.


기타 폐기 및 제한 사항:


- @Synchronized 및 @Volatile는 추상 선언에 적용할 수 없습니다.

 

- 외부 주석을 제거하기 위한 최종 단계로 @KotlinSignature는 폐기되며 제거될 것입니다.

 

- Nothing을 포함하는 일반 유형은 더 이상 정확한 Java 유형이 없기 때문에 이제 원시 Java 유형으로 컴파일됩니다.

 

IDE 변경 사항


이제 Parameter Info는 거의 모든 곳에서 작동하며, 괄호, this 및 super 호출도 지원됩니다.

 

 

완성은 이제 호출 가능한 참조를 지원합니다(:: 뒤에)

 

 

equals() 및 hashCode() 메서드를 쉽게 생성할 수 있습니다.

 

 

또한 IDE는 상위 클래스 생성자를 기반으로 보조 생성자를 생성하고 현재 초기화되지 않은 속성을 지원합니다.

 

 

Java 클래스 및 열거형의 정적 멤버를 가져올 수 있는 구성 가능한 "import *" 지원도 제공됩니다.

 

 

마지막으로, 유닛 테스트 경험이 훨씬 더 원활해졌습니다. 개선 사항 목록: "Create Test" 작업, 테스트는 거터 아이콘을 통해 실행 가능하며, 테스트 및 테스트 대상 간 탐색(⇧⌘T/⇧^T)도 지원되며, 필요한 경우 JUnit 및 TestNG에 대한 종속성을 추가하는 퀵픽스도 제공됩니다.

 

 

라이브러리


전파 연산자 의미론의 이전 변경으로 인해 listOf()가 더 효율적으로 작동합니다. 입력 배열을 방어적으로 복사할 필요가 없기 때문입니다.

정규식 API가 개선되어 이제 regex.hasMatch(string) 대신 문자열 안에서 regex를 사용할 수 있습니다.


도구


컴파일러 데몬은 이제 기본적으로 IDE에서 활성화됩니다.

독립 모듈의 병렬 컴파일이 데몬에서 지원됩니다.

외부 주석과 관련된 옵션은 Maven 및 Gradle과 같은 도구에서 제거되었습니다.


설치


Kotlin은 IntelliJ IDEA 15 RC와 번들로 제공됩니다. IntelliJ IDEA 14의 경우 플러그인 관리자를 통해 업데이트하십시오.

좋은 Kotlin을 이용하세요!

 

원문

 

https://blog.jetbrains.com/kotlin/2015/10/kotlin-1-0-beta-candidate-is-out/

반응형

댓글