이전에 작성했던 내용 이 수정 불가로 변경되어 버렸다.
2주 정도 지나면 변경이 안되는 건가??
우선 새로 글을 파서 정리한다.
Day 13. 코틀린 표준 라이브러리
require 함수는 조건 값이 false라면 IllegalArgumentExeption을 발생 시킨다.
fun setName(name: String) {
// calling setName("") throws IllegalArgumentException
require(name.isNotEmpty()) { "Invalid name" }
}
public inline fun require(value: Boolean, lazyMessage: () -> Any): Unit {
if (!value) {
val message = lazyMessage()
throw IllegalArgumentException(message.toString())
}
}
check 함수는 조건값이 false라면 IllegalStateException을 발생 시킨다.
fun User.logOut() {
// When not authenticated, throws IllegalStateExeption
check(isAuthenticated) { "User $email is not authenticated" }
isAuthenticated = false
}
public inline fun check(value: Boolean, lazyMessage: () -> Any): Unit {
if (!value) {
val message = lazyMessage()
throw IllegalStateException(message.toString())
}
}
Day 14. Inline Functions
inline 키워드를 사용하면 컴파일 시 해당 함수 구현 내용으로 전체가 대체된다. 람다 기반 API의 오버헤드를 0로 만든다.
// define an inline function that takes a function argument
inline fun onlyIf(check: Boolean, operation: () -> Unit) {
if (check) {
operation()
}
}
// call it like this
onlyIf(shouldPrint) { // call: pass operation as a lambda
println("Hello, Kotlin")
}
// which will be inlined to this
if (shouldPrint) { // execution: no need to create lambda
println("Hello, Kotlin")
}
Day 15. Package-Level Functions
코틀린에서는 파일내에 함수를 정의할 수 있다.(꼭 클래스 안에 넣을 필요가 없다.) 파일에 정의한 함수는 컴파일 시점에 파일명+Kt.class로 컴파일 된다 자바에서 이를 사용하려면 파일명+Kt 클래스를 사용하면 된다.
Kt가 보기 싫다면 @file:JvmName("Name") 어노테이션을 사용하면, 원하는 클래스(?) 명으로 변경 할 수 있다.
// File: ShapesGenerator.kt
package com.shapes
fun generateSquare() = Square()
fun generateTriangle() = Triangle()
// Java usage:
ShapeGeneratorKt.getnerateSquare()
---
// File: ShapesGenerator.kt
@file:JvmName("ShapesGenerator")
package com.shapes
fun generateSquare() = Square()
fun generateTriangle() = Triangle()
// Java usage:
ShapeGenerator.getnerateSquare()
Day 16. Reified type parameters.
reified 를 사용하면 파라메터/리턴타입으로 전달되는 타입을 함수 내부에서 사용할 수 있다.
// the old way
val alarmManager = context.getSystemService(AlarmManager::class.java)
// the reified way
val alarmManager: AlarmManager = context.systemService()
// the magic from Androiod KTX... whti "real" type T
inline fun <reified T> Context.systemService() = getSystemService(T::class.java)
Day 17. Delegation, Delegated Properties
- https://kotlinlang.org/docs/reference/delegation.html
- http://kotlinlang.org/docs/reference/delegated-properties.html
by 키워드를 사용하여 property getter/setter의 동작을 위임할 수 있다.
class MyAnimatingView : View( /* ... */ ) {
// delegated property. Uses the getter and setter defied in InvalidateDelegate
var foregroundX by InvalidateDelegate(0f)
}
// A View Delegate which invalidates View.postInvalidateOnAnimation when set.
class InvalidateDelegate<T : any>(var value: T) {
operator fun getValue(thisRef: View, property: KProperty<*>) = value
operator fun setValue(thisRef: View, property: KProperty<*>) {
this.value = value
thisRef.postInvalidateOnAnimation()
}
}
Day 18. Extension Functions.
- https://kotlinlang.org/docs/reference/extensions.html#extension-functions
- Example : https://github.com/android/android-ktx/blob/master/src/main/java/androidx/core/net/Uri.kt#L28
유틸 클래스는 그만 만들고, 확장 함수를 정의하자!!!
확장 함수는 멤버함수가 아니다.
원래 클래스를 어떠한 방식으로든 수정하지 않는다.
확장 함수는 컴파일 타임에 정적 타입 정보로 사용된다.
open class C class D: C() fun C.foo() = "c" fun D.foo() = "d" fun printFoo(c: C) { println(c.foo()) } // 출력 결과는 d 가 아닌 c 이다. printFoo(D())
정적 함수로 컴파일 된다.
확장 함수를 오버라이드 하지 말자.
// Extend String with toUri
inline fun String.toUri(): Uri = Uri.parse(this)
// And call it on any String!
val myUri = "www.developer.android.com".toUri()
Day 19. Android KTX
Android KTX에는 여러가지 유용한 함수들이 포함되어 있다. 이러한 함수들은 불필요한 보일러플레이트 코드들을 제거할 수 있게 해준다.
// get a drawable from resources
val myDrawable = ContextCompat.getDrawable(context, R.drawable.icon)
// convert the drawable to a bitmap
val bitmap = myDrawable.toBitmap()
/**
* Return a [Bitmap] representation of this [Drawable].
*
* If this instance is a [BitmapDrawable] and the [width], [height], and [config] match, the
* underlying [Bitmap] instance will be returned directly. If any of those three properties differ
* then a new [Bitmap] is created. For all other [Drawable] types, a new [Bitmap] is created.
*
* @param width Width of the desired bitmap. Defaults to [Drawable.getIntrinsicWidth].
* @param height Height of the desired bitmap. Defaults to [Drawable.getIntrinsicHeight].
* @param config Bitmap config of the desired bitmap. Null attempts to use the native config, if
* any. Defaults to [Config.ARGB_8888] otherwise.
*/
fun Drawable.toBitmap(
@Px width: Int = intrinsicWidth,
@Px height: Int = intrinsicHeight,
config: Config? = null
): Bitmap {
if (this is BitmapDrawable) {
if (config == null || bitmap.config == config) {
// Fast-path to return original. Bitmap.createScaledBitmap will do this check, but it
// involves allocation and two jumps into native code so we perform the check ourselves.
if (width == intrinsicWidth && height == intrinsicHeight) {
return bitmap
}
return Bitmap.createScaledBitmap(bitmap, width, height, true)
}
}
val (oldLeft, oldTop, oldRight, oldBottom) = bounds
val bitmap = Bitmap.createBitmap(width, height, config ?: Config.ARGB_8888)
setBounds(0, 0, width, height)
draw(Canvas(bitmap))
setBounds(oldLeft, oldTop, oldRight, oldBottom)
return bitmap
}
Day 20. Sequence
- https://kotlinlang.org/api/latest/jvm/stdlib/kotlin.sequences/index.html
- https://kotlinlang.org/docs/reference/coroutines.html#generators-api-in-kotlincoroutines
Java8의 Stream과 비슷하게 kotlin에는 Sequence가 있다.
Sequence는 사용되기 전까지 아무런 동작도 하지 않는다. (lazy)
map이나 filter를 사용할때 iterator에 사용하는 것과 차이가 발생하는데 iterator와는 달리 중간 연산의 결과물을 생성하지 않는다.
val sequence = List(50) { it * 5 }.asSequence()
sequence.map { it * 2 } // lazy (iterate 1 element at a time
.filter { it % 3 == 0 } // lazy (iterate 1 element at a time
.map { it + 1 } // lazy (iterate 1 element at a time
.toList() // eager (iterate all element)
// make a sequence from a list
list.asSequence().filter { it == "lazy" }
// or, reach infinity functionally
// 1부터 1씩 증가하는 무한의 리스트를 표현하려면 아래와 같은 시퀀스를 만들면 된다.
val natural = generateSequence(1) { it + 1 }
// or, cooperate with coroutines
// 코루틴의 buildSequence를 사용해서도 시퀀스를 만들 수 있다.
// 아래는 무한의 0 리스트를 나타낸다.
// take 함수를 통해 시퀀스의 한계 index를 지정할 수 있다.
val zeros = buildSequence {
while (true) {
yield(0)
}
}
zeros.take(10).forEach { println("$it") }
// 0 1 2 3 4 5 6 7 8 9 10 의 시퀀스
val lazySeq = buildSequence {
yield(0)
yieldAll(1..10)
}
lazySeq.forEach { print("$it ") }