본문 바로가기
Kotlin/Release Notes

[Kotlin Release Notes] M8 is out!

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

2014년 7월 2일

 

지난 릴리스 이후로 정말 바쁜 몇 달이었어요. 특히 속도 측면에서 상당한 개선을 이루어내기 위해 열심히 작업해왔습니다. 이번 릴리스에는 정말 많은 좋은 업데이트가 있답니다. 시작해볼까요?

 

JVM


속성에 대한 Reflection


Kotlin의 미래적인 반사 능력에 대한 첫 번째 살펴보기로, 이제 속성을 Kotlin에서 일급 객체로서 접근할 수 있습니다:

 

var x = 1

fun main(args: Array<String>) {
    println(::x.get()) // "1" 출력
    ::x.set(2)
    println(x)         // "2" 출력
}

 

"::propertyName" 구문은 속성 객체를 제공하므로 해당 값을 가져오거나 설정할 수 있습니다. 또한 속성의 이름에도 접근할 수 있습니다 (다양한 종류의 프레임워크에 유용합니다). 앞으로 더 많은 기능을 추가할 예정입니다.

클래스의 멤버인 속성에 접근하려면 다음과 같이 할 수 있습니다:

 

class A(val p: Int)

fun main(args: Array<String>) {
    val prop = A::p
    println(prop.get(A(1))) // "1" 출력
}

 

물론 확장 함수에도 동작합니다:

 

val String.lastChar: Char
  get() = this[this.length - 1]

fun main(args: Array<String>) {
  println(String::lastChar.get("abc")) // "c" 출력
}

 

Kotlin의 반사의 다른 측면은 Java의 반사와의 상호 운용성입니다. 예를 들어, Kotlin 속성의 백업 필드나 getter로 동작하는 Java 메서드를 찾으려면 다음과 같이 할 수 있습니다:

 

import kotlin.reflect.jvm.*

class A(val p: Int)

fun main(args: Array<String>) {
    println(A::p.javaGetter) // "public final int A.getP()" 출력
    println(A::p.javaField)  // "private final int A.p" 출력
}

 

앞으로 몇 달 동안 반사 능력을 더 발전시킬 것입니다. 궁극적인 목표는 프레임워크 작성자들에게 그들의 요구에 맞는 정말 강력한 도구를 제공하는 것입니다. 계획에는 적절한 Kotlin 클래스 내부 조사, 리플렉션을 통해 유형을 사용할 수 있게 만들기, 호출 가능한 참조에 반사 기능 가져오기(::functionName) 및 기타 기능이 포함됩니다. 기대해주세요.

 

인라인 함수 개선


인라인 함수에 대한 몇 가지 개선사항을 소개합니다.

- 기본 매개변수를 가진 함수 지원

 

- 객체 내부로 인라인이 가능한 함수 지원


이 두 가지 기능을 함께 보여주기 위해 다음 예시를 살펴보겠습니다. 가령, Value 인터페이스가 있다고 가정해봅시다:

 

interface Value {
    fun get(): V
}

 

이제 계산이 잠금으로 보호되는 값을 생성하고자 한다고 가정해봅시다:

 

inline fun guardedValue(
        lock: Lock = ReentrantLock(),
        compute: () -> T
): Value {
    return object : Value {
        override fun get(): T {
            return lock.withLock {
                compute()
            }
        }
    }
}

 

우리의 guardedValue() 함수는 인라인 함수입니다. 이제 lock 매개변수는 기본값을 가지며 (새로운 ReentrantLock이 각 값마다 생성됨), 이 함수 내부의 객체 표현식은 compute 매개변수를 "캡처"하고 익명 클래스 내에 직접 인라인합니다. 이로써 인라인되지 않은 경우처럼 값당 두 개의 클래스와 두 개의 객체 대신 하나의 클래스와 하나의 객체만 생성되고 저장됩니다.

 

플랫폼 시그니처 충돌 보고

 

컴파일러는 이제 생성하는 일부 JVM 시그니처가 바이트 코드에서 충돌하게 될 때 오류를 출력합니다. (이전에는 ClassFormatError를 유발하거나 부모 클래스의 메서드를 우연히 덮어쓰게 할 수 있었습니다).

예를 들어, 다음과 같은 Kotlin 코드는 무해해 보이지만 JVM에서 오작동합니다:

 

val x = 1
fun getX() = 1

 

이제 Kotlin은 이 코드에 대해 다음과 같은 오류를 출력합니다:

 

Error:(1, 1) Kotlin: Platform declaration clash: 
The following declarations have the same JVM signature (getX()I):
    fun (): kotlin.Int
    fun getX(): kotlin.Int

 

이유는 x의 getter가 getX와 동일한 이름(getX)과 시그니처(매개변수 없이 Int를 반환)를 가지고 있기 때문입니다. JVM은 이러한 것을 클래스 파일에서 허용하지 않습니다.

다른 경우는 우연한 오버라이드입니다:

 

open class Base {
    fun getX() = 1
}

class Derived(val x: Int) : Base()

 

여기서 x의 getter는 getX()와 동일한 시그니처를 가지고 있지만, 이제 서로 다른 클래스에 속하므로 getter는 함수를 묵시적으로 덮어쓰게 됩니다. 따라서 Derived(2).getX()는 1이 아닌 2를 반환합니다.

이제 컴파일러는 이를 감지하며 다음과 같은 오류를 출력합니다:

 

Error:(1, 15) Kotlin: Accidental override: 
The following declarations have the same JVM signature (getX()I):
    fun (): kotlin.Int
    fun getX(): kotlin.Int

 

어째서 우리는 자동으로 함수 중 하나의 이름을 변경하지 않고 오류를 출력하는 대신에 이름을 변경하지 않을까요? 이에 대한 답은 Java 상호 운용성 영역에 있으며 (아무도 컴파일러가 만든 이름을 좋아하지 않습니다) 이진 호환성 영역에 있습니다 (사물의 이름은 무시하고 절대적으로 바뀌어서는 안 되며, 그렇지 않으면 모든 종속 코드가 예상치 못하게 고장날 것입니다). 반면에 원하는 JVM 이름을 platformName 주석으로 수동으로 지정할 수도 있습니다:

 

import kotlin.platform.platformName

val x = 1
[platformName("doGetX")]
fun getX() = 1

 

이 코드의 Kotlin 클라이언트에는 아무런 변화가 없지만, Java 클라이언트는 Kotlin 이름(getX) 대신 플랫폼 이름(doGetX)을 사용해야 합니다.

경고: 이것은 파괴적인 변경 사항이므로 기존 코드 중 일부를 수정해야 할 수 있습니다.

 

transient, synchronized 및 strictfp 지원

 

오랫동안 사용 가능했던 volatile 외에도 이제 transient, synchronized 및 strictfp를 주석으로 지원합니다. 의미론은 정확히 Java와 같습니다:

 

class FlagDemo {
  volatile var s = ""
  transient var list = listOf(1, 2, 3)
  strictfp fun math() { /* 여기에서 엄격한 부동 소수점 사용 가능 */ }
  synchronized fun sync() { /* 이 객체에 대해 동기화 */ }
}

 

생성된 코드 속도 향상

 

- 생성된 대리 속성 코드의 속도가 훨씬 빨라졌으며, 훨씬 적은 메모리 할당이 이루어집니다. 

 

- 또한 상수가 포함된 when 문은 전용 바이트 코드 명령어가 사용되기 때문에 더 빠릅니다.

 

JavaScript 지원


- 이제 JavaScript에서 Data Classes 지원이 가능합니다. 이는 JavaScript에서도 클래스를 data 주석으로 표시하여 toString, equals 및 hashCode를 자동으로 생성할 수 있다는 것을 의미합니다.

 

- 또한 Java와 마찬가지로 LinkedHashSet 및 LinkedHashMap도 JavaScript에서 지원됩니다.

 

언어 변경 사항


몇 가지 언어 변경 사항이 있으며, 모두 파괴적인 변경 사항이므로 주의가 필요합니다:

- 패키지 내의 private은 이제 모듈 내에서 현재 패키지에 대해서만 private을 의미합니다. 다른 모듈에서 같은 이름의 패키지는 이러한 정의를 볼 수 없습니다. 패키지는 중첩될 수 있으므로 privates는 동일 모듈 내의 하위 패키지에도 표시됩니다.

 

- 확장 속성에는 더 이상 백업 필드가 없습니다. 이는 어차피 권장되지 않았던 내용입니다.

 

- Kotlin이 “@” 및 “@@”을 레이블 이름으로 허용한다는 것을 발견했다면, 이제 더 이상 해당되지 않음을 알려드립니다.

 

표준 라이브러리에 추가 사항

 

표준 라이브러리도 일부 새로운 기능을 제공합니다:

- iterable의 정수를 받아 해당 위치의 요소를 포함하는 목록을 반환하는 slice() 함수 추가


- 문자열의 iterables (배열, 리스트 등)에서 작동하는 join()으로 이들을 하나의 문자열로 결합


- iterables 또는 모든 유형의 배열에서 작동하는 joinToString()으로 .map { it.toString() }.join()과 동일함


- Map은 이제 contains() 함수가 오버로딩되어 있어 "key in map"과 같은 것이 가능해짐


- 문자열은 collection과 유사한 함수를 활용할 수 있게 되었습니다.

 

1. substringBefore, substringBeforeLast 및 substringAfter, substringAfterLast와 같은 함수를 통해 특정 문자 또는 문자열 앞과 뒤의 문자열을 찾을 수 있습니다.

 

2. replaceBefore, replaceBeforeLast 및 replaceAfter, replaceAfterLast를 통해 특정 문자 또는 문자열 앞과 뒤의 문자열을 대체할 수 있습니다.

 

3. StringBuilder에 appendln 추가


4. String builder 확장을 사용하는 새로운 StringBuilder 함수로 다음과 같은 코드가 가능해집니다:

 

val information = StringBuilder {
	append("A first entry")
	appendln()
	appendln("Some other line")
}


- 이제 Iterable에는 변환을 적용한 두 컬렉션에서 요소를 병합하여 새 목록을 반환하는 merge 함수가 추가되었습니다.

 

public inline fun  
    Iterable.merge(: Iterable, transform: (T, R) -> V): List

 

zip과 다른 점은 merge가 다음과 같이 사용된다면:

 

collection.merge(anotherCollection) { (v1, v2) -> v }

 

zip은 다음과 같이 사용됩니다:

 

val zip = merge { v1, v2 -> v1 to v2 }

 

합집합, 교집합 및 차집합과 같은 집합 연산 지원도 추가되었습니다. 자세한 내용은 API 문서를 참조하세요.

 

IntelliJ IDEA 14 EAP 지원 및 더 많은 내용


최근에 공개된 IntelliJ IDEA 14 EAP를 지원하는 것 외에도, 우리는 새로운 IDE 기능들이 많이 추가되었습니다:


디버거 개선사항


- 스마트 스텝 인투 기능 추가
- 평가식 표현을 지원합니다.


새로운 리팩터링 기능


- 최상위 선언을 새 파일로 이동

 


- 함수 추출. 아직 몇몇 개선이 필요하지만, 여러 경우에 작동합니다.

 

- 로컬 함수 추출

 

새로운 의도(Intentions)


우리는 의도를 좋아합니다. 의도는 문제를 빠르게 해결하는 데 도움을 주는 것뿐만 아니라 언어와 프레임워크 구조를 배우고 발견하는 데도 도움이 된다고 믿습니다. 이번 릴리스에서는 DeMorgan의 법칙과 같은 14개의 새로운 의도를 추가했습니다. 이에는 다음과 같은 내용이 포함됩니다:


- DeMorgan의 법칙

 


- 이항식 변환


- 이항식 뒤집기


- if 조건 분할


- 연산자 기호로 대체


- 전통적 할당으로 대체


- 중위 호출로 대체

 


- 중위 표현


- 부울 표현식 단순화


- 명시적/암시적 유형 인자 추가/제거


- assert를 throw가 있는 if로 변환하고 그 반대로 변환


- if를 ?:로 변환하고 그 반대로 변환


- !!를 if로 변환하고 그 반대로 변환


- 람다에서 유형을 명시적/암시적으로 만들기


- forEach { }를 for 루프로 변환하고 그 반대로 변환


- 문자열 연결을 보간으로 변환하고 그 반대로 변환

 


스마트 완성 개선


스마트 완성이 개선되었습니다. 다음과 같은 내용을 지원합니다:

- 오버로드된 메서드에 대한 더 똑똑한 완성.


- callable references는 이제 스마트 완성에서 표시되며, ::함수이름에 대한 완성을 받을 수 있습니다.

 

- 람다 내의 완성.


- nullable 멤버에 대한 !! 및 ?: 완성


- 익명 클래스(object 표현식)에서의 완성


- when() 문의 상수에 대한 완성


기타 IDE 개선사항


위의 내용 외에도 다음과 같은 개선이 이루어졌습니다:

- 개선된 포매터


- 구조 뷰에서 슈퍼타입의 멤버 표시


- 하이라이팅 및 완성 속도 향상


- Java to Kotlin 변환기 개선


마지막으로, 최신 버전의 Gradle 및 Android Studio(0.10, 0.11 및 0.12)도 지원합니다.

최신 릴리스를 다운로드하고, 항상 의견을 환영합니다! IntelliJ EAP 14는 이미 최신 플러그인을 포함하고 있으며, 플러그인은 우리의 플러그인 저장소에서 찾을 수 있습니다.

 

원문

 

https://blog.jetbrains.com/kotlin/2014/07/m8-is-out/

반응형

댓글