수색…
통사론
- fun TypeName.extensionName (params, ...) {/ * body * /} // 선언문
- fun <T : Any> TypeNameWithGenerics <T> .extensionName (params, ...) {/ * body * /} // 제네릭 선언
- myObj.extensionName (args, ...) // 호출
비고
확장은 정적으로 해결됩니다. 즉, 사용할 확장 메소드는 액세스하는 변수의 참조 유형에 의해 결정됩니다. 런타임에 변수의 유형이 무엇인지는 중요하지 않습니다. 동일한 확장 메서드가 항상 호출됩니다. 이는 확장 메소드 선언 이 실제로 수신자 유형에 멤버를 추가하지 않기 때문입니다.
최상위 확장
최상위 확장 메소드는 클래스 내에 포함되어 있지 않습니다.
fun IntArray.addTo(dest: IntArray) {
for (i in 0 .. size - 1) {
dest[i] += this[i]
}
}
위의 확장 메서드는 IntArray
형식에 대해 정의됩니다. 확장 메서드가 정의 된 개체 ( 수신기 라고 함)는 키워드 this
사용하여 액세스 this
.
이 확장은 다음과 같이 호출 할 수 있습니다.
val myArray = intArrayOf(1, 2, 3)
intArrayOf(4, 5, 6).addTo(myArray)
잠재적 인 핏톨 : 확장이 정적으로 해결됨
호출 할 확장 메소드는 액세스 할 변수의 참조 유형에 따라 컴파일 타임에 결정됩니다. 런타임에 변수의 유형이 무엇인지는 중요하지 않으며 동일한 확장 메서드가 항상 호출됩니다.
open class Super
class Sub : Super()
fun Super.myExtension() = "Defined for Super"
fun Sub.myExtension() = "Defined for Sub"
fun callMyExtension(myVar: Super) {
println(myVar.myExtension())
}
callMyExtension(Sub())
변수 myVar
의 선언 된 타입이 Super
이기 때문에 위의 예제는 "Defined for Super"
출력 할 것입니다.
사람이 읽을 수있는 문자열을 렌더링하기 위해 긴 확장 샘플
사람이 읽을 수있는 문자열을 렌더링하기 위해 Int
또는 Long
형식의 값을 지정하면 다음과 같습니다.
fun Long.humanReadable(): String {
if (this <= 0) return "0"
val units = arrayOf("B", "KB", "MB", "GB", "TB", "EB")
val digitGroups = (Math.log10(this.toDouble())/Math.log10(1024.0)).toInt();
return DecimalFormat("#,##0.#").format(this/Math.pow(1024.0, digitGroups.toDouble())) + " " + units[digitGroups];
}
fun Int.humanReadable(): String {
return this.toLong().humanReadable()
}
다음과 같이 쉽게 사용됩니다.
println(1999549L.humanReadable())
println(someInt.humanReadable())
샘플 Java 7+ Path 클래스 확장
확장 메소드의 일반적인 사용 사례는 기존 API를 개선하는 것입니다. 다음은 Java 7+ Path
클래스에 exist
, notExists
및 deleteRecursively
를 추가 exist
예제입니다.
fun Path.exists(): Boolean = Files.exists(this)
fun Path.notExists(): Boolean = !this.exists()
fun Path.deleteRecursively(): Boolean = this.toFile().deleteRecursively()
이 예제에서 이제 호출 할 수 있습니다.
val dir = Paths.get(dirName)
if (dir.exists()) dir.deleteRecursively()
확장 기능을 사용하여 가독성 향상
Kotlin에서는 다음과 같은 코드를 작성할 수 있습니다.
val x: Path = Paths.get("dirName").apply {
if (Files.notExists(this)) throw IllegalStateException("The important file does not exist")
}
그러나의 사용 apply
당신의 의도 등이 명확하지 않다. 때로는 비슷한 확장 기능을 만들어 액션의 이름을 바꾸고 좀 더 자명하게 만드는 것이 더 분명합니다. 이것은 손에 닿지 않아야하지만, 검증과 같은 매우 일반적인 행동에 대해서는 허용되지 않아야합니다.
infix inline fun <T> T.verifiedBy(verifyWith: (T) -> Unit): T {
verifyWith(this)
return this
}
infix inline fun <T: Any> T.verifiedWith(verifyWith: T.() -> Unit): T {
this.verifyWith()
return this
}
이제 코드를 다음과 같이 작성할 수 있습니다.
val x: Path = Paths.get("dirName") verifiedWith {
if (Files.notExists(this)) throw IllegalStateException("The important file does not exist")
}
이제는 사람들이 람다 매개 변수 내에서 무엇을 기대해야하는지 알려줍니다.
verifiedBy
에 대한 유형 매개 변수 T
는 T: Any?
와 동일합니다 T: Any?
이는 null 허용 유형에서도 해당 버전의 확장을 사용할 수 있음을 의미합니다. verifiedWith
되었지만 nullable이 필요하지 않습니다.
ISO 형식 문자열을 렌더링하는 Java 8 Temporal 클래스를 확장 한 샘플
이 선언으로 :
fun Temporal.toIsoString(): String = DateTimeFormatter.ISO_INSTANT.format(this)
이제 간단하게 다음 작업을 할 수 있습니다.
val dateAsString = someInstant.toIsoString()
컴패니언 객체에 대한 확장 함수 (정적 함수의 모양)
클래스를 확장하려는 경우 - 예를 들어, Something
클래스가 정적 fromString
함수 fromString
추가하는 경우와 같이 정적 함수 인 경우 클래스에 컴패니언 객체 가 있고 컴패니언 객체 에 확장 함수가 선언 된 경우에만 작동합니다 :
class Something {
companion object {}
}
class SomethingElse {
}
fun Something.Companion.fromString(s: String): Something = ...
fun SomethingElse.fromString(s: String): SomethingElse = ...
fun main(args: Array<String>) {
Something.fromString("") //valid as extension function declared upon the
//companion object
SomethingElse().fromString("") //valid, function invoked on instance not
//statically
SomethingElse.fromString("") //invalid
}
지연 확장 속성 해결 방법
계산하는 데 비용이 많이 드는 확장 속성을 만들려고한다고 가정합니다. 따라서 Kotlin 호 KT-9686 및 KT-13053 에서 설명한대로 지연 속성 대리자 를 사용하여 계산을 캐시하고 현재 인스턴스 ( this
)를 참조하려고합니다. 그러나 여기 에는 공식적인 해결 방법 이 있습니다 .
이 예제에서 확장 속성은 color
입니다. 그것은 lazy
가 필요하지 않으므로 this
사용할 수있는 명시적인 colorCache
를 사용합니다 :
class KColor(val value: Int)
private val colorCache = mutableMapOf<KColor, Color>()
val KColor.color: Color
get() = colorCache.getOrPut(this) { Color(value, true) }
보다 쉽게 참조 할 수있는 확장 코드에서보기
뷰를 만든 후에는 더 이상 상용구가 아닌 참조 뷰에 대한 확장을 사용할 수 있습니다.
Anko 도서관의 독창적 인 아이디어
확장 프로그램
inline fun <reified T : View> View.find(id: Int): T = findViewById(id) as T
inline fun <reified T : View> Activity.find(id: Int): T = findViewById(id) as T
inline fun <reified T : View> Fragment.find(id: Int): T = view?.findViewById(id) as T
inline fun <reified T : View> RecyclerView.ViewHolder.find(id: Int): T = itemView?.findViewById(id) as T
inline fun <reified T : View> View.findOptional(id: Int): T? = findViewById(id) as? T
inline fun <reified T : View> Activity.findOptional(id: Int): T? = findViewById(id) as? T
inline fun <reified T : View> Fragment.findOptional(id: Int): T? = view?.findViewById(id) as? T
inline fun <reified T : View> RecyclerView.ViewHolder.findOptional(id: Int): T? = itemView?.findViewById(id) as? T
용법
val yourButton by lazy { find<Button>(R.id.yourButtonId) }
val yourText by lazy { find<TextView>(R.id.yourTextId) }
val yourEdittextOptional by lazy { findOptional<EditText>(R.id.yourOptionEdittextId) }