lunes, junio 05, 2017

Android: Comenzando con Koltin - Clases, Variables y el Safety Null

En mi artículo anterior (http://paveliz.blogspot.com.ar/2017/05/android-comenzando-con-kotlin.html) les dejaba no solo los primeros pasos para configurar Android Studio y poder comenzar a trabajar en Kotlin, sino también un ejemplo práctico (https://github.com/paveliz?tab=repositories), con el cual ya pudiesen ver y comparar lo que usualmente escribían en Java y cómo es que se escribe ahora en Kotlin.

Mi pendiente a comenzar a resolver en los siguientes artículos es comenzar a dejarles una base teórica con explicaciones puntuales del lenguaje y por supuesto, continuar dejando código útil en Github.

Entonces, comencemos a ver algunos temas propios del lenguaje:

Clases

En Java escribir una clase cuya función no es más quizás que describir un dato no es complejo, pero hay algunos elementos que debemos respetar, getter, setter, constructor, etc:

public class Alumno {

private Integer documento;
private String nombre;
private String apellido;

public Integer getDocumento() {
     return documento;
}

public void setDocumento(Integer documento) {
     this.documento = documento;
}

...

}

En Kotlin esto se resuelve de la siguiente forma:

open class Alumno constructor (
  var documento: Int,
  var nombre: String,
  var apellido: String
) {
  init {

  }
}

- Si el constructor principal no tiene ninguna annotation o modificador, la palabra "constructor" puede obviarse.
- El constructor principal puede no tener ningún código en particular, pero si se desea implementar una inicialización, se puede hacer en un bloque "init".
- Se genera automáticamente todas las propiedades, así como algunos métodos útiles como toString (), equals() y hashCode().

val nuevoAlumno = Alumno (1212, "Pepe Nombre", "Pepe Apellido")

Toast.makeText(this, "Propiedad Nombre: " + nuevoAlumno.nombre, Toast.LENGTH_LONG).show()

- Por default, todas las clases en Kotlin son como Java "final classes". El "open" delante de la clase justamente es lo opuesto al final y permite que pueda ser heredada.
- Obviamente si el constructor tiene argumentos, deberemos completarlos a la hora de aplicar la herencia.

class Pablo () : Alumno(1212, "Pablo", "Veliz")

Lo que teníamos como "métodos" en Java para darle cierta funcionalidad a nuestras clases, en Kotlin se llaman Functions (Funciones) y se precede por la palabra reservada "fun":

fun onCreate(savedInstanceState: Bundle?) {
...
}

En Kotlin, una función siempre retorna un valor, si no lo especificamos retorna Unit que sería el equivalente a "void" en Java. Solo que en Kotlin, Unit es un objeto. Si especificamos un valor de retorno quedaría:

fun calcularSuma ( a: Int, b: Int) : Int {
     return a + b
}

Otra cosa interesante que podemos hacer en Koltin es definir valor default para los argumentos:

fun calcularSuma ( a: Int, b: Int = 0 ) : Int {
     return a + b
}

Noten que entonces ahora, yo podría simplificar el tema y enviar solo el primer argumento a "calcularSuma", el segundo me lo toma por default como cero.

calcularSuma(2, 5)
calcularSuma(2)

var vs val

Como comenté en mi post anterior, Kotlin tiene de una forma más natural implementado los conceptos de "inmutabilidad" de las variables. ¿qué significa esto?, básicamente el concepto habla de que el valor de una variable NO pueda ser modificado luego de su inicialización. En Java, todas las variables/objetos mutan (a menos que estén indicadas explícitamente como "final"). Mutar significa básicamente, que cualquier otra parte del código podría modificar su valor.

En Kotlin usaremos "val" usualmente para justamente crear variables inmutables. De hecho, el propio Android Studio al ver que definimos una variable cuyo valor es luego modificado o caso contrario, nunca se modifica, hará automáticamente la sugerencia de cambiar un var a un val o viceversa.

Otro tema muy interesante en Kotlin es que NO necesito declarar los tipos, los mismos se infieren de su contenido.

// Variables Inmutables SIN Declaración de Tipo:
val pepe = "Algo"
val numero = 22

// Variables Inmutables CON Declaración de Tipo:
val otroPepe : String = "Algo"
val otroNumero : Int = 22

// Variables mutables
var pais = "Argentina"
var otroPais : String = "Argentina"


El famoso tema del Safety Null

Uno de los temas que más se comenta de Kotlin es que es "seguro" en el sentido de que no tiene previene los famosos "Null Pointer Exception" de Java. Es cierto..., aunque en realidad no es tan así. Para ejemplificar esto, lo voy a comentar aquí y dejé un ejemplo en Github ().

Cualquier operación que hagamos en Java con un elemento que puede llegar a tener un valor nulo, dará como resultado en Null Pointer Exception. Y esto es algo que, si bien podemos intentar predecir cuándo escribimos el código nos puede pasar igual y Android Studio o cualquier otro IDE de programación jamás nos dará advertencia alguna. Ejemplo:

public String nombre = null;

if (nombre.length > 0) {
...
}

PUM!!!

Definitivamente esto dará como resultado una excepción. Quizás no fue nuestra intención que nuestra variable "nombre" quedara en null, quizás fue producto de un "getStringExtra" que vino vacío justamente por haber olvidado validar un campo en el layout..., miles de razones pueden llevar a este conocido error.

Pero cuando desarrollamos en Kotlin, pasa lo siguiente:

var parametro: String? = null

La variable "parametro" se debe inicializar y si no sabemos si puede ser null o no, agregamos al final del tipo de dato el símbolo del signo de interrogación "?". Lo que le dice esto a Kotlin es: "bueno..., esta variable de tipo String podría tener un valor o podría ser null ¿quien sabe?".

Si luego quiero realizar una operación como con Java usando el lenght, tendremos el siguiente problema:

Automáticamente el mismo Android Studio nos dirá que hay un problema, ya que de entrada habíamos avisado que la variable podía contener nulos, no podemos entonces operar con la misma directamente. Algo que podríamos hacer sería esto:

var i: Int? = parametro?.length

¿que es esto? es una "safety operation" u operación "segura" como Kotlin lo llama. Básicamente, en ambos lados declaramos con el "?" que la operación puede dar null y esto NO dará Null Pointer Exception.

Si estamos completamente seguros que la variable es segura y no dará problema de nulos, Kotlin nos deja declararlo de la siguiente manera:

var i: Int = parametro!!.length

Noten que ahora, en lugar de usar el signo "?" usamos dos signos de explamación "!!". Con esto, lo que le estamos diciendo a Kotlin es: "Tranquilo, esta variable no tendrá NUNCA nulos y la operación es segura". Claro está, que si bien esto deja tranquilo y libre de warnings y errores a nuestro Android Studio..., si llegamos a fallar y la variable llega a tener un valor nulo...PUM! . En este caso tendremos un "Kotlin Null Pointer Exception"..., con lo cuál se darán cuenta

Una de las tantas formas correctas en las que deje implementado esto en el ejemplo en Github (https://github.com/paveliz/ClassesVarsValsyNullsEnKotlin), es la siguiente:

if( ! parametro.isNullOrEmpty() ) {

Java tiene un .isEmpty() pero si lo usan, la operación continuará dando NullPointerException. Pero Kotlin tiene un isNullOrEmpty() que realmente nos soluciona el tema de poder operar con nuestra variable siempre y cuando tenga algo y no sea nula.

En mi próximo post, continuaré con la introducción a nuevos elementos de este lenguaje.