Clases en Kotlin
video[Tutorial] Clases en Kotlin
Cuando declaramos una clase en Kotlin podemos indicar parámetros que son utilizados para definir un constructor por defecto. Si indicamos var o val en estos parámetros pasarán a formar parte de las propiedades de la clase:
public class Complejo { private double re, im; public Complejo(double re, double im) { this.re = re; this.im = im; } public void suma(Complejo v) { re = re + v.re; im = im + v.im; } } |
class Complejo(var re: Double, var im: Dou-ble=0.0){ fun suma(v: Complejo) { re = re + v.re; im = im + v.im; } } |
También podemos declarar propiedades e inicializarlas a partir de los parámetros. La siguiente clase tiene tres propiedades nombre, edad y clavePersona:
class Persona(nombre: String, var edad: Int) { val clavePersona = nombre.toUpperCase() }Si necesitamos añadir instrucciones para inicializar el objeto podemos utilizar un bloque init. También podríamos añadir constructores alternativos siempre que utilicen parámetros diferentes
class Persona(val nombre: String) { init { println("Inicialización objeto") } constructor(nombre: String, edal: Int) : this(nombre) { println("Inicialización segundo constructor") } }Para crear objetos no es necesario usar new. Tampoco es imprescindible indicar el tipo del objeto, este se deduce del constructor.
Persona p = new Persona("Ana"); |
val p = Persona("Ana") |
public class Persona { private String nombre; public String getNombre() { return nombre; } public void setNombre(String n){ nombre = n; } } |
class Persona { var nombre: String = "" } |
p.setNombre("Ana"); String s = p.getNombre(); |
p.nombre = "Ana" var s = p.nombre |
class Persona { var nombre: String = "" get() = field.toUpperCase() set(value) { if (value=="Osama bin Laden") llamarFBI() field = value } }En un getter y setter hemos de utilizar field para acceder a la propiedad.
En Kotlin no es necesario que una propiedad se almacene en una variable de respaldo. Veamos un ejemplo:
class Persona { var nombre: String = "" var longNombre: Int get() = nombre.size }En la segunda propiedad no se creará una variable de tipo Int. Esto ocurre siempre que se implemente get o set y que, además, en ellos no se haga referencia a fiel (como ocurre en el ejemplo anterior).
Por defecto, tanto la clase como sus propiedades y funciones son públicos. Si queremos que algo no sea accesible desde fuera de la clase antepondremos private. Si queremos que solo el getter o el setter sean privados lo indicaremos de la siguiente manera:
class Persona { private var edad: Int = 0 var nombre: String = "" private set }Las clases anteriores no pueden usarse como base para crear nuevas clases; por defecto son cerradas. (En Java una clase cerrada se marca como final.) Si queremos heredar de una clase hemos de anteponer open.
open class Padre(p: Int) class Hijo(p: Int) : Padre(p)Si la clase no tiene un constructor primario, cada constructor secundario tiene que inicializar el tipo base con la palabra clave super:
class MyView : View { constructor(ctx: Context) : super(ctx) constructor(ctx: Context, attrs: AttributeSet) : super(ctx, attrs) }Por defecto, las propiedades y métodos de una clase son cerrados. (En Java marcados como final.) Para poder sobreescribirlos hemos de indicar open:
open class Padre { open var nombre = "Soy padre" open fun abierto() {} fun cerrado() {} } class Hijo() : Padre() { override var nombre = "Soy hijo" final override fun abierto() {} }Un miembro marcado con override continúa siendo abierto. Para cerrarlo hay que anteponer final.
Para acceder a propiedades y métodos de la clase padre usaremos super.
class Hijo() : Padre() { override var nombre = "hijo de ${super.nombre}" final override fun abierto() { super.abierto() } }
Extensiones
Las extensiones nos permiten ampliar la funcionalidad de una clase sin utilizar la herencia. Kotlin admite funciones de extensión y propiedades de extensión. Veamos un ejemplo:
fun MutableList<Int>.swap(index1: Int, index2: Int) { val tmp = this[index1] // 'this' corresponde con la lista this[index1] = this[index2] this[index2] = tmp }
Desde un punto de vista sintáctico es como si hubiéramos añadido un método a la clase MitableList<Int> y todos sus descendientes. Pero la función de extensión no modifica la clase, sino que se resuelve de forma estática.
En Java estarás acostumbrado a utilizar clases *Utils, como FileUtils, StringUtils o java.util.Collections. Estas clases extienden la funcionalidad de otras sin usar la herencia. La sintaxis resulta menos legible que en Kotlin:
Collections.swap(lista, 2, 5); |
lista.swap(2, 5) |
val <Int> List<Int>.lastIndex: Int get() = size – 1