Interfaces En Kotlin

En este tutorial verás cómo definir e implementar interfaces en Kotlin para proveer comportamiento adicional a tus clases.

Definir Una Interfaz

Las interfaces te permiten definir tipos cuyos comportamientos pueden ser compartidos por varias clases que no están relacionadas, con el fin de crear instancias se adopten a un dominio específico.

Para definir una interfaz en Kotlin usa la palabra reservada interface seguido del nombre de la misma. Luego entre llaves especifica el cuerpo con los miembros necesarios:

interface Interfaz {
    val p1: Int // Propiedad abstracta

    val p2: Boolean // Propiedad regular con accesor
        get() = p1 > 0

    fun m1() // Método abstracto

    fun m2() { // Método regular
        print("Método implementado")
    }
}

Los siguientes son aspectos que debes tener en cuenta al declarar una interfaz:

  • Puede contener métodos abstractos y métodos regulares con implementación
  • Puede contener propiedades abstractas y regulares pero sin campos de respaldo
  • No permite declaración de constructores
  • Las propiedades y métodos regulares de una interfaz pueden ser sobrescritos con el modificador override sin tener que marcarlos con open a diferencia de las clases abstractas.
  • Una clase puede implementar múltiples interfaces

Implementar Una Interfaz

Implementa una interfaz a través del símbolo : para separar el nombre de la clase del tipo:

class Ejemplo : Interfaz {
    override val p1: Int = 0

    override fun m1() {
        print("Sobrescibiendo método de Interfaz")
    }
}

Una vez escribas la implementación de forma Ejemplo: Interfaz, IntelliJ IDEA te marcará en rojo la necesidad de sobrescribir los miembros abstractos.

Si presionas la bombilla podrás acceder a un menú emergente que te facilita esta tarea a través de la opción Implement members:

Interfaces en Kotlin: Opción Implement members

Esto desplegará un diálogo con la lista de miembros que puedes incluir en la sobrescritura automática:

Diálogo Implement Members

Como resultado tendrás la sobrescritura por defecto con llamadas de la función TODO:

class Ejemplo : Interfaz {
    override val p1: Int
        get() = TODO("Not yet implemented")

    override fun m1() {
        TODO("Not yet implemented")
    }
}

Ejemplo De Interfaces En Kotlin

Usemos como referencia la declaración de una interfaz que representa a un modelo 3D como explosivo:

interface Explosive{
    fun explode()
}

Todos aquellos objetos que implementen esta interfaz deberán proveer las sentencias para materializar la explosión al interior de explode().

Por ejemplo, si tenemos una clase para cajas llamada Box y queremos que hagan parte del conjunto de objetos explosivos, entonces la implementamos:

class Box : Explosive {
    override fun explode() = println("¡Kaboom!")
}

fun main() {
    Box().explode()
}

Implementar Múltiples Interfaces

Ahora que tal si añadimos otra interfaz que permita dotar a objetos con la capacidad de incinerarse.

interface Incinerable {
    fun incinerate()
}

Si queremos que las cajas aparte de ser explosivas, también sean incinerables, entonces ubicamos en la cabecera de Box a Incinerable con una coma y sobrescribimos a incinerate().

class Box : Explosive, Incinerable {
    override fun explode() = println("¡Kaboom!")
    override fun incinerate() = println("¡Boosh!")
}

Resolver Conflictos De Sobrescritura

Adicionalmente, supongamos que cada interfaz muestra un mensaje avisando del peligro del objeto. Podemos implementar un método llamado warning() para ambas de la siguiente forma:

interface Explosive {
    fun explode()
    fun warning() = println("Explosivo")
}

interface Incinerable {
    fun incinerate()
    fun warning() = println("Incinerable")
}

El problema con esto, es que si una instancia de la clase Box decidiera llamar al método warning() habría un dilema para el compilador. Por lo que es necesario que sobrescribas el método para acotar su comportamiento:

class Box : Explosive, Incinerable {
    override fun explode() = println("¡Kaboom!")
    override fun incinerate() = println("¡Boosh!")
    override fun warning() {
        super<Incinerable>.warning()
        super<Explosive>.warning()
    }
}

Al sobrescribir a warning() puedes eliminar el conflicto de compilación. Luego puedes proceder a la llamada de la implementación que desees, a través de la referencia super junto al tipo base con paréntesis angulares: super<Tipo>.

fun main() {
    val box = Box()
    box.warning()
    box.explode()
    box.incinerate()
}

Salida:

Incinerable
Explosivo
¡Kaboom!
¡Boosh!

¿Ha sido útil esta publicación?