Sobrecarga de operadores do Kotlin
Tags: kotlin
Published: 2023-06-06 16:03:21

Um detalhe bastante curioso no Kotlin é que podemos "somar", "subtrair" ou fazer diversas operações com os nossos objetos. Um exemplo bastante interessante seria a soma de listas:

```kotlin
val words = listOf("alex", "felipe")
val numbers = listOf(1, 2, 3)
val someList = words + numbers
println(someList)
println(words)
println(numbers)
```

Qual o resultado você espera? Vejamos:

```
[alex, felipe, 1, 2, 3]
[alex, felipe]
[1, 2, 3]
```

Olha que interessante! Uma nova lista foi criada adicionando os itens de cada lista, e mais, a soma não é restrita apenas entre as listas, podemos somar com qualquer objeto, até mesmo os nossos:

```kotlin
data class User(
    val id: Long,
    val name: String
)

val numbersWithUser = listOf(1, 2) + User(
    id = 1,
    name = "alexfelipe"
)
val wordsWithNumber = listOf(
    "alex",
    "felipe"
) + 1
println(numbersWithUser)
println(wordsWithNumber)
```

E o resultado que temos é:

```
[1, 2, User(id=1, name=alexfelipe)]
[alex, felipe, 1]
```

Neste momento você pode pensar: 

> _"Como isso é possível?"_ 🤔

## Conhecendo a sobrecarga de operadores

Essa funcionalidade é conhecida como sobrecarga de operadores ou [operator overloading](https://kotlinlang.org/docs/operator-overloading.html), embora o nome poça assustar, basicamente, são métodos "especiais" com a keyword `operator` que permitem adicionar o comportamento que desejamos.

Vamos considerar o exemplo do `+`, ele é traduzido para o método `plus()`. Podemos até mesmo ver algumas implementações na interface de `Collection`:

```kotlin
public operator fun <T> Collection<T>.plus(element: T): List<T> {
    val result = ArrayList<T>(size + 1)
    result.addAll(this)
    result.add(element)
    return result
}

public operator fun <T> Iterable<T>.plus(elements: Iterable<T>): List<T> {
    if (this is Collection) return this.plus(elements)
    val result = ArrayList<T>()
    result.addAll(this)
    result.addAll(elements)
    return result
}
```

Veja que tem mais de uma implementação e é por isso que podemos somar listas com listas ou listas com objetos e por ai vai... Aqui você pode ver alguns operadores comuns:

| Expressão    | Traduzido para  |
|--------------|-----------------|
| `a + b`      | `a.plus(b)`     | 
| `a - b`      | `a.minus(b)`    |
| `a * b`      | `a.times(b)`    |
| `a / b`      | `a.rem(b)`      |
| `a..b`       | `a.rangeTo(b)`  |

Em outras palavras, se você é capaz de fazer alguma dessas operações com objetos em Kotlin, é porque existe essa sobrecarga! 

## Implementando sobrecarga de operadores

Inclusive, podemos implementar operadores em nossas classes:

```kotlin
data class Product(
    val id: Long,
    val name: String,
    val description: String
)

class Cart(
   val products: List<Product> = listOf()
) {
    operator fun plus(product: Product): Cart =
        Cart(products + product)
}
```

Nesta implementação, podemos criar novos carrinhos com os produtos que desejamos somar:

```kotlin
val cart = Cart()
val cartWithKeyboard = cart + Product(
    id = 1,
    name = "Teclado mecânico",
    description = "Teclado com switches brown"
)
println(cartWithKeyboard.products)
val cartWithTShirt = cartWithKeyboard + Product(
    id = 2,
    name = "Camiseta",
    description = "Material 100% algodão"
)
println(cartWithTShirt.products)
```

Olha só o resultado que temos:

```
[Product(id=1, name=Teclado mecânico, description=Teclado com switches brown)]
[Product(id=1, name=Teclado mecânico, description=Teclado com switches brown), Product(id=2, name=Camiseta, description=Material 100% algodão)]
```

Interessante, né? Outras possibilidades de implementação seriam:

- somar objetos de pedidos e o retorno ser um resumo com todos os produtos, quantidades e valores somados.
- somar objetos de pagamentos ou taxas para obter o valor final com as adições
- somar um usuário e um produto para criar um pedido

Enfim, uma série de ações comuns no nosso dia a dia.

## Outros casos de uso de operadores

Atualmente existem diversas implementações de operadores, se observarmos a própria `Collection`, identificamos que é possível subtrair também:

```kotlin
val words = listOf("alex", "felipe", "instrutor", "alura") - "felipe"
val numbers = listOf(1, 2, 3) - 2
println(words)
println(numbers)
```

Veja o resultado:

```
[alex, instrutor, alura]
[1, 3]
```

Ou seja, uma forma mais sucinta de criar uma lista nova sem os elementos que não queremos!

E não para por aqui, se pegarmos classes como `BigDecimal`, podemos até mesmo fazer operações aritméticas sem problemas:

```kotlin
val total = BigDecimal("2.99") + BigDecimal("4.35")
println(total)
```

E chegamos no seguinte resultado:

```
7.34
```

O mesmo resultado sem a sobrecarga de operador seria assim:

```kotlin
val total = BigDecimal("2.99")
    .add(BigDecimal("4.35"))
```

## Para saber mais: outros tipos de operadores

Neste artigo foquei na sobrecarga de operadores aritméticos, mas existem outras possibilidades, operações [unárias](https://kotlinlang.org/docs/operator-overloading.html#unary-operations) e [binárias](https://kotlinlang.org/docs/operator-overloading.html#binary-operations), sendo que as aritméticas fazem parte das binárias.

O que achou da sobrecarga de operadores no Kotlin? Já usava no seu código? Compartilhe sua experiência nos comentários