Introducción a las interfaces en Kotlin
Una interfaz es un conjunto de métodos que indican a los objetos qué hacer y cómo hacerlo de forma predeterminada. Podemos entender que una interfaz es como un contrato con unas cláusulas que obligan a las clases que firman o implementan esa interfaz.
Las interfaces en Kotlin permiten una importante reutilización de código declarando una serie de métodos y propiedades que deben ser implementados luego por una o más clases puesto que resultan inútiles sin uno o más implementadores.
De alguna manera, las interfaces vienen a suplir la imposibilidad de herencia múltiple en Kotlin. Y al igual que las clases abstractas, las interfaces en Kotlin pueden contener tanto métodos abstractos como métodos implementados, pero a diferencia de aquellas no pueden almacenar ningún estado. Esto quiere decir que no podemos crear una propiedad e inicializarla en la misma interfaz; si definimos una propiedad en una interfaz, la clase que la implemente debe sobrescribirla.
Las interfaces se definen con la palabra interface seguida de su identificador o nombre, y para indicar que una clase implementa una interfaz se utiliza la misma sintaxis que para declarar la herencia:
interface MiInterface { fun hacerAlgo() // método abstracto fun saludar() = "Hola" // método con implementación } class ClaseImp: MiInterface { ... }
Como vemos en el código anterior, no se incluye la palabra abstract en el método de una interfaz porque resultaría redundante, ya que son implícitamente abstractos.
En las interfaces también podemos declarar propiedades, que deben ser abstractas o bien proporcionar implementaciones de acceso (pero recuerda que no pueden guardar estados, es decir, no pueden ser inicializadas en la interfaz):
interface MiInterface { val prop: Int // propiedad abstracta // val propiedad: Boolean = true // ERROR val prop2: String // propiedad con implementación get() = "algo" fun hacerAlgo() { print(prop) } } class ClaseImp : MiInterface { override val prop: Int = 29 override fun hacerAlgo() { // ... } } fun main() { val inter = ClaseImp() println(inter.prop2) // algo }
Entonces, el código de una clase implementando una interfaz podría ser así:
interface miInterface { // propiedad abstracta val test: Int // método abstracto que devuelve un tipo String fun despedir() : String // método con implementación por defecto fun saludar() { // ... } } class ClaseImp : miInterface { // clase que implementa una interface // la clase sobrescribe los miembros abstractos de la interface override val test: Int = 25 override fun despedir() = "Adiós" // ... }
Una interfaz también puede derivar de otras interfaces (herencia de interfaces) y, por lo tanto, proporcionar implementaciones para sus miembros y declarar nuevas funciones y propiedades:
interface Identificacion { val nombreCompleto: String } interface Persona : Identificacion { val nombre: String val apellido: String override val nombreCompleto: String get() = "$nombre $apellido" } data class Empleado( // no es necesario implementar 'nombreCompleto' override val nombre: String, override val apellido: String, val puesto: Puesto ) : Persona
Un ejemplo de cómo trabaja una interfaz:
interface MiInterface { val test: Int fun despedir() : String fun saludar() { println("Hola, ¿qué tal?") } } class InterfaceImp : MiInterface { override val test: Int = 25 override fun despedir() = "Adiós" } fun main(args: Array<String>) { val obj = InterfaceImp() println("test = ${obj.test}") print("Llamando saludar(): ") obj.saludar() print("Llamando e imprimiendo despedir(): ") println(obj.despedir()) }
Es posible implementar dos o más interfaces en una misma clase (interfaces múltiples) separando por una coma cada una de las interfaces. Por ejemplo:
interface A { fun llamarA() { println("Desde interfaz A") } } interface B { fun llamarB() { println("Desde interfaz B") } } // implementa dos interfaces class ClaseImp: A, B fun main() { val obj = ClaseImp() obj.llamarA() obj.llamarB() }
En estos casos de múltiples interfaces implementadas en una misma clase, hay que tener en cuenta que si dos interfaces tienen un método no abstracto con el mismo nombre, al llamar a ese método existe un conflicto de nombres y el compilador generará un error:
interface A { fun llamar() { println("Desde interfaz A") } } interface B { fun llamar() { println("Desde interfaz B") } } class ClaseImp: A, B fun main(args: Array<String>) { val obj = ClaseImp() obj.llamar() // ERROR }
Este problema se puede resolver proporcionando una nueva implementación de los métodos:
interface A { fun llamar() { println("Desde interface A") } } interface B { fun llamar() { println("Desde interface B") } } class ClaseImp: A, B { // implementación explícita del método en la clase override fun llamar() { super<A>.llamar() // llama al método de la clase A } } fun main(args: Array<String>) { val obj = ClaseImp() obj.llamar() }
Otro ejemplo del uso de interfaz:
interface Punto { //declaramos la interfaz Punto fun localizar () // método abstracto } // clase con dos propiedades que implementa la interfaz Punto class D2 (val x: Int, val y: Int): Punto { override fun localizar () { println ("Localización del punto en dos dimensiones: $x, $y") } } // clase con tres propiedades que implementa la interfaz Punto class D3 (val x: Int, val y: Int, val z: Int): Punto { override fun localizar () { println ("Localización del punto en tres dimensiones: $x, $y, $z") } } fun main() { val puntoD2 = D2 (16, 8) puntoD2.localizar () val punto3D = D3 (56, 65, 22) punto3D.localizar () }
Comentarios
Publicar un comentario