수색…


통사론

  • 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) }


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