DEV Community

Erick Giorgio
Erick Giorgio

Posted on

NULL Kotlin vs. Java Optional

Neste post veremos como o tipo Optional compara e traduz para tipos nullable em Kotlin.

Optional/Nullable Propriedades e tipos de retorno

Em Java, uma propriedade Optional pode ser declarada da seguinte forma:

public interface Person {
  String getName();  
  Optional<Integer> getAge();
}
Enter fullscreen mode Exit fullscreen mode

Em vez de usar o tipo Optional, em Kotlin vamos apenas declarar a propriedade age como anulável:

interface Person { 
  val name : String 
  val age : Int? 
}
Enter fullscreen mode Exit fullscreen mode

Optional.map() vs. Safe-Call Operator

O método map() do tipo Optional pode ser usado para acessar as propriedades da instância encapsulada de uma maneira “empty-safe”.

public interface Car {
  Person getDriver();
}
Optional<Car> car = getNextCarIfPresent();

Optional<Integer> driversAge =
    car.map(Car::getDriver).flatMap(Person::getAge);
Enter fullscreen mode Exit fullscreen mode

Este trecho de código recupera o motorista de um carro opcional e, em seguida, recupera a idade opcional desse motorista. Como ambos, o carro e a idade são opcionais, somos forçados a usar o método flatMap() ao recuperar a idade do motorista. Em Kotlin, podemos usar o operador de chamada segura embutido ?. para acessar propriedades de tipos Nullable :

interface Car {
  val driver: Person
}
val car: Car? = getNextCarIfPresent()

val driversAge: Int? = car?.driver?.age
Enter fullscreen mode Exit fullscreen mode

Optional.map() vs. let() Function

Às vezes, queremos usar um método externo dentro da cadeia de chamadas seguras no tipo Optional. Com o tipo Optional do Java, o mesmo método map() pode ser usado para esta finalidade:

Optional<DriversLicence> driversLicence =
    car.map(Car::getDriver).map(licenceService::getDriversLicence);
Enter fullscreen mode Exit fullscreen mode

Em Kotlin, teremos que usar a função let() do stdlib para invocar funções externas dentro de uma cadeia de operadores de chamada segura para atingir o mesmo objetivo.

val driversLicence: DriversLicence? = car?.driver?.let {
    licenceService.getDriversLicence(it) 
}
Enter fullscreen mode Exit fullscreen mode

Optional.orElse() vs. Elvis Operator

Quando recuperamos o valor encapsulado por um Optional, geralmente queremos fornecer um valor de fallback. Isso é obtido usando o método *orElse()* do tipo Opcional :

boolean isOfLegalAge =
    car.map(Car::getDriver).flatMap(Person::getAge).orElse(0) > 18;
Enter fullscreen mode Exit fullscreen mode

Quando a cadeia de chamadas de mapa retornar uma idade não vazia, ela será usada. Caso contrário, o valor de fallback fornecido 0 será usado. Kotlin tem o elvis Operator ?: para este propósito:

val isOfLegalAge: Boolean = car?.driver?.age ?: 0 > 18
Enter fullscreen mode Exit fullscreen mode

Optional.filter() vs. takeIf() Function

O tipo opcional fornece o método filter() , que pode ser usado para verificar uma condição no valor encapsulado. Se a condição for satisfeita pelo valor encapsulado, o método de filtro retornará o mesmo objeto Optional. Caso contrário, retornará um objeto Optional vazio.

Optional<Person> illegalDriver =
    car.map(Car::getDriver).filter(p -> p.getAge().orElse(0) < 18);
Enter fullscreen mode Exit fullscreen mode

Em Kotlin, usamos a função takeIf() do stdlib e acabamos com muito menos código:

val ilegalDriver: Pessoa? = carro?.motorista ?. takeIf { it.idade ?: 0 < 18 }
Enter fullscreen mode Exit fullscreen mode

Optional.ifPresent() vs. let() Function

Ao final da conversão e filtragem do valor Optional, geralmente queremos usar o valor encapsulado em algum cálculo ou código. Para isso, o tipo Optional fornece o método ifPresent() :

car.map(Car::getDriver) 
    .filter(person -> person.getAge().orElse(0) < 18) 
    . ifPresent (illegalDriver -> { 
        checkIdentity(illegalDriver); 
        putInJail(illegalDriver); 
    });
Enter fullscreen mode Exit fullscreen mode

Em Kotlin, usaríamos novamente a função let() para isso:

car?.driver?.takeIf { it.age ?: 0 < 18 }?.let { illegalDriver ->
  checkIdentity(illegalDriver)
  putInJail(illegalDriver)
}
Enter fullscreen mode Exit fullscreen mode

Kotlin fornece uma variedade de operadores integrados e funções stdlib que simplificam o manuseio de tipos anuláveis. Usá-los leva a um código curto, conciso e legível, especialmente quando combinado em cadeias de chamadas mais longas.

Além do código mais legível, os tipos anuláveis do Kotlin têm várias vantagens sobre o tipo Optional do Java.

Primeiro, não há sobrecarga de tempo de execução envolvida ao usar tipos anuláveis em Kotlin¹. Ao contrário de Opcional, nenhum objeto wrapper é criado para agrupar o valor real.

Em segundo lugar, os tipos anuláveis em Kotlin fornecem segurança nula em tempo de compilação. Usá-los de maneira não segura levará a um erro em tempo de compilação. Por outro lado, usar o tipo Optional de maneira não segura não é verificado pelo compilador e levará a uma exceção de tempo de execução.

a menos que estejamos lidando com tipos primitivos anuláveis, caso em que a versão em caixa do tipo primitivo é usada no nível da JVM.

Referência

https://docs.google.com/spreadsheets/d/1P2gMRuu36pSDW4fdwE-fLN9fcA_ZboIU2Q5VtgixBNo/edit#gid=0

https://www.zup.com.br/blog/java-vs-kotlin-vantagens-desvantagens

Top comments (0)