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;
   }
}   
Si no se indica lo contrario, una clase extiende de Any, que es similar a Object en Java. Las clases en Kotlin por defecto son públicas.
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") 
Para acceder a las propiedades de un objeto ya no es necesario definir getters y setters. Estos son creados de forma automática:
public class Persona {
   private String nombre;
   public String getNombre() {
      return nombre;
   }
   public void setNombre(String n){
      nombre = n;
   }
} 
 class Persona {
   var nombre: String = ""
} 
Modificar las propiedades de un objeto es más sencillo en Kotlin, simplemente se indica su nombre y Kotlin realizará la llamada al getter o setter. Gracias a esto, el código resulta mucho más legible:
 
p.setNombre("Ana");
String s = p.getNombre(); 
p.nombre = "Ana"
var s = p.nombre 
Kotlin también nos permite crear nuestros propios getters y setters.
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) 
También podemos extender una clase con nuevas propiedades. Veamos un ejemplo:
val <Int> List<Int>.lastIndex: Int
    get() = size – 1