Nesse codelab você aprenderá como refatorar o seu código de Java para Kotlin. Você também aprenderá como garantir que o código que você está escrevendo seguem as convenções da linguagem Kotlin.

Esse codelab é feito para qualquer pessoa desenvolvedora que usa Java e que está considerando migrar um projeto para Kotlin. Nós começaremos com algumas classes Java que você converterá para Kotlin usando a IDE.

O que aprenderá

Você aprenderá como refatorar Java para Kotlin. Fazendo isso você aprenderá conceitos e recursos da linguagem Kotlin, listados a seguir:

Suposições

Você já deve ter familiaridade com Java.

Do que você precisará

Crie um novo projeto

Se você está utilizando o IntelliJ IDEA, crie um novo projeto Java para Kotlin/JVM.

Se você está utilizando o Android Studio, crie um novo projeto sem Activity. Deixe a opção de linguagem como Kotlin.

Ao escolher Kotlin como linguagem padrão, seu projeto é configurado automaticamente para suportá!

O código

Nós vamos criar um objeto de modelo chamado User e uma classe singleton Repository que trabalhe com objetos User e expõe listas de usuários e nomes de usuários formatados.

Crie um novo arquivo chamado User.java dentro de app/java/<nomedoseupacote> e cole o seguinte código:

public class User {

    private String firstName;
    private String lastName;

    public User(String firstName, String lastName) {
        this.firstName = firstName;
        this.lastName = lastName;
    }

    public String getFirstName() {
        return firstName;
    }

    public void setFirstName(String firstName) {
        this.firstName = firstName;
    }

    public String getLastName() {
        return lastName;
    }

    public void setLastName(String lastName) {
        this.lastName = lastName;
    }

}

Crie um novo arquivo chamado Repository.java e cole o código a seguir:

import java.util.ArrayList;
import java.util.List;


public class Repository {

    private static final Repository INSTANCE = null;

    private List<User> users = null;

    public static Repository getInstance() {
        if (INSTANCE == null) {
            synchronized (Repository.class) {
                if (INSTANCE == null) {
                    INSTANCE = new Repository();
                }
            }
        }
        return INSTANCE;
    }

    // mantendo o construtor privado para reforçar o uso de getInstance
    private Repository() {

        User user1 = new User("Jane", "");
        User user2 = new User("John", null);
        User user3 = new User("Anne", "Doe");

        users = new ArrayList();
        users.add(user1);
        users.add(user2);
        users.add(user3);
    }

    public List<User> getUsers() {
        return users;
    }

    public List<String> getFormattedUserNames() {
        List<String> userNames = new ArrayList<>(users.size());
        for (User user : users) {
            String name;

            if (user.getLastName() != null) {
                if (user.getFirstName() != null) {
                    name = user.getFirstName() + " " + user.getLastName();
                } else {
                    name = user.getLastName();
                }
            } else if (user.getFirstName() != null) {
                name = user.getFirstName();
            } else {
                name = "Unknown";
            }
            userNames.add(name);
        }
        return userNames;
    }
}



      

Nossa IDE consegue fazer um ótimo trabalho refatorando automaticamente código Java para código Kotlin, mas às vezes ela precisa de uma mãozinha. Nós faremos isso primeiro e depois passaremos pelo código refatorado para entender como e porque ele foi refatorado desta forma.

Vá para o arquivo User.java e o converta para Kotlin: Code -> Convert Java File to Kotlin File.

Se a sua IDE pedir para corrigir após a conversão, pressione Yes.

Você deve ver o código Kotlin a seguir:

class User(var firstName: String?, var lastName: String?)

Note que o User.java foi renomeado para User.kt. Arquivos Kotlin tem a extensão .kt.

Na classe Java User nós temos duas propriedades firstName e lastName. Cada uma tem um método getter e setter, tornando o seu valor mutável. A palavra chave em Kotlin para variáveis mutáveis é var para cada uma dessas propriedades. Se as propriedades Java só tivessem getters, elas seriam imutáveis e teriam sido declaradas como val. val é similar a palavra chave final em Java.

Uma das principais diferenças entre Kotlin e Java é que Kotlin especifica explicitamente se uma variável pode aceitar um valor nulo. Isso é feito acrescentando um `?` à declaração do tipo.

As propriedades da classe User em Java podem receber valores nulos, então cada uma é marcada como nullable (ou seja, que pode ser nula) com String?. Se você anotar seus membros Java como não-nulos (usando org.jetbrains.annotations.NotNull ou androidx.annotation.NonNull), o conversor vai reconhecerá isso e tornará os campos não-nulos em Kotlin também.

A refatoração básica já está pronta. Mas nós podemos escrever isso de uma maneira mais idiomática. Vamos ver como.

Classe de dados (Data)

Nossa classe User contém apenas dados. Kotlin tem uma palavra chave para classes com esse papel: data. Ao marcá-la como uma classe data, o compilador criará automaticamente getters e setters para nós. Ela também derivará as funções equals(), hashCode() e toString().

Vamos adicionar a palavra chave data na nossa classe User:

data class User(var firstName: String?, var lastName: String?)

Kotlin, assim como Java, pode ter um construtor primário e um ou mais construtores secundários. O construtor no exemplo acima é o primário da classe User. Se você está convertendo uma classe Java que possui múltiplos construtores, o conversor irá criá-los automaticamente em Kotlin também. Eles são definidos usando a palavra chave constructor.

Se quisermos criar uma instância dessa classe, podemos fazer assim:

val user1 = User("Jane", "Doe")

Igualdade

Em Kotlin temos dois tipos de igualdade:

As propriedades definidas no construtor principal da classe de dados serão usadas para verificações de igualdade estrutural.

val user1 = User("Jane", "Doe")
val user2 = User("Jane", "Doe")
val structurallyEqual = user1 == user2 // true
val referentiallyEqual = user1 === user2 // false

Em Kotlin, nós podemos atribuir valores padrão para argumentos em chamadas de função. O valor padrão é usado quando o argumento é omitido. Em Kotlin, construtores também são funções, então podemos usar argumentos padrão para especificar que o valor padrão de lastName é null. Para fazer isso, nós apenas atribuímos null a lastName.

data class User(var firstName: String?, var lastName: String? = null)

// uso
val jane = User ("Jane") // mesmo que User("Jane", null)
val joe = User ("John", "Doe")

Parâmetros de funções também podem ser nomeados ao chamar funções:

val john = User (firstName = "John", lastName = "Doe") 

Digamos que o firstName tenha null como valor padrão e lastName não. Nesse caso, como o parâmetro padrão precederia um parâmetro sem valor padrão, teria que chamar a função com argumentos nomeados:

data class User(var firstName: String? = null, var lastName: String?)

// uso
val jane = User (lastName = "Doe") // mesmo que User(null, "Doe")
val john = User ("John", "Doe")

Antes de prosseguir, verifique se a classe User é uma classe de dados (data). Vamos converter a classe Repository para Kotlin. O resultado da conversão automática deve ficar da seguinte forma:

class Repository
// mantendo o construtor privado para reforçar o uso de getInstance
private constructor() {

    private val users: MutableList<User>? = null

    val formattedUserNames: List<String>
        get() {
            val userNames = ArrayList<String>(users!!.size)
            for ((firstName, lastName) in users) {
                val name: String?

                if (lastName != null) {
                    if (firstName != null) {
                        name = "$firstName $lastName"
                    } else {
                        name = lastName
                    }
                } else if (firstName != null) {
                    name = firstName
                } else {
                    name = "Unknown"
                }
                userNames.add(name)
            }
            return userNames
        }

    init {

        val user1 = User("Jane", "")
        val user2 = User("John", null)
        val user3 = User("Anne", "Doe")

        users = ArrayList()
        users!!.add(user1)
        users.add(user2)
        users.add(user3)
    }

    fun getUsers(): List<User>? {
        return users
    }

    companion object {

        private var INSTANCE: Repository? = null

        val instance: Repository
            get() {
                if (INSTANCE == null) {
                    synchronized(Repository::class.java) {
                        if (INSTANCE == null) {
                            INSTANCE = Repository()
                        }
                    }
                }
                return INSTANCE
            }
    }
}

Vamos ver o que o conversor automático fez:

Bloco init

No Kotlin, o construtor principal não pode conter nenhum código, então o código de inicialização é colocado dentro de blocos init. A funcionalidade é a mesma.

class Repository private constructor() {
    ...
    init {

        val user1 = User("Jane", "")
        val user2 = User("John", null)
        val user3 = User("Anne", "Doe")

        users = ArrayList()
        users!!.add(user1)
        users.add(user2)
        users.add(user3)
    }

}

Grande parte do código init manipula as propriedades de inicialização. Isso também pode ser feito na declaração da propriedade. Por exemplo, na versão Kotlin de nossa classe Repository, vemos que a propriedade users foi inicializada na declaração.

private val users: MutableList<User>? = null

Propriedades e métodos estáticos (static) em Kotlin

Em Java, usamos a palavra-chave static para campos ou funções para dizer que eles pertencem a uma classe, mas não a uma instância da classe. É por isso que criamos o campo estático INSTANCE na nossa classe Repository. O equivalente Kotlin para isso é o bloco companion object. Aqui você também declararia os campos e funções estáticas. O conversor criou e moveu o campo INSTANCE para aqui.

Lidando com singletons

Como precisamos apenas de uma instância da classe Repository, usamos o padrão singleton em Java. Com o Kotlin, você pode impor esse padrão no nível do compilador, substituindo a palavra-chave class por object. Com isso, podemos agora remover o construtor privado e o objeto complementar (companion object).

object Repository {

    private val users: MutableList<User>? = null

    val formattedUserNames: List<String>
        get() {
            val userNames = ArrayList<String>(users!!.size)
            for ((firstName, lastName) in users) {
                val name: String?

                if (lastName != null) {
                    if (firstName != null) {
                        name = "$firstName $lastName"
                    } else {
                        name = lastName
                    }
                } else if (firstName != null) {
                    name = firstName
                } else {
                    name = "Unknown"
                }
                userNames.add(name)
            }
            return userNames
        }

    init {

        val user1 = User("Jane", "")
        val user2 = User("John", null)
        val user3 = User("Anne", "Doe")
        users = ArrayList()
        users!!.add(user1)
        users.add(user2)
        users.add(user3)
    }

    fun getUsers(): List<User>? {
        return users
    }
}

Ao usar a classe object, apenas chamamos funções e propriedades diretamente no objeto, assim:

val users = Repository.users

Desestruturação

Kotlin permite desestruturar um objeto em diversas variáveis, usando uma sintaxe chamada declaração desestruturada. Criamos múltiplas variáveis e podemos usá-las independentemente.

Por exemplo, as classes de dados suportam a desestruturação, de modo que o conversor automático desestruturou o objeto User no laço for. Isso nos permite trabalhar diretamente com os valores firstName e lastName:

 
for ((firstName, lastName) in users) {
       val name: String?

       if (lastName != null) {
          if (firstName != null) {
                name = "$firstName $lastName"
          } 
       ...

Ao converter a classe Repository para Kotlin, o conversor automático tornou a lista de usuários nullable (ou seja, que pode ser nula), porque não foi inicializado em um objeto quando foi declarado. Para todos os usos do objeto users, o operador de asserção não-nulo !! é usado. Ele converte qualquer variável para um tipo não nulo e lança uma exceção caso o valor seja nulo. Ao usar !!, você está arriscando ter exceções sendo lançadas em tempo de execução.

Em vez disso, prefira lidar com nulidade usando um destes métodos:

No nosso caso, sabemos que a lista de usuários não precisa ser anulável (nullable), já que é inicializada logo após o objeto ser construído, para que possamos instanciar diretamente o objeto quando o declaramos.

Ao criar instâncias de tipos de coleção, o Kotlin fornece várias funções auxiliares para tornar seu código mais legível e flexível. Aqui estamos usando um MutableList para users:

private val users: MutableList<User>? = null

Para simplificar, podemos usar a função mutableListOf(), fornecer o tipo de elemento da lista, remover a chamada de construtor ArrayList do bloco init e a declaração de tipo explícito da propriedade users:

private val users = mutableListOf<User>()

Com essa mudança, nossa propriedade users agora é não-nula (non-null), e podemos remover todos as ocorrências desnecessárias do operador !!.

Uma vez que lastName quanto firstName podem ser nulos, precisamos lidar com a nulidade quando construímos a lista de nomes de usuários formatados. O conversor automático tornou a variável de nome anulável, mas como queremos exibir "Unknown" se algum dos nomes estiver ausente, podemos tornar o nome não-nulo removendo o ? da declaração de tipo.

val name: String

Se o lastName é nulo, name é o firstName ou "Unknown":

if (lastName != null) {
    if (firstName != null) {
        name = "$firstName $lastName"
    } else {
        name = lastName
    }
} else if (firstName != null) {
    name = firstName
} else {
    name = "Unknown"
}

Isso pode ser escrito de forma mais idiomática usando o operador elvis ?:. O operador elvis retornará a expressão do lado esquerdo se não for nula, ou a expressão do lado direito se o lado esquerdo for nulo.

Portanto, no código a seguir, user.firstName é retornado se não for nulo. Se user.firstName for nulo, a expressão retorna o valor à direita, "Unknown":

if (lastName != null) {
    ...
} else {
    name = firstName ?: "Unknown"
}

Vamos aplicar o operador elvis ao nosso método get() do formattedUserNames também:

val formattedUserNames: List<String>
    get() {
        val userNames = ArrayList<String>(users.size)
        for ((firstName, lastName) in users) {
            val name: String

            if (lastName != null) {
                if (firstName != null) {
                    name = "$firstName $lastName"
                } else {
                    name = lastName ?: "Unknown"
                }
            } else {
                name = firstName ?: "Unknown"
            }
            userNames.add(name)
        }
        return userNames
    }

Kotlin facilita o trabalho com strings utilizando templates de string. Templates de string permitem que você faça referência à variáveis dentro de declarações de string.

O conversor automático atualizou a concatenação do nome e sobrenome para referenciar o nome da variável diretamente na string usando o símbolo $ e colocando a expressão entre { }.

// Java
name = user.getFirstName() + " " + user.getLastName();

// Kotlin
name = "${user.firstName} ${user.lastName}"

Em Kotlin if, when, for e while são expressões - elas retornam um valor. Sua IDE está até mostrando um aviso de que a tarefa deve ser retirada do if:

Vamos seguir a sugestão da IDE e levantar a tarefa para ambas as declarações if. A última linha da instrução if será atribuída. Assim, fica mais claro que o único objetivo desse bloco é inicializar o valor do nome:

name = if (firstName != null) {
      // faça algo
      firstName 
  }
// name = firstName 

Em seguida, vamos receber um aviso de que a declaração de name pode ser associada à atribuição. Vamos aplicar isso também. Como o tipo da variável de nome pode ser inferido, podemos remover a declaração de tipo explícita. Agora nosso formattedUserNames se parece com isso:

val formattedUserNames: List<String>
        get() {
            val userNames = ArrayList<String>(users.size)
            for ((firstName, lastName) in users) {
                val name = if (lastName != null) {
                    if (firstName != null) {
                        "$firstName $lastName"
                    } else {
                        lastName ?: "Unknown"
                    }
                } else {
                    firstName ?: "Unknown"
                }

                userNames.add(name)
            }
            return userNames
        }

Vamos dar uma olhada mais de perto no getter formattedUserNames e ver como podemos torná-lo mais idiomático. Neste momento o código faz o seguinte:

val formattedUserNames: List<String>
        get() {
            val userNames = ArrayList<String>(users.size)
            for ((firstName, lastName) in users) {
                val name = if (lastName != null) {
                    if (firstName != null) {
                        "$firstName $lastName"
                    } else {
                        lastName ?: "Unknown"
                    }
                } else {
                    firstName ?: "Unknown"
                }

                userNames.add(name)
            }
            return userNames
        }

O Kotlin fornece uma extensa lista de transformações de coleção que tornam o desenvolvimento mais rápido e seguro, expandindo os recursos da API de coleções do Java. Uma delas é a função de map. Essa função retorna uma nova lista contendo os resultados da aplicação da função de transformação fornecida para cada elemento na matriz (array) original. Portanto, em vez de criar uma nova lista e iterar a lista de usuários manualmente, podemos usar a função map e mover a lógica que temos no laço for para dentro do corpo do map. Por padrão, o nome do item de lista atual usado no map é it, mas para facilitar a leitura, você pode substituir o it pelo nome de sua preferência. No nosso caso, vamos chamá-la de user:

    
val formattedUserNames: List<String>
        get() {
            return users.map { user ->
                val name = if (user.lastName != null) {
                    if (user.firstName != null) {
                        "${user.firstName} ${user.lastName}"
                    } else {
                        user.lastName ?: "Unknown"
                    }
                }  else {
                    user.firstName ?: "Unknown"
                }
                name
            }
        }

Para simplificar ainda mais, podemos remover completamente a variável name:

    
val formattedUserNames: List<String>
        get() {
            return users.map { user ->
                if (user.lastName != null) {
                    if (user.firstName != null) {
                        "${user.firstName} ${user.lastName}"
                    } else {
                        user.lastName ?: "Unknown"
                    }
                }  else {
                    user.firstName ?: "Unknown"
                }
            }
        }

Vimos que o conversor automático substituiu a função getFormattedUserNames() por uma propriedade chamada formattedUserNames que possui um getter personalizado. Por baixo dos panos o Kotlin ainda gera um método getFormattedUserNames() que retorna um List.

Em Java, nossas propriedades de classe seriam expostas por meio de funções getter e setter. O Kotlin nos permite ter uma melhor diferenciação entre propriedades de uma classe, expressa com campos, e funcionalidades, ações que uma classe pode fazer, expressas com funções. No nosso caso, a classe Repository é muito simples e não realiza nenhuma ação, por isso só tem campos.

A lógica que foi definida na função Java getFormattedUserNames() agora é acionada ao chamar o getter da propriedade formattedUserNames do Kotlin.

Embora não tenhamos explicitamente um campo correspondente à propriedade formattedUserNames, o Kotlin nos fornece um campo de apoio automático denominado field, que podemos acessar, se necessário, de getters e setters personalizados.

Às vezes, no entanto, queremos alguma funcionalidade extra que o campo de apoio automático não forneça. Vamos passar por um exemplo a seguir.

Dentro de nossa classe Repository temos uma lista mutável de usuários que está sendo exposta na função getUsers que foi gerada a partir do nosso código Java:

fun getUsers(): List<User>? {
    return users
}

O problema aqui é que, ao retornar users, qualquer consumidor da classe Repository pode modificar nossa lista de usuários - isso não é uma boa ideia! Vamos consertar isso usando uma propriedade de apoio.

Primeiro, vamos renomear users para _users. Agora, adicione uma propriedade pública imutável que retorne uma lista de usuários. Vamos chamá-lo de users:

private val _users = mutableListOf<User>()
val users: List<User>
      get() = _users

Com essa alteração, a propriedade privada de _users se torna a propriedade de suporte para a propriedades pública de users. Fora da classe Repository, a lista _users não é modificável, pois os consumidores da classe só podem acessar a lista por meio de users.

No momento, a classe Repository sabe como formatar um nome de usuário para um objeto User. Mas se quisermos reutilizar a mesma lógica de formatação em outras classes, precisamos copiar e colar ou movê-la para a classe User.

O Kotlin fornece a capacidade de declarar funções e propriedades fora de qualquer classe, objeto ou interface. Por exemplo, a função mutableListOf() que usamos para criar uma nova instância de uma List é definida diretamente em Collections.kt da Biblioteca Padrão.

Em Java, sempre que você precisar de alguma funcionalidade de utilitário, você provavelmente criará uma classe Util e declarará essa funcionalidade como uma função estática. Em Kotlin você pode declarar funções de alto nível, sem ter uma classe. No entanto, o Kotlin também fornece a capacidade de criar funções de extensão. Essas são funções que estendem um certo tipo, mas são declaradas fora do tipo. Como tal, eles têm uma afinidade com esse tipo.

A visibilidade das funções e propriedades de extensão pode ser restrita usando modificadores de visibilidade. Eles restringem o uso somente a turmas que precisam das extensões e não poluem o namespace.

Para a classe User, podemos adicionar uma função de extensão que calcula o nome formatado ou podemos manter o nome formatado em uma propriedade de extensão. Pode ser adicionado fora da classe Repository, no mesmo arquivo:

// função de extensão
fun User.getFormattedName(): String {
    return if (lastName != null) {
        if (firstName != null) {
            "$firstName $lastName"
        } else {
            lastName ?: "Unknown"
        }
    } else {
        firstName ?: "Unknown"
    }
}

// propriedade de extensão
val User.userFormattedName: String
    get() {
        return if (lastName != null) {
            if (firstName != null) {
                "$firstName $lastName"
            } else {
                lastName ?: "Unknown"
            }
        } else {
            firstName ?: "Unknown"
        }
    }

// uso:
val user = User(...)
val name = user.getFormattedName()
val formattedName = user.userFormattedName

Podemos então usar as funções de extensão e as propriedades como se fizessem parte da classe User.

Como o nome formatado é uma propriedade do usuário e não uma funcionalidade da classe Repository, vamos usar a propriedade de extensão. Nosso arquivo Repository agora se parece com isto:

val User.formattedName: String
    get() {
        return if (lastName != null) {
            if (firstName != null) {
                "$firstName $lastName"
            } else {
                lastName ?: "Unknown"
            }
        } else {
            firstName ?: "Unknown"
        }
    }

object Repository {

    private val _users = mutableListOf<User>()
    val users: List<User>
      get() = _users

    val formattedUserNames: List<String>
        get() {
            return _users.map { user -> user.formattedName }
        }

    init {

        val user1 = User("Jane", "")
        val user2 = User("John", null)
        val user3 = User("Anne", "Doe")

        _users.add(user1)
        _users.add(user2)
        _users.add(user3)
    }
}

A Biblioteca Padrão do Kotlin usa funções de extensão para estender a funcionalidade de várias APIs do Java; muitas das funcionalidades do Iterable e Collection são implementadas como funções de extensão. Por exemplo, a função map que usamos anteriormente é uma função de extensão em Collection.

Em nosso código de classe Repository, estamos adicionando vários objetos de usuário à lista _users. Essas chamadas podem ser mais idiomáticas com a ajuda de funções de escopo.

Para executar código apenas no contexto de um objeto específico, sem precisar acessar o objeto com base em seu nome, o Kotlin criou 5 funções de escopo: let, apply, with, run e also. Curto e poderoso, todas essas funções têm um receptor (this), podem ter um argumento (it) e podem retornar um valor. Você decidirá qual usar, dependendo do que deseja alcançar.

Aqui está uma folha de dicas (cheat sheet) útil para ajudá-lo a lembrar disso:

Como estamos configurando nosso objeto _users em nosso Repository, podemos tornar o código mais idiomático usando a função apply:

init {
    val user1 = User("Jane", "")
    val user2 = User("John", null)
    val user3 = User("Anne", "Doe")
   
    _users.apply {
       // this == _users
       add(user1)
       add(user2)
       add(user3)
    }
 }

Neste codelab, cobrimos as noções básicas que você precisa para começar a refatorar seu código de Java para Kotlin. Essa refatoração é independente da sua plataforma de desenvolvimento e ajuda a garantir que o código que você escreve seja idiomático.

O Kotlin idiomático torna a escrita de código curta e prazerosa. Com todas as funcionalidades que o Kotlin oferece, existem muitas formas de tornar o seu código mais seguro, mais conciso e mais legível. Por exemplo, podemos até otimizar nossa classe Repository instanciando a lista de _users com usuários diretamente na declaração, livrando-se do bloco init:

private val users = mutableListOf(User("Jane", ""), User("John", null), User("Anne", "Doe"))

Cobrimos uma grande variedade de tópicos, desde lidar com capacidade de nulidade, singletons, Strings e coleções até tópicos como funções de extensão, funções de alto nível, propriedades e funções de escopo. Nós fomos de duas classes Java para duas do Kotlin que agora se parecem com isso:

User.kt

class User(var firstName: String?, var lastName: String?)

Repository.kt

val User.formattedName: String
    get() {
       return if (lastName != null) {
            if (firstName != null) {
                "$firstName $lastName"
            } else {
                lastName ?: "Unknown"
            }
        } else {
            firstName ?: "Unknown"
        }
    }

object Repository {

    private val _users = mutableListOf(User("Jane", ""), User("John", null), User("Anne", "Doe"))
    val users: List<User>
        get() = users

    val formattedUserNames: List<String>
        get() {
            _users.map { user -> user.formattedName }
        }
}

Aqui está um resumo das funcionalidades em Java e seu mapeamento para Kotlin:

Java

Kotlin

Objeto final

Objeto val

equals()

==

==

===

Classe que só mantém os dados

Classe data

Initialização no constructor

Initialização no bloco init

Campos e funções static

Campos e funções declaradas em um companion object

Classe singleton

object

Para saber mais sobre o Kotlin e como usá-lo em sua plataforma, confira estes materiais: