Kotlin

Kotlin (liberado por JetBrains) es un lenguaje de programación que opera bajo la máquina virtual de java (JVM) con unas características interesantes que simpatizan con un estilo sencillo y funcional, su interoperalidad con Java lo hace atractivo para entenderse perfectamente con sistemas informáticos ya existentes (en Java), además de ser usado para programar bajo Android, también tiene características de integración nativa (Kotlin-Native basada en LLVM) y para la web (KotlinJS), posibilitando programar para el navegador como sustituto de Javascript, planteando desde su filosofía el concepto de multiplataforma a un nuevo nivel (aunque quién guste de dejar quieto el navegador con Javascript podrá tomar su propia opinión). De hecho, JetBrains ha liberado su propia tecnología para móviles denominada Kotlin Multiplatform Mobile que atienda la lógica de las aplicaciones móviles dejando la implementación de un componente estrictamente visual en el lenguaje nativo según corresponda (por ejemplo: Swift para iOS, el mismo Kotlin para Android).

Esta es una referencia ágil para quién tenga nociones de programación o codifique con algún otro lenguaje, también a modo de repaso y uso frecuente. Siendo así, tener esta información como memoria te resultará simple de acceder a los fundamentos, incluso como prueba de concepto sobre el lenguaje.

Ejemplo esencial

package hello

fun main(args: Array<String>) {
    println("Hi there!")
}

El ejemplo con clases

package project

class App {
    fun print() {
        println("Hi there!")
    }
}

fun main(args: Array<String>) {
    App().print()
}

Tips esenciales del lenguaje

Para quien tenga habilidades, experticia en programación y/o requiera agilidad en conceptos técnicos, pueden resumirse los siguientes tips esenciales del lenguaje:

  1. Tipos de datos básicos: Int, Double, Boolean, String, List, Array. Las variables se definen con var o val (si no cambia o no muta) y se puede asignar el valor directamente sin necesidad de especificar el tipo de datos. Si requiere especificarse el tipo de datos se usa : y luego el tipo.
  2. Las funciones se definen con fun, luego los parámetros van entre paréntesis (...), continuando con el nombre del parámetro, el caracter : que separa el tipo de datos posteriormente (y separando los parámetros con coma ,). El tipo de datos a retornar va también después de :. Además, se usa return para retornar un valor. La función principal de un programa se denomina main.
  3. Para el bloque de la función o el flujo de control se usan llaves {}. Las sentencias pueden terminar sin punto y coma, siendo solamente necesario para separar y admitir varias sentencias en la misma línea (por lo demás, no se usa).
  4. El flujo de control es semejante a lenguajes como C, Java, Javascript, Dart, es decir que se cuenta con una anatomía cercana para el uso de if, for, while, incluso para el manejo de excepciones (try).
  5. El constructor de una clase lleva el nombre constructor y la clase se define con la palabra reservada class y el nombre que debe iniciar en mayúscula. También existe un bloque o constructor de inicialización init {...}.
  6. Siendo un lenguaje moderno, ha promovido desde sus inicios el operador de nulo seguro (null-safe): ?.. Esta característica ha sido un gran diferenciador del lenguaje respecto a otros, y evita excepciones frecuentes que requieren mayor control y código en un programa con lenguaje Java.
  7. A diferencia de Java, incorpora data class para definir, de modo abreviado y máginamente, una clase que representa una estructura de datos con métodos para acceder a éstos. Serializar datos en JSON no es natural al lenguaje pero básicamente consiste en establecer el data class con la anotación Serializable, se importa la librería respectiva (kotlinx.serialization) y se usa Json.encodeToString o Json.deodeFromString según corresponda.

Por cierto, Kotlin brinda interoperabilidad con Java, por lo que reconoce naturalmente librerías escritas en Java (sobre la JVM), por ejemplo los conectores a las bases de datos, y también puede incorporar trozos de SQL.

Una breve comparación con Javascript

Si tienes conocimiento en Javascript u otro lenguaje, puede ser útil un sencillo escenario de comparación que nos da una referencia inmediata de Kotlin.

Concepto Javascript Kotlin
Variable let variable = ‘Ana’ var variable = “Ana”
Función function pow(x) { } fun pow(x: Int): Int { }
Condición if (i == 1) { } if (i == 1) { }
Ciclo For for (let i = 0; i < 10; i++) { } for (i in 0…9) { }
Excepción try { } catch(err) { } try { } catch(err: Exception) { }

Como puede observarse un programador puede facilmente asimilar la sintaxis de Kotlin en poco tiempo.

Tipos de datos básicos

Declaración de variables

Se puede declarar variables con las palabras reservadas var y val, esta última para indicar que el valor no cambia o no es mutable.

Ejemplo:

var variable: String = "Ana"
val immutable: String = "Ana"
var n: Int = 0

Definición de paquetes

Si estás iniciando en la programación bajo la JVM, una manera sencilla de comprender qué son los paquetes es verlos como las carpetas agrupadoras para organizar tu aplicación, y en muchas ocasiones pueden establecerse con un estilo semejante a un subdominio en internet de modo inverso, por ejemplo, si se piensa en un subdominio app.mycompany.com el paquete podría ser com.mycompany.app, de otro modo, se puede usar una carpeta de estructura simple que se refiera al nombre o alias del proyecto.

package project

import java.util.*
...

Definición de funciones

fun plus1(a: Int, b: Int) = a + b

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

Comentarios

// Esto es un comentario de fin de línea

/*
   Este es un comentario de bloque
*/

Condicionamiento if / else (if)

if (i == 1) {
    println("one")
}
else if (i == 2) {
    println("two")
}
else {
    println("aha")
}

El Ciclo For

for (item in list) {
    println(item)
}
for (index in list.indices) {
    println(list[index])
}
for (index in 0 until 10) {
    println(index)
}

Puede interrumpirse un ciclo con la sentencia break o dar el paso a la siguiente iteración con la setencia continue.

El Ciclo While

var i = 0
while (i < 10) {
    i++
}
var i = 0
do {
    i++
}
while (i < 10)

Evaluador When

var i = 0
when (i) {
    1 -> println("one")
    2 -> println("two")
    else -> println("aha")
}

Iteracion (forEach)

list.forEach {
    println(it)
}

La expresion it es en realidad una palabra reservada del lenguage para obtener el elemento conforme al ámbito

Excepciones

try {
    ...
}
catch (e: Exception) {
    ...
}
finally {
    ...
}

Puedes usar throw (por ejemplo: throw Exception()) para forzar el lanzamiento de un error.

Clases

La programación orientada a objetos (OOP) tiene su propia base de conceptos que abre un capítulo aparte para su comprensión, se puede decir que se pasa de pensar en funciones a clases que agrupan métodos (funciones) y propiedades (variables), siendo de gran útilidad para la reutilización de código. Como referencia simplemente se presenta la anatomía de una clase.

class Shape(val name: String) {
    private var sides: Int? = null

    constructor(name: String, sides: Int) : this(name) {
        this.sides = sides
    }
}
open class Base {
    open fun run() { ... }
}

class Some : Base() {
    override fun run() {
        ...
    }
}
data class Person(val name: String, val age: Int)

Kotlin & JSON

Para lograr json en Kotlin se usan librerías, como Jackson, que permiten mapear un objeto a una cadena que representa el json o viceversa. Veremos algun ejemplo basado en la siguiente clase:

import kotlinx.serialization.decodeFromString
import kotlinx.serialization.encodeToString
import kotlinx.serialization.json.Json
import kotlinx.serialization.Serializable

@Serializable
class Person(val name: String, val age: Int)

fun main() {
  val person = Person( "Andrey", 27 )
  val encodedJson  = Json.encodeToString(person)
  val decodedJson  = Json.decodeFromString<Person>(encodedJson)
}

Instalación de Kotlin

Para instalar Kotlin bajo Windows se puede descargar desde el sitio oficial y se agrega la ruta de la carpeta bin en el PATH del sistema. También puede usarse scoop en Windows (si esta previamente instalado) usando PowerShell con la sentencia:

scoop install kotlin

Para Linux se puede usar lo siguiente:

sdk install kotlin

Para macOS se usa Homebrew (previamente instalado) y se ejecuta:

brew install kotlin

kotlin -version nos puede mostrar la versión instalada

Para compilar una aplicación el comando siguiría un formato, dónde program representaría el nombre respectivo de nuestro programa. Veamos a continuación:

kotlinc program.kt -include-runtime -d program.jar

Para correr el programa se podría usar el siguiente comando:

java -jar program.jar

Configurando un proyecto con Maven

Maven es una de los gestores de paquetes de Java bien reconocidos, y también se usa para Kotlin. Un proyecto con Maven consiste en una archivo denominado pom.xml que tiene algunas secciones. Un ejemplo de un archivo pom.xml para un proyecto bajo Kotlin sería el siguiente:

<properties>
    <kotlin.version>1.6.10</kotlin.version>
</properties>

<dependencies>
    <dependency>
        <groupId>org.jetbrains.kotlin</groupId>
        <artifactId>kotlin-stdlib-jdk8</artifactId>
        <version>${kotlin.version}</version>
    </dependency>
</dependencies>

<build>
    <sourceDirectory>${project.basedir}/src/main/kotlin</sourceDirectory>
    <build>
        <plugins>
            <plugin>
                <groupId>org.jetbrains.kotlin</groupId>
                <artifactId>kotlin-maven-plugin</artifactId>
                <version>${kotlin.version}</version>
                <executions>
                    <execution>
                        <id>compile</id>
                        <goals>
                            <goal>compile</goal>
                        </goals>
                    </execution>
                </executions>
                <configuration>
                  <jvmTarget>1.8</jvmTarget>
                </configuration>
            </plugin>
        </plugins>
    </build>
</build>

Esta configuración inicial nos permitirá compilar nuestro proyecto. Sin embargo, debe interpretarse considerando que, si existe un archivo previo, debemos usar los fragmentos respectivos del contenido en formato xml.
Nótese que en el elemento jvmTarget se debe especificar la versión de JDK, en este caso 1.8 pero podría ser la versión 11.

Iniciando un Servidor Web con Ktor

Ktor (liberado por JetBrains) es un marco de trabajo para desarrollo Web con Kotlin que opera bajo la máquina virtual de java (JVM) siendo sencillo y abierto a opiniones, por ejemplo, puede integrarse con Netty o Jetty para un uso simplificado y escalar, incluso en un contenedor de Servlets como Tomcat.

Ejemplo esencial

import io.ktor.application.*
import io.ktor.http.*
import io.ktor.response.*
import io.ktor.routing.*
import io.ktor.server.engine.embeddedServer
import io.ktor.server.netty.Netty

fun main (args: Array<String>) {
    val server = embeddedServer(Netty, 9000) {
        routing {
            get("/") {
                call.respondText("Hi there!", ContentType.Text.Plain)
            }
        }
    }.start(wait = true)
}

Iniciando un Servidor Web con Javalin

Continuando con tecnología para programar un servidor web sencillo, encontramos el proyecto Javalin que usa Jetty. Veamos el ejemlo:

import io.javalin.Javalin

fun main(args: Array<String>) {
    val port = 8000
    val app = Javalin.create().start(port)
    app.get("/") { ctx -> ctx.result("Hi there!") }
}

Llamada al sistema con Kotlin & ProcessBuilder

En el siguiente ejemplo, usaremos ProcessBuilder para invocar una sentencia desde el sistema operativo, esto puede ser últil para gestionar scripts o tareas de servidor con sistemas como Linux y macOS (o en Windows usando WSL). En el ejemplo esencial podemos ilustrar un código como el siguiente:

package com.example

import java.io.BufferedReader
import java.io.IOException
import java.io.InputStreamReader

class ShellRemoteCalled() {
    fun remoteCalled() {
        val processBuilder = ProcessBuilder()
        processBuilder.command("python3", "test.py")
        try {
            val rpc = processBuilder.start()
            val reader = BufferedReader(InputStreamReader(rpc.inputStream))
            var result: String? = ""
            var line: String?
            while (reader.readLine().also { line = it } != null) {
                result += line
            }
            val exitCode = rpc.waitFor()
            println("\nShellRemoteCalled output : $result")
            println("Exited with error code : $exitCode")
        } catch (e: IOException) {
            println("\nException occurs")
            e.printStackTrace()
        } catch (e: InterruptedException) {
            println("\nInterrupted")
            e.printStackTrace()
        }
    }
}

fun main() {  // Could have args: Array<String>
    ShellRemoteCalled().remoteCalled()
}

test.py representa un script en Python (puede consistir simplemente en un saludo: print("Hello"))

Para lazar nuestro ejercicio ejecutamos lo siguiente:

kotlinc ShellRemoteCalled.kt -include-runtime -d ShellRemoteCalled.jar
java -jar ShellRemoteCalled.jar

© 2019 by César Arcila