수색…


통사론

  • 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 , notExistsdeleteRecursively 를 추가 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 에 대한 유형 매개 변수 TT: 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-9686KT-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) }


Modified text is an extract of the original Stack Overflow Documentation
아래 라이선스 CC BY-SA 3.0
와 제휴하지 않음 Stack Overflow