NOTA: Este artículo es una traducción manual de este otro artículo.

Versión preeliminar:

Esta es una versión preeliminar. Para poder utilizarla en maven o en gradle, tienes que incluir el repositorio de gradle http://dl.bintray.com/kotlin/kotlin-eap-1.2:

buildscript {
    ext.kotlin_version = '1.1-M04'

    repositories {
        maven { url "http://dl.bintray.com/kotlin/kotlin-eap-1.1" }
        mavenCentral()
    }
    dependencies {
        classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"
    }
}
    

Nos complace presentaros el cuarto hito de la próxima versión de Kotlin. Estamos ultimando detalles de la 1.1 y tenemos previsto sacar una versión final para el primer trimestre de 2017. La mayoría de las características están ya bastante perfiladas, así que es un buen momento para que la probéis y nos digáis vuestra opinión. ¡Nos ayudaría mucho!

Como otra versión hito, no ofrecemos ninguna garantía de retrocompatibilidad en relación a las nuevas características del lenguaje y librerías. Cualquier cosa que introduzcamos en la 1.1 está sujeto a cambio antes de la versión final.

Por favor, compartid vuestra opinión sobre las nuevas características o cualquier problema que os surja, via YouTrack, los foros o Slack.

En este hito hemos cambiado sustancialmente la sintaxis de las corrutinas y las semánticas, haciendéndolas más sencillas y flexibles. También viene con mejoras en la librería estándar, nuevas características del lenguaje, plugins de compilación, numerosas características y mejoras en el backend de JavaScript, así como correcciones y actualizaciones. La nueva versión también incluye todas las características introducidas en Kotlin 1.0.6, incluyendo actualizaciones de compatibilidad con Android Studio 2.3 Beta 1.

Puedes encontrar aquí la lista de cambios completa y vamos a destacar algunos cambios abajo:

corrutinas

Le hemos dado una nueva vuelta de tuerca al diseño de las corrutinas, haciéndola más sencillas, más capacidad de composición y más potentes:

  • Ahora todas las construcciones de corrutinas y suspensiones tienen una firma mucho más intuitiva (see acabaron las transaformaciones raras a memorizar).
  • Solo hay un concepto básico del lenguaje de funciones de suspensión y sus correspondientes tipos de función de suspensión. Nos hemos deshecho de la palabra clave reservada coroutine. La corrutina es ahora una mera instancia de una computación suspendible que se inicia con la función startCoroutine de la librería estándar.
  • Las funciones complejas de suspensión se pueden componer a partir de otras funciones primitivas de suspensión. En esta versión únicamente se puede llamar a funciones de suspensión en llamadas en posiciones tail-call, pero quitaremos esta restricción más adelante.
  • Las funciones de suspensión se pueden definir para envolver cualquier API con estilo de callbacks y se puede usar libremente dentro de cualquier corrutina asíncrona. Ya no hacen falta los controladores. Los pares generate y yield, que construyen secuencias síncronas, limitan las suspensiones dentro de los bloques generate usando la anotación @RestrictsSuspension.
  • Hemos implementado la inferencia de tipos en las corrutinas. Ahora podéis omitir los tipos en la mayoría de casos en los constructores de corrutinas y los tipos se inferirán automáticamente.

La función de suspensión clásica await se puede implementar ahora mediante una llamada en posición tailcall a la función de suspensión suspendCoroutine que es parte de la librería estándar.

suspend fun <T> await(f: CompletableFuture<T>): T =
    suspendCoroutine<T> { c: Continuation<T> ->
        f.whenComplete { result, exception ->
            if (exception == null) // Se ha completado el future satisfactoriamente
                c.resume(result)
            else // El future se ha completado con una excepción
                c.resumeWithException(exception)
        }
    }

El constructor correspondiente se llama async y se implementa mediante la función startCoroutine:

fun <T> async(block: suspend () -> T): CompletableFuture<T> {
    val future = CompletableFuture<T>()
    block.startCoroutine(completion = object : Continuation<T> {
        override fun resume(value: T) {
            future.complete(value)
        }
        override fun resumeWithException(exception: Throwable) {
            future.completeExceptionally(exception)
        }
    })
    return future
}

Y se pueden usar conjuntamente para escribir código más natural con futures:

async {
    val original = asyncLoadImage("...original...") // creates a Future
    val overlay = asyncLoadImage("...overlay...")   // creates a Future
    ...
    // suspend while awaiting the loading of the images
    // then run `applyOverlay(...)` when they are both loaded
    return applyOverlay(await(original), await(overlay))
}

Sin embargo, los futures son solo uno de los casos de uso de las corrutinas. Podéis encontrar información detallada junto a implementaciones y ejemplos de uso en el documento KEEP revisado.

Creemos que ahora tenemos un gran diseño de corrutinas para Kotlin, pero somos conscientes de que no ha sido probado lo suficiente exhaustivamente todavía. De forma que vamos a publicarlo en la 1.1, con uso opcional bajo un flag de incubación. A partir de esta versión os aparecerá una advertencia “This feature is experimental: coroutines” al usar las corrutinas. Podéis deshabilitar esta advertencia con el flag de compilación -Xcoroutines=enable o deshabilitar dicha característica con el flag -Xcoroutines=error. La configuración correspondiente está disponible en las preferencias del compilador de Kotlin en IDEA. Para setear esta opción en un proyecto gradle podéis añadir kotlin.coroutines=enable o kotlin.coroutines=error al archivo raíz del proyecto local.properties.

Si estáis usando la librería kotlinx.coroutines por favor, usad la versión actualizada 0.2-alpha-1, adaptada a los nuevos cambios del diseño de corrutinas. Esta versión también introduce el método yieldAll en el ámbito generate. Por favor leed el archivo readme para más detalles.

Características del lenguaje

El tipo de las propiedades se puede inferir a partir de los getters

Por ejemplo, en el código de abajo se inferirá automáticamente el tipo de la propiedad foo a String. Ver la tarea KT-550 para más información.

val foo get() = ""

Características, mejoras y correcciones relacionadas con coma flontate

La comparación de números de coma flotante usa ahora una comparación compatible con IEEE 754 cuando el tipo se conoce de forma estática que es Double o Float. Para rangos de números de coma flotante hemos introducido una interfaz especializada ClosedfloatingPointRange, que proporciona su propio método de comparación, de forma que las operaciones de extensión como coerceIn, se pueden implementar por encima. Su instancias se obtiene con el operador .. incoado con dos valores Float o Double. Ved KT-4481 y KT-14651 para más detalles.

Interceptación del bindeo de propiedades delegadas

Ahora es posible interceptar delegados al bindeo de la propiedad usando el operador provideDelegate. Por ejemplo, si queremos comprobar el nombre de la propiedad antes de bindear, podemos hacer algo como esto:

class ResourceLoader<T>(id: ResourceID<T>) {
    operator fun provideDelegate(thisRef: MyUI, property: KProperty<*>): ReadOnlyProperty<MyUI, T> {
        checkProperty(thisRef, property.name)
        ... // creación de la propiedad
    }

    private fun checkProperty(thisRef: MyUI, name: String) { ... }
}

fun <T> bindResource(id: ResourceID<T>): ResourceLoader<T> { ... }

class MyUI {
    val image by bindResource(ResourceID.image_id)
    val text by bindResource(ResourceID.text_id)
}

Aquí se llamará al método provideDelegate en el inicializador del constructor de la clase MyUI. Así que podemos comprobar la consistencia de las propiedades en el momento de la creación. En versiones anteriores solo era posible en el momento de llamar al getter o al setter.

Desafortunadamente dicha característica no está documentada apropiadamente todavía, pero podéis ver el borrador del documento como referencia inicial.

Mejora de nulabilidad en algunos métodos del JDK

Algunas funciones del JDK tienen un contrato de nulabilidad definido en la documentación, otros no aceptan valores null, y otros devuelven null a veces. Desafortunadamente, el JDK no usa ninguna anotación para expresar dichos contratos y solo proporciona dicha información en la documentación. Antes de la 1.0 usábamos un artefacto de anotaciones externas para el JDK que se podía proporcionar por el compilador para alterar las firmas de las funciones del JDK, pero dicha aproximación no era lo suficiente confiable.

Ahora introducimos una nueva aproximación: embebiendo la información necesaria para mejorar las firmas del JDK directamente en el compilador. Como primer paso cubrimos la nulabilidad de un pequeño subset del API.

  • Las funciones miembro y factoría de java.util.Optional ** of: no permite valores nulos ** ofNillable, toma valores nullable, y devuelve un Optional que no puede ser nulable. ** get siempre devuelve un valor no nulo.
  • java.lang.ref.Reference y todos sus herederos, como WeakReference y SoftReference: ** get devuelve un valor nulable,, ya que se puede volver null en cualquier momento si el objeto referenciado es recolectado por el recolector de basura
  • los métodos default de Iterator, Iterable, Collection, List, Map que se exponen como funciones dependientes de la plataforma en las interfaces de colección intrínsecas de Kotlin.
  • Los tipos funcionales de Java, ahora tienen tipos sin plataforma y sus métodos de invocación cuando se construyen con tipos sin plataforma.

Estas mejoras son seguras en la mayoría de casos. En particular, son seguros cuando los tipos mejorados se hacen más específicos (no nulables) en posiciones de retorno o más generales (nilable) en posiciones de parámetros. Pero cuando el tipo cambia en la dirección opuesta, ducho cambio puede romper código.

Tratamos de no introducir dichas mejoras que puedan romper códigoa no ser que respeten la nulabilidad correctamente pudiendo llevar a excepciones en runtime. Así que por ejemplo Optional.of ahora recibe un argumento no nulable que es más restrictivo, pero tratar de pasar null a dicho método llevaría a una excepció en runtime en cualquier caso. Por otro lado, hemos decidido no especificar la nulabilidad correcta para File.listFiles que puede devolver null en determinados casos, porque en la mayoría de los casos no hay una opción mejor que lanzar otra excepción.

Otros cambios

  • El problema de usar un miembro no público dentro de una función pública inline se puede resolver con la anotación @PublishedApi. Cuando se aplica en un miembro interno, se convierte a efectos prácticos en pública y está disponible desde la función pública inline. Ver KT-12215 para más información.
  • const val ahora se reemplazan inline (ver KT-11734)
  • Las conversiones SAM ahora tienen la misma prioridad en la resolución de sobrecarga como los métodos normales. Eso arregla KT-11128 y similares.
  • Hemos considerado que ha sido un error elegir el nombre mod para el operador % (resto) con algunas consecuencias no muy agradables (ver KT-14650). Por lo que hemos decidido introducir el operador rem, y deprecar el mod y proporcionar las herramientas para que la transición sea todo lo fácil posible.

Librería estándar

Conversiones de cadena a números

Hay un buen puñado de extensiones nuevas para la clase String para convertir números sin lanzar excepciones o números invákidos: String.toIntOrNull(): Int?, String.toDoubleOrNull(): Double? etc. Tened en cuenta que dichas funciones producirán un boxing de los nñumeros al devolverlos, como asume el tipo de retorno.

También las conversiones de enteros, Int.toString(), String.toInt(), String.toIntOrNull() tienen una nueva sobrecarga con un parámetro radix, que permite especificar la base de conversión.

Nos gustaría agradecer a Daniil Vodopian por su contribución sustancial al desarrollo de dichas funciones.

onEach

onEach es una función de extensión pequeña pero útil para colecciones y secuencias, que permite realizar alguans acciones, posiblemente con efectos colaterales, en cada elemento de la colección/secuenciaen una operación de la cadena. En iterables se comporta como forEach pero también devuelve una instancia iterable. Y en secuencias devuelve una secuencia wrappeada, que aplica la acción proporcionada de forma vaga conforme se itera sobre los elementos.

Gracias a Christian Brüggemann por su prototipo inicial.

Backend JavaScript

external en vez de @native

Desde esta versión deprecamos la anotación @native y en su lugar debes usar el modificador external. A diferencia del target JVM, el de JS permite usar el modificador external en clases y propiedades. Nótese, que no tienes por qué marcar los miembros como external en clases que ya tienen el modificador external, ya que se hereda automáticamente. Así que en vez de:

@native fun alert(message: Any?): Unit {}

podéis escribir

external fun alert(message: Any?)

Mejor gestión de imports

Ahora podéis describir las declaraciones que se tienen que importar de los módulos de JavaScript de forma más precisa. Si añadís la anotación @JsModule("<module-name>") en una declaración externa, se importará de forma adecuada al sistema de módulos (ya sea CommonJS o AMD) durante la compilación. Por ejemplo, con CommonJS la declaración se importará via la función require(...). Adicionalmente, si queréis importar una declaración bien com módulo o como objeto global de JavaScript, podéis usar la anotación @JsNonModule.

Veamos un ejemplo completo. Podéis importar la librería jQuery a código Kotlin así:

@JsModule("jquery")
@JsNonModule
@JsName("$")
external abstract class JQuery {
    fun toggle(duration: Int = 0): JQuery
    fun click(handler: (Event) -> Unit): JQuery
}

@JsModule("jquery")
@JsNonModule
@JsName("$")
external fun JQuery(selector: String): JQuery

En este caso, se importará JQuery como módulo llamado jquery y alternativamente, se puede usar un objeto $, dependiendo de qué sistema de módulos se configure para usar en el compilador de Kotlin.

Podéis usar dichas declaraciones en vuestra aplicación así:

fun main(args: Array<String>) {
    JQuery(".toggle-button").click {
        JQuery(".toggle-panel").toggle(300)
    }
}

Podéis comprobar el código JS generado a para CommonJS y sistemas de módulos planos aquí.

Cómo probar

En Maven/Gradle: Añadir http://dl.bintray.com/kotlin/kotlin-eap-1.1 como repositorio para el build script en vuestros proyectos; y usar 1.1-M04 como el nñumero de versión para el compilador y la librería estándar.

En IntelliJ IDEA: Ir a Tools -> Kotlin -> Configure Kotlin Plugin Updates, y selecionar “Early Access Preview 1.1” en el desplegable Update channel y pulsad sobre Check for updates.

El compilador de línea de comandos se puede descargar de la página de releases de Github.

En try.kotlinlang.org Usad el desplegable de la esquina inferior derecha para cambiar la versión del compilador a la 1.1-M04.

¡Feliz Kotlin-dad!