2013년 6월 6일
코틀린 M5.3은 IntelliJ IDEA 13에 대한 지원 및 새로운 기능들을 제공합니다. 우리는 강력한 런타임 지원을 포함한 여러 프레임워크 활성화 기능으로 나아가고 있습니다. 이 마일스톤은 그 방향으로의 첫걸음입니다.
IntelliJ IDEA 12.1과 13
IntelliJ IDEA 13의 첫 조기 액세스 버전이 출시되고 있으며, 이에 호환되는 코틀린 플러그인을 제공합니다. 이것은 EAP이므로 본인의 책임 하에 사용해야 합니다. 물론, 예전의 IntelliJ IDEA 12.1도 지원됩니다.
참고: Android Studio에서의 코틀린 지원에 대한 뉴스가 곧 발표될 예정입니다.
많은 개선 사항들
M5.3은 컴파일러와 IDE 모두에서 많은 개선을 가져다줍니다. 컴파일러에서는 주로 성능에 관심을 가지고 있으며, 이는 점진적으로 개선되고 있습니다. IDE는 새로운 퀵 픽스와 리팩토링을 제공하며, 이 중 일부는 아래에서 설명됩니다. 이제 프로퍼티를 오버라이딩하는 프로퍼티로 이동할 수 있습니다(왼쪽 선에서 아이콘을 참조하세요). 에디터는 KDoc의 문법을 인식합니다(이 풀 리퀘스트 덕분에) ... 하지만 먼저, 오래 기다렸던 새로운 언어 기능, 또는 뜻밖의 새로운 언어 기능에 대해 얘기해 봅시다.
새로운 언어 기능: 위임 프로퍼티
다음과 같은 기능 요청을 자주 받습니다:
우리는 종종 다음과 같은 기능 요청을 받습니다:
- 지연 초기화 프로퍼티: 값이 첫 접근시에만 계산됩니다.
- 관찰 가능한 프로퍼티: 이 프로퍼티에 변경이 생기면 리스너들이 알림을 받습니다.
- 프로퍼티를 맵에서 저장, 각각 별도의 필드가 아님.
- <내가 좋아하는 프로퍼티 의미> 지원 ...
이러한 요청에 대한 하나의 방법은 사용자가 고생해야 한다고 하는 것일 수 있습니다. 또 다른 방법은 언어 수준에서 다양한 종류의 프로퍼티를 지원하는 것입니다. 우리는 이 두 가지 접근 방식 중 어느 것도 좋아하지 않습니다: 한편으로는 너무 많은 불행한 사용자들이 있고, 다른 한편으로는 너무 많은 즉흥적인 기능들이 있기 때문입니다. 그래서 우리는 세 번째 방법을 선택했습니다: 이러한 요청을 모두 커버할 수 있는 통합된 메커니즘을 지원하므로, 특정 종류의 프로퍼티는 라이브러리에서 구현될 수 있습니다, 언어를 변경할 필요 없이.
위임 프로퍼티를 만나보세요:
class Example {
var p: String by Delegate()
}
여기에는 새로운 문법이 있습니다: "val <프로퍼티 이름>: <타입> by <표현식>"이라고 말할 수 있습니다. by 다음에 오는 표현식이 위임자(delegate)입니다, 왜냐하면 해당 프로퍼티에 대응하는 get() 및 set() 메서드가 이 위임자에게 위임되기 때문입니다. 프로퍼티 위임자는 어떠한 인터페이스도 구현할 필요가 없지만, 호출될 get() 및 set() 메서드를 제공해야 합니다. 예를 들어:
class Delegate() {
fun get(thisRef: Any?, prop: PropertyMetadata): String {
return "$thisRef, thank you for delegating '${prop.name}' to me!"
}
fun set(thisRef: Any?, prop: PropertyMetadata, value: String) {
println("$value has been assigned")
}
}
p에서 읽을 때, Delegate의 get() 함수가 호출됩니다. 따라서 첫 번째 매개변수는 p를 읽는 객체이고 두 번째 매개변수는 p 자체에 대한 설명을 가집니다(예: 그 이름을 가져올 수 있습니다). 예를 들어:
val e = Example()
println(e.p)
이것은 "Example@33a17727, 'p'를 나에게 위임해주셔서 감사합니다!"라고 출력합니다. 마찬가지로, 우리가 p에 값을 할당하면 set() 함수가 호출됩니다. 첫 두 매개변수는 동일하고, 세 번째는 할당되는 값을 가지고 있습니다.
e.p = "NEW"
이것은 "Example@33a17727에 'p'에 'NEW'가 할당되었습니다"라고 출력합니다.
아마도 이 메커니즘으로 lazy나 observable과 같은 것들을 어떻게 구현할 수 있는지 이미 알고 있을 것입니다. 연습 차원에서 한 번 시도해보세요, 하지만 대부분은 이미 표준 라이브러리에서 구현되어 있습니다.
kotlin.properties.Delegates 객체에는 가장 유용한 것들이 들어 있습니다. lazy부터 시작해봅시다:
import kotlin.properties.Delegates
class LazySample {
val lazy: String by Delegates.lazy {
println("computed!")
"Hello"
}
}
Delegates.lazy()는 지연된 속성을 구현하는 대리자를 반환하는 함수입니다: 첫 번째 get() 호출은 lazy()에 전달된 람다 표현식을 실행하고 결과를 기억합니다. 그 후의 get() 호출은 단순히 기억된 결과를 반환합니다. 스레드 안전성이 필요하다면 대신 blockingLazy()를 사용하세요: 이것은 값이 하나의 스레드에서만 계산되고 모든 스레드가 동일한 값을 볼 것임을 보장합니다.
이제 observable에 대해 알아봅시다:
class User {
var name: String by Delegates.observable("<no name>") {
d, old, new ->
println("$old -> $new")
}
}
observable() 함수는 두 개의 인자를 받습니다: 초기값과 수정을 위한 핸들러입니다. 핸들러는 'name'에 값을 할당할 때마다 호출되며, 세 개의 매개변수를 가집니다: 할당되는 속성, 이전 값, 그리고 새 값입니다. 할당을 '거부'할 수 있도록 하려면 observable() 대신 vetoable()을 사용하세요.
다음은 다소 예상치 못한 상황일 수 있습니다: 사용자들은 종종 non-null var를 가지고 있을 때, 생성자에서 적절한 값을 할당할 수 없는 경우(즉, 나중에 할당되어야 함) 어떻게 해야 하는지 묻습니다. Kotlin에서는 초기화되지 않은 비추상 속성을 가질 수 없습니다:
class Foo {
var bar: Bar // error: must be initialized
}
null로 초기화할 수 있지만, 그렇게 하면 속성에 접근할 때마다 매번 확인해야 합니다. 이제 이를 처리할 대리자가 있습니다:
class Foo {
var bar: Bar by Delegates.notNull()
}
이 속성에서 값을 쓰기 전에 읽으면 예외가 발생합니다. 첫 번째 할당 이후에는 예상대로 작동합니다.
마지막으로 보여줄 것은 맵에 저장된 속성입니다. 이것은 JSON을 파싱하거나 다른 "동적" 작업을 수행하는 같은 애플리케이션에서 자주 나타납니다:
class User(val map: Map<String, Any?>) {
val name: String by Delegates.mapVal(map)
val age: Int by Delegates.mapVal(map)
}
이 예시에서는 생성자가 맵을 받습니다:
val user = User(mapOf(
"name" to "John Doe",
"age" to 25
))
대리자는 이 맵에서 값들을 가져옵니다(문자열 키를 통해 - 속성의 이름들):
println(user.name) // Prints "John Doe"
println(user.age) // Prints 25
물론 var도 가질 수 있습니다(mapVar() 함수를 사용하여), 이것은 할당 시에 맵을 수정할 것입니다(읽기 전용 Map 대신 MutableMap이 필요하다는 점을 주목하세요).
다른 사용 사례도 있고, 이러한 것들에 대한 수많은 개선점이 있을 것입니다. 상상하고, 실험하고, 즐기세요! ;)
첫걸음 SAM 변환
지난 번에 SAM 생성자를 소개했습니다. 물론 이것만으로는 충분하지 않으므로, 완전한 SAM 변환에 대해 작업 중입니다. 이 기능은 아직 완성되지 않았지만, 다음과 같은 간단한 경우에는 이미 사용할 수 있습니다:
SwingUtilities.invokeLater {
button.setVisible(true)
}
여러분에게 상기시켜 드리면, SAM 변환은 Java 8이 람다에 사용하는 것입니다: 하나의 (추상) 메서드만 가진 인터페이스가 있을 때, 예를 들어 Comparator나 Runnable 같은 것, 이 인터페이스의 인스턴스가 예상되는 곳에 람다를 전달할 수 있습니다 (이 예에서는 Runnable 대신 람다를 전달합니다). Kotlin은 이것을 언어 기능으로 가지고 있지 않습니다(적절한 함수 타입을 가진 언어에서는 필요하지 않기 때문에), 따라서 이것은 Java 클래스에 대해서만 작동할 것입니다.
“호출 가능한 참조”의 첫걸음
우리가 작업 중인 또 다른 것은 "호출 가능한 참조(Callable References)" 또는 "기능 리터럴(Feature Literals)"이라고도 하는데, 이는 명명된 함수나 속성을 값으로 전달할 수 있는 능력입니다. 사용자들은 종종 "foo() 함수가 있을 때, 이것을 어떻게 인자로 전달하나요?"라고 물어봅니다. 답은 " '::'로 접두사를 붙입니다" 입니다. 예를 들어:
fun isOdd(x: Int) = x % 2 != 0
val numbers = listOf(1, 2, 3)
println(numbers.filter(::isOdd)) // [1, 3]을 출력
여기서 "::isOdd"는 함수 타입 "(Int) -> Boolean"의 값이며, 이를 필터링 술어로 전달할 수 있습니다. 다른 예는 다음과 같습니다:
fun compose<A, B, C>(f: (B) -> C, g: (A) -> B): (A) -> C {
return {x -> f(g(x))}
}
이 함수는 그것에 전달된 두 함수의 합성을 반환합니다: compose(f, g) = f(g(*)). 이제 이것을 호출 가능한 참조에 적용할 수 있습니다:
fun length(s: String) = s.size
val oddLength = compose(::isOdd, ::length)
val strings = listOf("a", "ab", "abc")
println(strings.filter(oddLength)) // "[a, abc]"를 출력
클래스의 멤버를 사용하려면 이를 구분해야 하고, 결과는 "확장 함수" 타입이 될 것입니다. 예를 들어, String::toCharArray는 String 타입에 대한 확장 함수를 제공합니다.
이것은 초기 작업 단계이므로 많은 것들이 아직 작동하지 않습니다. 예를 들어, 오버로드 구별, 타입 추론, 속성 지원 등입니다. 결국 이 기능은 완전한 타입-안전 리플렉션으로 발전할 것이지만, 오늘날에는 이에 대한 작업을 시작하기만 했습니다.
시그니처 변경 리팩터링
함수 매개변수를 추가/제거/재정렬할 때 많은 호출 위치를 업데이트해야 하는 경우 지루할 수 있습니다. 그래서 IDE들은 "시그니처 변경(Change Signature)" 리팩터링을 제공합니다. 함수나 생성자 위에 커서를 두고 Ctrl+F6을 누르면 (Mac에서는 Cmd+F6), 다음과 같은 대화상자가 나타납니다:
“타입 불일치” 등에 대한 빠른 수정
Stanford University가 주도하는 오픈 소스 멘토링 프로그램의 학생들인 Jack Zhou, Michał Sapalski, Wojciech Łopata 등의 기여로 인해, 이제 많은 멋진 빠른 수정 기능을 얻을 수 있습니다. 예를 들어, 타입 불일치 오류가 발생하면 Alt+Enter를 눌러 코드를 수정할 제안을 받을 수 있습니다:
타입 변경, 이름 변경, 재정렬 또는 매개변수 삭제를 하면 모든 호출 위치가 그에 따라 업데이트됩니다.
코드 변환
코드의 동등한 형태 간에 변환을 수행하는 유용한 IDE 액션의 또 다른 그룹이 있습니다. 예를 들면: http://www.youtube.com/watch?v=Cfwq-pYtiDY
문장이나 선언을 이동하려면 Ctrl+Shift+Up/Down을 사용할 수도 있습니다:
http://www.youtube.com/watch?v=RRRROZc3-2g
설치
보통처럼, 새 플러그인은 우리의 플러그인 저장소에서 설치할 수 있습니다.
좋은 Kotlin 사용을 바랍니다!
원문
https://blog.jetbrains.com/kotlin/2013/06/kotlin-m5-3-idea-13-delegated-properties-and-more/
'Kotlin > Release Notes' 카테고리의 다른 글
[Kotlin Release Notes] M6.1 Released (0) | 2023.08.28 |
---|---|
[Kotlin Release Notes] Kotlin M6 is here! (0) | 2023.08.27 |
[Kotlin Release Notes] Kotlin M5.2: IntelliJ IDEA 12.1 and Gradle (0) | 2023.08.27 |
[Kotlin Release Notes] Kotlin M5.1 (0) | 2023.08.27 |
[Kotlin Release Notes] Kotlin M5 is Out! (0) | 2023.08.27 |
댓글