Jetbrains

Lançamento do Kotlin 1.5.0-RC: alterações nas Bibliotecas Padrão e de Teste

O Kotlin 1.5.0-RC já está disponível com todos os recursos planejados para a versão 1.5.0: confira todo o escopo da próxima versão! Novos recursos de linguagem, atualizações do stdlib, uma biblioteca de teste melhorada e muitas outras alterações estão recebendo os toques finais. As únicas outras alterações antes do lançamento serão correções de bugs.

Experimente as modernas APIs do Kotlin nos seus projetos do mundo real com a versão 1.5.0-RC e nos ajude a deixar essa versão de lançamento melhor ainda! Informe quaisquer problemas que você encontrar no nosso rastreador de issues YouTrack.

Instalar a versão 1.5.0-RC

Neste artigo, nós vamos apresentar o que mudou no Kotlin padrão e nas bibliotecas de teste com a versão 1.5.0-RC:

Você encontra todos os detalhes abaixo!

Tipos inteiros sem sinal estáveis

A biblioteca padrão inclui a API de inteiros sem sinal que é útil para lidar com operações de inteiros não negativos. Ele inclui:

  • Tipos numéricos sem sinal: UInt, ULong, UByte, UShort e funções relacionadas, como conversões.
  • Tipos agregados: arrays, intervalos e progressões de inteiros sem sinal:UIntArray, UIntRange e containers similares para outros tipos.

Tipos inteiros sem sinal fazem parte da versão Beta desde o Kotlin 1.3. Agora estamos classificando tipos e operações com inteiros sem sinal como estáveis, permitindo que estejam disponíveis sem a necessidade de confirmação e seguros para uso em projetos do mundo real.

As novas APIs estáveis são:

  • Tipos inteiros sem sinal
  • Intervalos e progressões de tipos inteiros sem sinal
  • Funções que operam com tipos inteiros sem sinal
fun main() {
//sampleStart
    val zero = 0U // Define unsigned numbers with literal suffixes
    val ten = 10.toUInt() // or by converting non-negative signed numbers
    //val minusOne: UInt = -1U // Error: unary minus is not defined
    val range: UIntRange = zero..ten // Separate types for ranges and progressions

    for (i in range) print(i)
    println()
    println("UInt covers the range from ${UInt.MIN_VALUE} to ${UInt.MAX_VALUE}") // UInt covers the range from 0 to 4294967295
//sampleEnd
}

Arrays de inteiros sem sinal permanecem em Beta. Assim como varargs de inteiros sem sinal que são implementados com arrays. Se você quiser usá-los no seu código, você pode confirmá-los com a anotação @ExperimentalUnsignedTypes.

Saiba mais sobre inteiros sem sinal no Kotlin.

Extensões para a API java.nio.file.Path

O Kotlin agora fornece uma maneira de usar a moderna API Java IO não bloqueante num estilo idiomático Kotlin pronto para uso, através de funções de extensão para java.nio.file.Path.

Eis aqui um pequeno exemplo:

import kotlin.io.path.*
import java.nio.file.Path

fun main() {
    // construct path with the div (/) operator
    val baseDir = Path("/base")
    val subDir = baseDir / "subdirectory"

    // list files in a directory
    val kotlinFiles = Path("/home/user").listDirectoryEntries("*.kt")
    // count lines in all kotlin files
    val totalLines = kotlinFiles.sumOf { file -> file.useLines { lines -> lines.count() } }
}

Essas extensões foram incluídas como um recurso experimental no Kotlin 1.4.20 e agora estão disponíveis sem a necessidade de confirmação. Dê uma olhada no pacote kotlin.io.path para obter a lista de funções que você pode usar.

As extensões existentes para a API File permanecem disponíveis, portanto você é livre para escolher a API de sua preferência.

API independente de parâmetros de localização para caixa-alta e caixa-baixa

Muitos de vocês devem estar familiarizados com as funções stdlib para alterar a capitalização de strings e caracteres: toUpperCase(), toLowerCase(), toTitleCase(). Elas geralmente funcionam bem, mas podem causar dor de cabeça quando for necessário lidar com parâmetros de localização em plataforma diferentes: são todas sensíveis à localização, o que significa que o resultado poderá variar de acordo com a plataforma de localização onde o código é executado. Por exemplo, o que você acha que é retornado pela chamada ”Kotlin”.toUpperCase()? “KOTLIN”, é claro, você diria. Mas em turco, a letra maiúscula correspondente a i é İ, portanto o resultado é diferente: KOTLİN.

Agora existe uma nova API independente de localização para alterar o formato maiúsculo/minúsculo de strings e caracteres: as extensões uppercase(), lowercase(), titlecase() e suas versões correspondentes *Char(). Você talvez já tenha experimentado a prévia desses recursos na versão 1.4.30.

As novas funções funcionam de forma igual, independentemente das configurações de localização da plataforma. Basta chamar as funções e deixar o resto para o stdlib.

As novas funções funcionam de forma igual, independentemente das configurações de localização da plataforma. Basta chamar as funções e deixar o resto para o stdlib.

fun main() {
//sampleStart
    // replace the old API
    println("Kotlin".toUpperCase()) // KOTLIN or KOTLİN or?..

    // with the new API
    println("Kotlin".uppercase()) // Always KOTLIN
//sampleEnd
}

Na JVM, você pode realizar alterações entre maiúsculas e minúsculas levando em conta os parâmetros de localização ao chamar as novas funções passando a localização atual como argumento:

"Kotlin".uppercase(Locale.getDefault()) // Locale-sensitive uppercasing

As novas funções deverão substituir completamente as antigas, que estamos agora marcando como deprecadas.

Conversões legíveis de caractere para código e de caractere para dígito

A operação que retorna o código UTF-16 de um caractere – a função toInt() – era uma armadilha comum porque ela parece muito com String.toInt() usada em strings de um dígito, que produz um Int apresentado por este dígito.

"4".toInt() // returns 4
'4'.toInt() // returns 52

Além disso, não havia uma função comum que retornasse o valor numérico 4 para Char '4'.

Para solucionar essas questões, agora existe um conjunto de novas funções para conversão entre caracteres e seus códigos inteiros e valores numéricos:

  • Char(code) e Char.code convertem entre um char e seu código.
  • Char.digitToInt(radix: Int) e sua versão *OrNull criam um inteiro a partir de um dígito na base especificada.
  • Int.digitToChar(radix: Int) cria um caractere de um dígito que representa um inteiro na base especificada.

Essas funções têm nomes claros e deixam o código mais legível:

fun main() {
//sampleStart
    val capsK = Char(75) // ‘K’
    val one = '1'.digitToInt(10) // 1
    val digitC = 12.digitToChar(16) // hexadecimal digit ‘C’

    println("${capsK}otlin ${one}.5.0-R${digitC}") // “Kotlin 1.5.0-RC”
    println(capsK.code) // 75
//sampleEnd
}

As novas funções estão disponíveis desde o Kotlin 1.4.30, no modo de pré-visualização, mas agora são funções estáveis. As antigas funções para conversão de caractere para número (Char.toInt() e funções similares para outros tipos numéricos) e conversão de número para caractere (Long.toChar() e similares, exceto Int.toChar()) agora estão deprecadas.

API de caracteres multiplataforma estendida

Continuamos a estender a parte multiplataforma da biblioteca padrão para que todos os seus recursos façam parte do código comum do projeto multiplataforma.

Agora, disponibilizamos várias funções Char em todas as plataformas e em código comum. Estas funções são:

  • Char.isDigit(), Char.isLetter(), Char.isLetterOrDigit() que verificam se um caractere é uma letra ou um dígito.
  • Char.isLowerCase(), Char.isUpperCase(), Char.isTitleCase() que verificam o formato maiúsculo/minúsculo de um caractere.
  • Char.isDefined() que verifica se um caractere tem uma categoria geral Unicode diferente de Cn (undefined).
  • Char.isISOControl() que verifica se um caractere é um caractere de controle ISO, que possui código nos intervalosu0000..u001F ou u007F..u009F.

A propriedade Char.category e seu tipo de retorno de classe enum CharCategory, que indica a categoria geral de um caractere de acordo com o Unicode, agora estão disponíveis nos projetos multiplataforma.

fun main() {
//sampleStart
    val array = "Kotlin 1.5.0-RC".toCharArray()
    val (letterOrDigit, punctuation) = array.partition { it.isLetterOrDigit() }
    val (upperCase, notUpperCase ) = array.partition { it.isUpperCase() }

    println("$letterOrDigit, $punctuation") // [K, o, t, l, i, n, 1, 5, 0, R, C], [ , ., ., -]
    println("$upperCase, $notUpperCase") // [K, R, C], [o, t, l, i, n, , 1, ., 5, ., 0, -]

    if (array[0].isDefined()) println(array[0].category)
//sampleEnd
}

Versões estritas de String?.toBoolean()

A função String?.toBoolean() do Kotlin é amplamente usada para criar valores booleanos a partir de strings. Seu funcionamento é bastante simples: seu valor é true para a string “true”, independente se estiver em letras maiúsculas ou minúsculas, e false para todas as outras strings, inclusive null.

Embora esse comportamento pareça natural, ele pode ocultar situações potencialmente incorretas. O que quer que você converta com esta função, você obterá um booleano mesmo se a string tiver algum valor inesperado.

Novas versões estritas que diferenciam maiúsculas de minúsculas de String?.toBoolean() foram criadas para ajudar a evitar esses erros:

  • String.toBooleanStrict() lança uma exceção para todas as entradas, exceto as literais “true” e “false”.
  • String.toBooleanStrictOrNull() retorna null para todas as entradas, exceto literais “true” e “false”.
fun main() {
//sampleStart
    println("true".toBooleanStrict()) // True
    // println("1".toBooleanStrict()) // Exception
    println("1".toBooleanStrictOrNull()) // null
    println("True".toBooleanStrictOrNull()) // null: the function is case-sensitive
//sampleEnd
}

Mudanças na API Duration

A API experimental Duration and Time Measurement faz parte da stdlib desde a versão 1.3.50. Ela oferece uma API para a medição precisa de intervalos de tempo.

Uma das principais classes desta API é Duration. Ela representa a quantidade de tempo entre dois instantes de tempo. Na versão 1.5.0, Duration ganha mudanças significativas tanto na API quanto na representação interna.

Duration agora usa um valor Long para a representação interna, em vez de Double. O intervalo de valores Long permite representar mais de cem anos com precisão de nanossegundos ou cem milhões de anos com precisão de milissegundos. No entanto, as durações de frações de nanosegundos suportadas anteriormente não estão mais disponíveis.

Também introduzimos novas propriedades para recuperar uma duração como um valor Long. Elas estão disponíveis para diversas unidades de tempo:Duration.inWholeMinutes, Duration.inWholeSeconds e outras. Essas funções vieram substituir as propriedades baseadas em Double, como Duration.inMinutes.

Outra mudança é um conjunto de novas funções de fábrica para criar instâncias de Duration a partir de valores inteiros. Elas são definidas diretamente no tipo Duration e substituem as antigas propriedades de extensão de tipos numéricos como Int.seconds.

import kotlin.time.ExperimentalTime
import kotlin.time.Duration

@ExperimentalTime
fun main() {
    val duration = Duration.milliseconds(120000)
    println("There are ${duration.inWholeSeconds} seconds in ${duration.inWholeMinutes} minutes")
}

Dadas essas mudanças importantes, toda a API Duration and Time Measurement permanece experimental na versão 1.5.0 e requer confirmação através da anotação @ExperimentalTime.

Por favor, experimente a nova versão e compartilhe seus comentários no nosso rastreador de issues, YouTrack.

Operações matemáticas: divisão arredondada para baixo (função piso) e o operador mod

No Kotlin, o operador de divisão (/) em inteiros representa a divisão truncada, que despreza a parte fracionária do resultado. Na aritmética modular, há também uma alternativa: a divisão arredondada para baixo (função piso) que converte o resultado para o menor valor inteiro, produzindo um resultado diferente em números negativos.

Anteriormente, a divisão arredondada para baixo necessitava de uma função personalizada como:

fun floorDivision(i: Int, j: Int): Int {
    var result = i / j
    if (i != 0 && result <= 0) result--
    return result
}

Na versão 1.5.0-RC, apresentamos a função floorDiv() que realiza a divisão arredondada para baixo em inteiros.

fun main() {
//sampleStart
    println("Truncated division -5/3: ${-5 / 3}")
    println("Floored division -5/3: ${(-5).floorDiv(3)}")
//sampleEnd
}

Na versão 1.5.0, introduzimos a nova função mod(). Ela agora funciona exatamente como o nome sugere: retorna o módulo, que é o resto da divisão arredondada para baixo.

Ela difere da função rem(), do Kotlin (ou operador %). O módulo é a diferença entre a e a.floorDiv(b) * b. O módulo diferente de zero sempre terá o mesmo sinal que b enquanto que a % b pode ter um sinal diferente. Isto pode ser útil, por exemplo, para implementar listas cíclicas:

fun main() {
//sampleStart
    fun getNextIndexCyclic(current: Int, size: Int ) = (current + 1).mod(size)
    fun getPreviousIndexCyclic(current: Int, size: Int ) = (current - 1).mod(size)
    // unlike %, mod() produces the expected non-negative value even if (current - 1) is less than 0

    val size = 5
    for (i in 0..(size * 2)) print(getNextIndexCyclic(i, size))
    println()
    for (i in 0..(size * 2)) print(getPreviousIndexCyclic(i, size))
//sampleEnd
}

Coleções: firstNotNullOf() e firstNotNullOfOrNull()

A API de coleções Kotlin cobre uma variedade de operações populares em coleções com funções integradas. Para os casos que não são comuns, você geralmente combina chamadas dessas funções. Isto funciona, mas nem sempre fica muito elegante e pode causar sobrecarga.

Por exemplo, para obter o primeiro resultado não nulo de uma função de seletor nos elementos da coleção, você chamaria mapNotNull() e first(). Na versão 1.5.0, você pode fazer isso com uma única chamada da nova função firstNotNullOf(). Junto com firstNotNullOf(), estamos acrescentando sua contraparte *orNull() que produz null se não houver nenhum valor a retornar.

Eis aqui um exemplo de como ele pode ser usado para encurtar seu código.

Suponha que você tenha uma classe com uma propriedade anulável e precise do seu primeiro valor não nulo dentro de uma lista de instâncias de classe.

class Item(val name: String?)

You can implement this by iterating the collection and checking if a property is not null:

// Option 1: manual implementation
for (element in collection) {
    val itemName = element.name
    if (itemName != null) return itemName
}
return null

Outra maneira é usar as funções que já existem mapNotNull() e firstOrNull(). Observe que mapNotNull() constrói uma coleção intermediária, que requer memória adicional, especialmente em coleções grandes. Então a transformação em sequência pode também ser necessária aqui.

// Option 2: old stdlib functions
return collection
    // .asSequence() // Avoid creating intermediate list for big collections
    .mapNotNull { it.name }
    .firstOrNull()

E é assim que fica com a nova função:

// Option 3: new firstNotNullOfOrNull()
return collection.firstNotNullOfOrNull { it.name }

Mudanças na biblioteca de testes

Faz vários lançamentos que não incluímos atualizações importantes na biblioteca de testes do Kotlin kotlin-test, mas agora estamos entregando algumas mudanças há muito esperadas. Com a versão 1.5.0-RC, você pode experimentar uma série de novos recursos:

  • Uma única dependência kotlin-test em projetos multiplataforma.
  • Escolha automática de um framework de testes para conjuntos de fontes Kotlin/JVM.
  • Atualizações nas funções de asserção.

Dependência kotlin-test em projetos multiplataforma

Estamos continuando nosso desenvolvimento do processo de configuração para projetos multiplataforma. Na versão 1.5.0, tornamos mais fácil configurar uma dependência do kotlin-test para todos os conjuntos de fontes.

Agora, a dependência kotlin-test no conjunto de fontes de teste comum é a única que você precisa adicionar. O plug-in Gradle irá inferir a dependência de plataforma correspondente para outros conjuntos de fontes:

  • kotlin-test-junit para conjuntos de fontes JVM. Você também pode mudar para kotlin-test-junit-5 ou kotlin-test-testng se você ativá-los explicitamente (continue lendo para saber como).
  • kotlin-test-js para conjuntos de fontes Kotlin/JS.
  • kotlin-test-common e kotlin-test-annotations-common para conjuntos de fontes comuns.
  • Não há um artefato extra para conjuntos de fontes Kotlin/Native porque Kotlin/Native fornece implementações integradas da API kotlin-test.

Escolha automática de um framework de testes para conjuntos de fontes Kotlin/JVM

Depois de especificar a dependência kotlin-test no conjunto de fontes de teste comum, conforme descrito acima, os conjuntos de fontes da JVM recebem automaticamente a dependência do JUnit 4. É isso! Você pode escrever e executar os testes imediatamente!

É assim que fica no Groovy DSL:

kotlin {
    sourceSets {
        commonTest {
            dependencies {
                 // This brings the dependency
                // on JUnit 4 transitively
                implementation kotlin('test')
            }
        }
    }
}

E no Kotlin DSL fica assim:

kotlin {
    sourceSets {
        val commonTest by getting {
            dependencies {
                // This brings the dependency
                // on JUnit 4 transitively
                implementation(kotlin("test"))
            }
        }
    }
}

Você também pode mudar para o JUnit 5 ou TestNG simplesmente chamando uma função na tarefa de teste: useJUnitPlatform() ou useTestNG().

kotlin {
    jvm {
        testRuns["test"].executionTask.configure {
            // enable TestNG support
            useTestNG()
            // or
            // enable JUnit Platform (a.k.a. JUnit 5) support
            useJUnitPlatform()
        }
    }
}

O mesmo funciona em projetos JVM puros quando você adiciona a dependência kotlin-test.

Atualizações nas funções de asserção

Para a versão 1.5.0, preparamos uma série de novas funções de asserção, juntamente com melhorias nas existentes.

Primeiro, vamos dar uma olhada nas novas funções:

  • assertIs<T>() e assertIsNot<T>() verificam o tipo do valor.
  • assertContentEquals() compara o conteúdo do contêiner para vetores, sequências e qualquer Iterable. Mais precisamente, ele verifica se expected e actual contêm os mesmos elementos na mesma ordem.
  • assertEquals() e assertNotEquals() para Double e Float têm novas sobrecargas com um terceiro parâmetro: precisão.
  • assertContains() verifica a presença de um item em qualquer objeto com o operador contains() definido: array, lista, intervalo, etc.

Aqui está um breve exemplo que mostra o uso dessas funções:

@Test
fun test() {
    val expectedArray = arrayOf(1, 2, 3)
    val actualArray = Array(3) { it + 1 }

    val first: Any = actualArray[0]
    assertIs<Int>(first)
    // first is smart-cast to Int now
    println("${first + 1}")

    assertContentEquals(expectedArray, actualArray)
    assertContains(expectedArray, 2)

    val x = sin(PI)

    // precision parameter
    val tolerance = 0.000001

    assertEquals(0.0, x, tolerance)
}

Em relação às funções de asserção existentes, agora é possível chamar suspending functions dentro do lambda, que são passadas para assertTrue(), assertFalse() e expect() porque essas funções agora são inline.

Experimente todos os recursos do Kotlin 1.5.0

Traga todas essas APIs modernas do Kotlin para seus projetos do mundo real com a versão 1.5.0-RC!

No IntelliJ IDEA e no Android Studio, instale o Kotlin Plugin versão 1.5.0-RC. Saiba como obter as versões EAP do plugin.

Construa seus projetos existentes com a versão 1.5.0-RC para saber como eles irão funcionar na versão 1.5.0. Com a nova configuração simplificada para versões de pré-visualização, você só precisa alterar a versão do Kotlin para 1.5.0-RC e ajustar as versões das dependências, se necessário.

Instalar a versão 1.5.0-RC

Como sempre, você pode experimentar a mais recente versão online no Kotlin Playground.

Compatibilidade

Como em todos os lançamentos de recursos, alguns ciclos de descontinuação de alterações previamente anunciadas estarão chegando ao fim com o Kotlin 1.5.0. Todas essas situações foram cuidadosamente revisadas pelo comitê de linguagens e estão listados no Guia de Compatibilidade para Kotlin 1.5. Você também pode explorar essas mudanças no YouTrack.

Notas sobre o lançamento

Agora que chegamos ao release candidate final para o Kotlin 1.5.0, é hora de começar a compilar e publicar! Diferentemente dos lançamentos de milestone anteriores, binários criados com o Kotlin 1.5.0-RC têm garantia de compatibilidade com o Kotlin 1.5.0.

Compartilhe suas opiniões

Esta é a sua última oportunidade de influenciar o próximo lançamento! Compartilhe quaisquer problemas que você encontrar conosco, usando o rastreador de issues. Faça com que o Kotlin 1.5.0 seja melhor para você e para a comunidade!

Instalar a versão 1.5.0-RC

Powered by WPeMatico