Buscar..


Sintaxis

  • fun TypeName.extensionName (params, ...) {/ * body * /} // Declaración
  • fun <T: Any> TypeNameWithGenerics <T> .extensionName (params, ...) {/ * body * /} // Declaración con genéricos
  • myObj.extensionName (args, ...) // invocación

Observaciones

Las extensiones se resuelven estáticamente . Esto significa que el método de extensión que se utilizará está determinado por el tipo de referencia de la variable a la que está accediendo; No importa cuál sea el tipo de la variable en el tiempo de ejecución, siempre se llamará al mismo método de extensión. Esto se debe a que declarar un método de extensión en realidad no agrega un miembro al tipo de receptor .

Extensiones de nivel superior

Los métodos de extensión de nivel superior no están contenidos dentro de una clase.

fun IntArray.addTo(dest: IntArray) {
    for (i in 0 .. size - 1) {
        dest[i] += this[i]
    }
}

Encima se define un método de extensión para el tipo IntArray . Tenga en cuenta que se accede al objeto para el que se define el método de extensión (llamado receptor ) con la palabra clave this .

Esta extensión se puede llamar así:

val myArray = intArrayOf(1, 2, 3)
intArrayOf(4, 5, 6).addTo(myArray)

Posible trampa: las extensiones se resuelven de forma estática

El método de extensión a llamar se determina en tiempo de compilación en función del tipo de referencia de la variable a la que se accede. No importa cuál sea el tipo de la variable en el tiempo de ejecución, siempre se llamará al mismo método de extensión.

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())

El ejemplo anterior imprimirá "Defined for Super" , porque el tipo declarado de la variable myVar es Super .

Muestra que se extiende por mucho tiempo para representar una cadena humana legible

Dado cualquier valor de tipo Int o Long para representar una cadena legible por humanos:

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

Luego se usa fácilmente como:

println(1999549L.humanReadable())
println(someInt.humanReadable())

Ejemplo de extensión de Java 7+ clase de ruta

Un caso de uso común para los métodos de extensión es mejorar una API existente. Aquí hay ejemplos de notExists agregar exist , notExists y deleteRecursively a la clase Java 7+ Path :

fun Path.exists(): Boolean = Files.exists(this)
fun Path.notExists(): Boolean = !this.exists()
fun Path.deleteRecursively(): Boolean = this.toFile().deleteRecursively()

Que ahora se puede invocar en este ejemplo:

val dir = Paths.get(dirName)
if (dir.exists()) dir.deleteRecursively()

Usando funciones de extensión para mejorar la legibilidad

En Kotlin puedes escribir código como:

val x: Path = Paths.get("dirName").apply { 
    if (Files.notExists(this)) throw IllegalStateException("The important file does not exist")
}

Pero el uso de apply no es tan claro en cuanto a su intención. A veces es más claro crear una función de extensión similar para, en efecto, cambiar el nombre de la acción y hacerla más evidente. No se debe permitir que esto se salga de control, pero para acciones muy comunes como la verificación:

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
}

Ahora puedes escribir el código como:

val x: Path = Paths.get("dirName") verifiedWith {
    if (Files.notExists(this)) throw IllegalStateException("The important file does not exist")
}

Que ahora la gente sepa qué esperar dentro del parámetro lambda.

Tenga en cuenta que el parámetro de tipo T para verifiedBy es el mismo que T: Any? lo que significa que incluso los tipos anulables podrán usar esa versión de la extensión. Aunque verifiedWith requiere no anulable.

Ejemplo de extensión de clases temporales de Java 8 para representar una cadena con formato ISO

Con esta declaración:

fun Temporal.toIsoString(): String = DateTimeFormatter.ISO_INSTANT.format(this)

Ahora puedes simplemente:

val dateAsString = someInstant.toIsoString()

Funciones de extensión a objetos complementarios (apariencia de funciones estáticas)

Si desea extender una clase como-si es una función estática, por ejemplo, para la clase Something agregue la función de aspecto estático desde la fromString , esto solo puede funcionar si la clase tiene un objeto complementario y la función de extensión se ha declarado en el objeto complementario. :

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
}

Solución perezosa de la propiedad de la extensión

Supongamos que desea crear una propiedad de extensión que sea costosa de computar. Así que le gustaría almacenar en caché el cálculo, utilizando el delegado propiedad perezosa y se refieren a instancia actual ( this ), pero no puede hacerlo, como se explica en el Kotlin emite KT-9686 y KT-13053 . Sin embargo, hay una solución oficial proporcionada aquí .

En el ejemplo, la propiedad de extensión es color . Utiliza un colorCache explícito que puede usarse con this ya que no es necesario lazy :

class KColor(val value: Int)

private val colorCache = mutableMapOf<KColor, Color>()

val KColor.color: Color
    get() = colorCache.getOrPut(this) { Color(value, true) }

Extensiones para una referencia más fácil Vista desde el código

Puede usar extensiones para la vista de referencia, no más repeticiones después de crear las vistas.

La idea original es de la biblioteca de Anko

Extensiones

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

Uso

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
Licenciado bajo CC BY-SA 3.0
No afiliado a Stack Overflow