Usar listas no Kotlin

Fazer listas para situações cotidianas é algo comum, como uma lista de afazeres, de convidados para um evento, de pedidos ou de compras. Em programação, as listas também são muito úteis. Por exemplo, um app pode ter listas de notícias, músicas, eventos da agenda ou postagens de mídia social.

Aprender a criar e usar listas é um conceito de programação importante para incluir na suas habilidades, permitindo que você crie apps mais sofisticados.

Neste codelab, você usará o Playground Kotlin para se familiarizar com listas e criar um programa para pedir diferentes variações de sopa de macarrão oriental. Já está com fome?

Pré-requisitos

  • Ter familiaridade com o Playground Kotlin para criar e editar programas Kotlin.
  • Ter familiaridade com os conceitos básicos de programação Kotlin da Unidade 1 do curso Noções básicas do Android no Kotlin: a função main(), argumentos de funções, valores de retorno, variáveis, tipos de dados e operações, além de instruções de fluxo de controle.
  • Saber definir uma classe Kotlin, criar uma instância de objeto e acessar as propriedades e os métodos dela.
  • Saber criar subclasses e entender como elas são herdadas umas das outras.

O que você aprenderá

  • Como criar e usar listas no Kotlin.
  • A diferença entre a List e a MutableList e quando usar cada uma delas.
  • Como iterar todos os itens de uma lista e realizar uma ação em cada item.

O que você criará

  • Você fará experimentos com listas e operações de lista no Playground Kotlin.
  • Você criará um programa para pedir comida que usa listas no Playground Kotlin.
  • Seu programa poderá criar um pedido, adicionar macarrão e legumes a ele e calcular o custo total.

O que é necessário

Nos codelabs anteriores, você aprendeu sobre os tipos de dados básicos do Kotlin, como Int, Double, Boolean e String. Eles permitem o armazenamento de certo tipo de valor em uma variável. Mas e se você quiser armazenar mais de um valor? Nesses casos, é útil ter o tipo de dados List.

Uma lista é um conjunto de itens em uma ordem específica. O Kotlin tem dois tipos de listas:

  • Lista somente leitura: a List não pode ser modificada depois de criada.
  • Lista mutável: a MutableList pode ser modificada depois de criada. Isso significa que você pode adicionar, remover ou atualizar os elementos dela.

Ao usar a List ou a MutableList, é necessário especificar o tipo de elemento que ela pode conter. Por exemplo, a List<Int> contém uma lista de números inteiros e a List<String> contém uma lista de strings. Se você definir uma classe Car em seu programa, poderá criar uma List<Car> com uma lista de instâncias de objetos Car.

A melhor maneira de entender as listas é testando.

Criar uma lista

  1. Abra o Playground Kotlin e exclua o código fornecido.
  2. Crie uma função main() vazia. Todas as etapas de código a seguir serão inseridas nessa função main().
fun main() {

}
  1. Dentro da main(), crie uma variável chamada numbers do tipo List<Int>, porque ela conterá uma lista somente leitura de números inteiros. Crie uma nova List usando a função listOf() (link em inglês) da biblioteca padrão do Kotlin e transmita os elementos da lista como argumentos separados por vírgulas. A listOf(1, 2, 3, 4, 5, 6) retornará uma lista somente leitura de números inteiros de 1 a 6.
val numbers: List<Int> = listOf(1, 2, 3, 4, 5, 6)
  1. Se o tipo de variável puder ser deduzido (ou inferido) com base no valor no lado direito do operador de atribuição (=), será possível omitir o tipo de dados da variável. Dessa forma, você poderá reduzir essa linha de código da seguinte maneira:
val numbers = listOf(1, 2, 3, 4, 5, 6)
  1. Use a println() para exibir a lista numbers.
println("List: $numbers")

Se você inserir "$" na string, a expressão seguinte será avaliada e adicionada a essa string. Consulte Modelos de string (link em inglês). Essa linha de código também pode ser escrita como println("List: " + numbers).

  1. Recupere o tamanho de uma lista usando a propriedade numbers.size e exiba-o também.
println("Size: ${numbers.size}")
  1. Execute seu programa. A saída mostra todos os elementos e o tamanho da lista. Observe os colchetes [], indicando que se trata de uma List. Dentro dos colchetes estão os elementos da numbers, separados por vírgulas. Além disso, observe que os elementos estão na mesma ordem em que foram criados.
List: [1, 2, 3, 4, 5, 6]
Size: 6

Acessar elementos da lista

A funcionalidade específica das listas é que você pode acessar cada elemento delas pelo índice, que é um número inteiro que representa a posição do elemento. Este é um diagrama da lista numbers que criamos, mostrando cada elemento e o índice correspondente.

cb6924554804458d.png

O índice é um deslocamento do primeiro elemento. Por exemplo, quando você usa list[2], não está pedindo o segundo elemento da lista, mas o elemento que está duas posições além do primeiro elemento. Portanto, list[0] é o primeiro elemento (sem deslocamento), list[1] é o segundo elemento (deslocamento de uma posição), list[2] é o terceiro elemento (deslocamento de duas posições) e assim por diante.

Adicione o seguinte código após o já existente na função main(). Execute o código após cada etapa para verificar se o resultado é o esperado.

  1. Exiba o primeiro elemento da lista no índice 0. Você pode chamar a função get() com o índice desejado, como numbers.get(0), ou pode usar a sintaxe abreviada com colchetes ao redor do índice, como numbers[0].
println("First element: ${numbers[0]}")
  1. Em seguida, exiba o segundo elemento da lista no índice 1.
println("Second element: ${numbers[1]}")

Os valores de índice válidos ("indices") de uma lista vão de 0 até o último índice, que é igual ao tamanho da lista menos 1. Isso significa que, para sua lista numbers, os índices vão de 0 a 5.

  1. Exiba o último elemento da lista usando numbers.size - 1 para calcular o índice dele, que será 5. O quinto índice precisa retornar 6 como valor de saída.
println("Last index: ${numbers.size - 1}")
println("Last element: ${numbers[numbers.size - 1]}")
  1. O Kotlin também é compatível com as operações first() e last() em listas. Tente chamar numbers.first() e numbers.last() e veja a saída.
println("First: ${numbers.first()}")
println("Last: ${numbers.last()}")

Você perceberá que numbers.first() retornará o primeiro elemento da lista e numbers.last() retornará o último.

  1. Outra operação de lista útil é o método contains(), que verifica se determinado elemento está na lista. Por exemplo, se você tiver uma lista de nomes de funcionários de uma empresa, poderá usar o método contains() para verificar se determinado nome está na lista.

Na lista numbers, chame o método contains() com um dos números inteiros presentes nela. O numbers.contains(4) retornará o valor true. Em seguida, chame o método contains() com um número inteiro que não esteja em sua lista. O numbers.contains(7) retornará false.

println("Contains 4? ${numbers.contains(4)}")
println("Contains 7? ${numbers.contains(7)}")
  1. O código final ficará assim: Os comentários são opcionais.
fun main() {
    val numbers = listOf(1, 2, 3, 4, 5, 6)
    println("List: $numbers")
    println("Size: ${numbers.size}")

    // Access elements of the list
    println("First element: ${numbers[0]}")
    println("Second element: ${numbers[1]}")
    println("Last index: ${numbers.size - 1}")
    println("Last element: ${numbers[numbers.size - 1]}")
    println("First: ${numbers.first()}")
    println("Last: ${numbers.last()}")

    // Use the contains() method
    println("Contains 4? ${numbers.contains(4)}")
    println("Contains 7? ${numbers.contains(7)}")
}
  1. Execute o código. O resultado será este:
List: [1, 2, 3, 4, 5, 6]
Size: 6
First element: 1
Second element: 2
Last index: 5
Last element: 6
First: 1
Last: 6
Contains 4? true
Contains 7? false

Listas são somente leitura

  1. Exclua o código no Playground Kotlin e substitua pelo seguinte. A lista colors é iniciada como uma lista de três cores representadas por Strings.
fun main() {
    val colors = listOf("green", "orange", "blue")
}
  1. Não é possível adicionar ou mudar elementos em uma List somente leitura. Veja o que acontece se você tentar adicionar um item à lista ou modificar um elemento dela configurando-o como um novo valor.
colors.add("purple")
colors[0] = "yellow"
  1. Execute seu código e receberá várias mensagens de erro. Basicamente, os erros significam que o método add() não existe para a List e que você não tem autorização para mudar o valor de um elemento.

dd21aaccdf3528c6.png

  1. Remova o código incorreto.

Você viu inicialmente que não é possível mudar uma lista somente leitura. Porém, há várias operações em listas que não as mudam, mas retornam uma nova lista. Duas delas são reversed() e sorted(). A função reversed() retorna uma nova lista com os elementos na ordem inversa, e a sorted() retorna uma nova lista com os elementos ordenados em ordem crescente.

  1. Adicione o código para inverter a lista colors. Exiba a saída. Ela será uma nova lista com os elementos da colors na ordem inversa.
  2. Adicione uma segunda linha de código para exibir a list original. Assim, você verá que a lista original não mudou.
println("Reversed list: ${colors.reversed()}")
println("List: $colors")
  1. Esta será a saída das duas listas.
Reversed list: [blue, orange, green]
List: [green, orange, blue]
  1. Adicione o código para retornar uma versão ordenada de uma List usando a função sorted() (link em inglês).
println("Sorted list: ${colors.sorted()}")

A saída será uma lista nova de cores ordenadas em ordem alfabética. Legal!

Sorted list: [blue, green, orange]
  1. Também é possível testar a função sorted() em uma lista de números não ordenados.
val oddNumbers = listOf(5, 3, 7, 1)
println("List: $oddNumbers")
println("Sorted list: ${oddNumbers.sorted()}")
List: [5, 3, 7, 1]
Sorted list: [1, 3, 5, 7]

Agora, você já consegue entender a utilidade de criar listas. Mas seria bom poder modificar a lista após a criação. Por isso, vamos dar uma olhada nas listas mutáveis.

Listas mutáveis são aquelas que podem ser modificadas após a criação. Você pode adicionar, remover ou alterar itens. Também é possível fazer as mesmas operações das listas somente leitura. As listas mutáveis são do tipo MutableList, e é possível criá-las chamando mutableListOf() (links em inglês).

Criar uma MutableList

  1. Exclua o código existente na main().
  2. Na função main(), crie e atribua uma lista mutável vazia a uma variável val chamada entrees.
val entrees = mutableListOf()

O seguinte erro aparecerá se você tentar executar o código.

Not enough information to infer type variable T

Como mencionado anteriormente, quando você cria uma MutableList ou uma List, o Kotlin tenta inferir o tipo de elementos que a lista contém usando os argumentos transmitidos. Por exemplo, se você escrever listOf("noodles"), o Kotlin inferirá que você quer criar uma lista de Strings. Quando você inicializar uma lista vazia sem elementos, o Kotlin não poderá inferir o tipo deles, então será necessário declarar o tipo explicitamente. Para isso, adicione o tipo entre colchetes angulares logo depois de mutableListOf ou listOf. Na documentação (link em inglês), você pode ver isso representado como <T>, em que T significa o parâmetro do tipo.

  1. Corrija a declaração de variável para especificar que você quer criar uma lista mutável com o tipo String.
val entrees = mutableListOf<String>()

Outra forma de corrigir o erro é especificando antecipadamente o tipo de dados da variável.

val entrees: MutableList<String> = mutableListOf()
  1. Exiba a lista.
println("Entrees: $entrees")
  1. A saída mostrará [] para uma lista vazia.
Entrees: []

Adicionar elementos a uma lista

As listas mutáveis são úteis para adicionar, remover e atualizar elementos.

  1. Adicione "noodles" à lista usando entrees.add("noodles"). A função add() (link em inglês) retornará true se a adição do elemento à lista for bem-sucedida. Caso contrário, retornará false.
  2. Exiba a lista para confirmar se "noodles" foi realmente adicionado.
println("Add noodles: ${entrees.add("noodles")}")
println("Entrees: $entrees")

A saída será:

Add noodles: true
Entrees: [noodles]
  1. Adicione outro item "spaghetti" à lista.
println("Add spaghetti: ${entrees.add("spaghetti")}")
println("Entrees: $entrees")

A lista entrees resultante agora contém dois itens.

Add spaghetti: true
Entrees: [noodles, spaghetti]

Em vez de adicionar um elemento por vez usando add(), você pode adicionar vários elementos usando addAll() (link em inglês) e transmitir em uma lista.

  1. Crie uma lista de moreItems. Não será necessário mudá-la. Portanto, defina-a como val e imutável.
val moreItems = listOf("ravioli", "lasagna", "fettuccine")
  1. Usando addAll(), adicione todos os itens da nova lista à entrees. Exiba a lista resultante.
println("Add list: ${entrees.addAll(moreItems)}")
println("Entrees: $entrees")

A saída mostrará que a adição foi bem-sucedida. Agora a lista entrees tem um total de 5 itens.

Add list: true
Entrees: [noodles, spaghetti, ravioli, lasagna, fettuccine]
  1. Tente adicionar um número à lista.
entrees.add(10)

Isso falhará com um erro:

The integer literal does not conform to the expected type String

Isso ocorre porque a lista entrees espera elementos do tipo String e você está tentando adicionar um Int. Adicione apenas elementos do tipo de dados correto a uma lista. Caso contrário, ocorrerá um erro de build. Essa é uma das formas que o Kotlin garante a segurança de tipo do seu código.

  1. Remova a linha incorreta do código para compilá-lo.

Remover elementos de uma lista

  1. Chame remove() (link em inglês) para remover "spaghetti" da lista. Exiba a lista novamente.
println("Remove spaghetti: ${entrees.remove("spaghetti")}")
println("Entrees: $entrees")
  1. A remoção de "spaghetti" retorna "true" porque o elemento estava presente na lista e pôde ser removido. Agora a lista só tem quatro itens restantes.
Remove spaghetti: true
Entrees: [noodles, ravioli, lasagna, fettuccine]
  1. O que acontece se você tentar remover um item que não está na lista? Tente remover "rice" da lista usando entrees.remove("rice").
println("Remove item that doesn't exist: ${entrees.remove("rice")}")
println("Entrees: $entrees")

O método remove() retornará false porque o elemento não existe e, portanto, não pode ser removido. A lista permanecerá inalterada com apenas quatro itens. Saída:

Remove item that doesn't exist: false
Entrees: [noodles, ravioli, lasagna, fettuccine]
  1. Também é possível especificar o índice do elemento a ser removido. Use removeAt() (link em inglês) para remover o item no índice 0.
println("Remove first element: ${entrees.removeAt(0)}")
println("Entrees: $entrees")

O valor de retorno de removeAt(0) é o primeiro elemento ("noodles") que foi removido da lista. Agora, a lista entrees tem três itens restantes.

Remove first element: noodles
Entrees: [ravioli, lasagna, fettuccine]
  1. Se você quiser limpar a lista inteira, chame clear() (link em inglês).
entrees.clear()
println("Entrees: $entrees")

A saída mostrará uma lista vazia agora.

Entrees: []
  1. O Kotlin oferece uma maneira de verificar se uma lista está vazia usando a função isEmpty() (link em inglês). Tente exibir entrees.isEmpty().
println("Empty? ${entrees.isEmpty()}")

A saída será "true" porque a lista está vazia, sem elementos.

Empty? true

O método isEmpty() é útil se você quiser realizar uma operação em uma lista ou acessar determinado elemento, mas quiser verificar se ela não está vazia primeiro.

Veja todo o código que você criou para listas mutáveis. Os comentários são opcionais.

fun main() {
    val entrees = mutableListOf<String>()
    println("Entrees: $entrees")

    // Add individual items using add()
    println("Add noodles: ${entrees.add("noodles")}")
    println("Entrees: $entrees")
    println("Add spaghetti: ${entrees.add("spaghetti")}")
    println("Entrees: $entrees")

    // Add a list of items using addAll()
    val moreItems = listOf("ravioli", "lasagna", "fettuccine")
    println("Add list: ${entrees.addAll(moreItems)}")
    println("Entrees: $entrees")

    // Remove an item using remove()
    println("Remove spaghetti: ${entrees.remove("spaghetti")}")
    println("Entrees: $entrees")
    println("Remove item that doesn't exist: ${entrees.remove("rice")}")
    println("Entrees: $entrees")

    // Remove an item using removeAt() with an index
    println("Remove first element: ${entrees.removeAt(0)}")
    println("Entrees: $entrees")

    // Clear out the list
    entrees.clear()
    println("Entrees: $entrees")

    // Check if the list is empty
    println("Empty? ${entrees.isEmpty()}")
}

Para fazer uma operação em cada item de uma lista, é possível fazer uma repetição nela (também conhecido como iterar a lista). As repetições podem ser usadas com as Lists e as MutableLists.

Repetições while

Um tipo de repetição é a while (link em inglês). Uma repetição while começa com a palavra-chave while no Kotlin. Ela contém um bloco de código (entre chaves) que é executado diversas vezes, desde que a expressão entre parênteses seja verdadeira. Para evitar que o código seja executado indefinidamente (o que é chamado de repetição infinita), o bloco de código precisa conter uma lógica que mude o valor da expressão, para que ela se torne falsa e a repetição termine. Nesse ponto, a repetição while é concluída, e a execução do código que vem depois dela continua.

while (expression) {
    // While the expression is true, execute this code block
}

Use uma repetição while para iterar uma lista. Crie uma variável para acompanhar para qual index da lista você está olhando. Essa variável index continuará aumentando em incrementos de 1 a cada repetição até chegar ao último índice da lista. Depois disso, a repetição será encerrada.

  1. Exclua o código existente no Playground Kotlin e use uma função main() vazia.
  2. Digamos que você esteja organizando uma festa. Crie uma lista em que cada elemento representa o número de convidados confirmados de cada família. A primeira família disse que duas pessoas iriam à festa. A segunda família disse que quatro iriam e assim por diante.
val guestsPerFamily = listOf(2, 4, 1, 3)
  1. Saiba quantos convidados haverá no total. Crie uma repetição para encontrar a resposta. Crie uma var para o número total de convidados e inicialize-a em 0.
var totalGuests = 0
  1. Inicialize uma var para a variável index, conforme descrito anteriormente.
var index = 0
  1. Crie uma repetição while para iterar a lista. A condição é continuar executando o bloco de código, desde que o valor index seja menor que o tamanho da lista.
while (index < guestsPerFamily.size) {

}
  1. Na repetição, acesse o elemento da lista no index atual e adicione-o à variável do número total de convidados. totalGuests += guestsPerFamily[index] é igual a totalGuests = totalGuests + guestsPerFamily[index].

Observe que a última linha da repetição aumenta o valor da variável index em 1 usando index++. Assim, a próxima iteração da repetição será executada na próxima família da lista.

while (index < guestsPerFamily.size) {
    totalGuests += guestsPerFamily[index]
    index++
}
  1. Após a repetição while, você poderá exibir o resultado.
while ... {
    ...
}
println("Total Guest Count: $totalGuests")
  1. Execute o programa e a saída será a seguinte. Você pode verificar se essa é a resposta correta adicionando manualmente os números da lista.
Total Guest Count: 10

Veja o snippet de código completo:

val guestsPerFamily = listOf(2, 4, 1, 3)
var totalGuests = 0
var index = 0
while (index < guestsPerFamily.size) {
    totalGuests += guestsPerFamily[index]
    index++
}
println("Total Guest Count: $totalGuests")

Em uma repetição while, era necessário escrever código para criar uma variável para acompanhar o índice, para acessar o elemento no índice da lista e para atualizar essa variável. Há um jeito mais rápido e conciso de iterar uma lista. Use repetições for.

Repetições for

Outro tipo de repetição é o for (link em inglês). Ele facilita muito a repetição da lista. No Kotlin, ele começa com a palavra-chave for com o bloco de código entre chaves. A condição para executar esse bloco é indicada entre parênteses.

for (number in numberList) {
   // For each element in the list, execute this code block
}

Neste exemplo, a variável number está definida como igual ao primeiro elemento da numberList e o bloco de código é executado. Em seguida, a variável number é atualizada automaticamente para ser o próximo elemento da numberList e o bloco de código é executado novamente. Isso se repete para cada elemento da lista até o fim da numberList.

  1. Exclua o código existente no Playground Kotlin e substitua-o por este:
fun main() {
    val names = listOf("Jessica", "Henry", "Alicia", "Jose")
}
  1. Adicione uma repetição for para exibir todos os itens da lista names.
for (name in names) {
    println(name)
}

É muito mais fácil do que se você tivesse que escrever isso com uma repetição while.

  1. A saída será:
Jessica
Henry
Alicia
Jose

Uma operação comum em listas é fazer algo em cada elemento dela.

  1. Modifique a repetição para exibir o número de caracteres do nome da pessoa. Dica: você pode usar a propriedade length (link em inglês) de uma String para descobrir o número de caracteres dessa String.
val names = listOf("Jessica", "Henry", "Alicia", "Jose")
for (name in names) {
    println("$name - Number of characters: ${name.length}")
}

Saída:

Jessica - Number of characters: 7
Henry - Number of characters: 5
Alicia - Number of characters: 6
Jose - Number of characters: 4

O código na repetição não mudou a List original. Ele afetou apenas o que foi exibido.

É muito útil poder escrever as instruções sobre o que precisa acontecer para um item da lista e o código ser executado para todos os itens dela. O uso de uma repetição pode evitar que você digite muito código repetitivo.

Agora que você já testou a criação e o uso de listas somente leitura e mutáveis, além de aprender sobre repetições, é hora de aplicar esse conhecimento em um caso de uso de exemplo.

Quando um cliente pede comida em um restaurante local, ele geralmente escolhe vários itens em um único pedido. O uso de listas é ideal para armazenar as informações do pedido. Você também usará seu conhecimento sobre classes e herança para criar um programa Kotlin mais robusto e escalonável, em vez de criar todo o código na função main().

Para a próxima série de tarefas, crie um programa Kotlin que permita pedir diferentes combinações de comida.

Primeiro, veja este exemplo de saída do código final. Você pode imaginar quais tipos de classes precisa criar para organizar todos esses dados?

Order #1
Noodles: $10
Total: $10

Order #2
Noodles: $10
Vegetables Chef's Choice: $5
Total: $15

A partir da saída, você verá que:

  • há uma lista de pedidos;
  • cada pedido tem um número;
  • cada pedido pode conter uma lista de itens, como macarrão e vegetais;
  • cada item tem um preço;
  • cada pedido tem um preço total, que é a soma dos preços dos itens individuais.

Você pode criar uma classe para representar um Order e uma classe para representar cada item de comida, como Noodles ou Vegetables. Também é possível observar que Noodles e Vegetables têm algumas semelhanças, porque são itens de comida e têm um preço. Você pode criar uma classe Item com propriedades compartilhadas que possam ser herdadas pelas classes Noodle e Vegetable. Dessa forma, não é necessário duplicar a lógica nas classes Noodle e Vegetable.

  1. O seguinte código inicial é fornecido. Os desenvolvedores profissionais geralmente precisam ler o código de outras pessoas, por exemplo, se estão participando de um novo projeto ou contribuindo com um recurso que outra pessoa criou. Ser capaz de ler e entender o código é uma habilidade importante.

Reserve um tempo para examinar esse código e entender o que está acontecendo. Copie e cole este código no Playground Kotlin e execute-o. Lembre-se de excluir qualquer código existente no Playground Kotlin antes de colar esse novo código. Observe a saída e veja se ela ajuda a entender melhor o código.

open class Item(val name: String, val price: Int)

class Noodles : Item("Noodles", 10)

class Vegetables : Item("Vegetables", 5)

fun main() {
    val noodles = Noodles()
    val vegetables = Vegetables()
    println(noodles)
    println(vegetables)
}
  1. Você deverá obter uma saída similar a:
Noodles@5451c3a8
Vegetables@76ed5528

Veja a seguir uma explicação mais detalhada do código. Primeiro, há uma classe chamada Item, em que o construtor aceita dois parâmetros: um name para o item (como uma string) e um price (como um número inteiro). As duas propriedades não mudam depois que são transmitidas. Por isso, elas são marcadas como val. Como Item é uma classe pai e as subclasses se estendem dela, a classe é marcada com a palavra-chave open.

O construtor da classe Noodles não recebe parâmetros, mas os estende da classe Item e chama o construtor da superclasse transmitindo "Noodles" como o nome e 10 como o preço. A classe Vegetables é semelhante, mas chama o construtor da superclasse com o nome "Vegetables" e o preço de 5.

A função main() inicializa as novas instâncias de objeto das classes Noodles e Vegetables e as exibe na saída.

Substituir o método toString()

Quando você exibe uma instância de objeto na saída, o método toString() do objeto é chamado. No Kotlin, todas as classes herdam o método toString() automaticamente. A implementação padrão desse método retorna apenas o tipo de objeto com um endereço de memória para a instância. Modifique toString() para retornar algo mais significativo e fácil de usar que Noodles@5451c3a8 e Vegetables@76ed5528.

  1. Na classe Noodles, modifique o método toString() e faça com que ele retorne o name. Lembre-se que a classe Noodles herda a propriedade name da classe pai Item.
class Noodles : Item("Noodles", 10) {
   override fun toString(): String {
       return name
   }
}
  1. Repita o procedimento para a classe Vegetables.
class Vegetables() : Item("Vegetables", 5) {
   override fun toString(): String {
       return name
   }
}
  1. Execute o código. A saída está melhor agora:
Noodles
Vegetables

Na próxima etapa, você mudará o construtor da classe Vegetables para incluir alguns parâmetros e atualizará o método toString() para refletir essas outras informações.

Personalizar os vegetais em um pedido

Para que as sopas de macarrão oriental sejam mais interessantes, você pode incluir diferentes vegetais nos pedidos.

  1. Na função main(), em vez de inicializar uma instância de Vegetables sem argumentos de entrada, transmita os tipos específicos de vegetais que o cliente quer.
fun main() {
    ...
    val vegetables = Vegetables("Cabbage", "Sprouts", "Onion")
    ...
}

Se você tentar compilar seu código agora, verá o seguinte erro:

Too many arguments for public constructor Vegetables() defined in Vegetables

Agora, você está transmitindo três argumentos String para o construtor da classe Vegetables, então precisa modificar a classe Vegetables.

  1. Atualize o cabeçalho da classe Vegetables para receber três (3) parâmetros de string, conforme mostrado no código a seguir:
class Vegetables(val topping1: String,
                 val topping2: String,
                 val topping3: String) : Item ("Vegetables", 5) {
  1. Agora, seu código será compilado novamente. No entanto, essa solução só funcionará se os clientes sempre quiserem pedir exatamente três vegetais. Quando um cliente quiser pedir um ou cinco vegetais, ele não conseguirá.
  2. Em vez de usar uma propriedade para cada vegetal, você pode corrigir o problema aceitando uma lista de vegetais (que pode ser de qualquer tamanho) no construtor da classe Vegetables. A List precisa conter apenas Strings, portanto, o tipo do parâmetro de entrada é List<String>.
class Vegetables(val toppings: List<String>) : Item("Vegetables", 5) {

Essa não é a solução mais elegante porque, na main(), seria preciso mudar o código para criar uma lista de ingredientes antes de transmiti-la ao construtor Vegetables.

Vegetables(listOf("Cabbage", "Sprouts", "Onion"))

Existe uma forma ainda melhor de resolver isso.

  1. No Kotlin, o modificador vararg permite transmitir um número variável de argumentos do mesmo tipo em uma função ou construtor. Dessa forma, você pode fornecer vegetais como strings individuais, em vez de uma lista.

Mude a definição da classe Vegetables para receber vararg toppings do tipo String.

class Vegetables(vararg val toppings: String) : Item("Vegetables", 5) {
  1. Este código na função main() funcionará agora. É possível criar uma instância de Vegetables transmitindo qualquer número de strings de ingredientes.
fun main() {
    ...
    val vegetables = Vegetables("Cabbage", "Sprouts", "Onion")
    ...
}
  1. Agora, modifique o método toString() da classe Vegetables para que retorne uma String que também mencione os ingredientes neste formato: Vegetables Cabbage, Sprouts, Onion.

Comece pelo nome do item (Vegetables). Em seguida, use o método joinToString() para unir todos os ingredientes em uma única string. Una essas duas partes usando o operador + com um espaço no meio.

class Vegetables(vararg val toppings: String) : Item("Vegetables", 5) {
    override fun toString(): String {
        return name + " " + toppings.joinToString()
    }
}
  1. Execute seu programa. A saída será:
Noodles
Vegetables Cabbage, Sprouts, Onion
  1. Ao criar programas, é preciso considerar todas as entradas possíveis. Quando não houver argumentos de entrada para o construtor Vegetables, processe o método toString() de uma maneira mais fácil.

Como o cliente quer vegetais, mas não disse quais, uma solução é oferecer a ele opções padrão escolhidas pelo chef.

Atualize o método toString() para retornar Vegetables Chef's Choice se nenhum ingrediente for transmitido. Use o método isEmpty() que você aprendeu anteriormente.

override fun toString(): String {
    if (toppings.isEmpty()) {
        return "$name Chef's Choice"
    } else {
        return name + " " + toppings.joinToString()
    }
}
  1. Atualize a função main() para testar as duas possibilidades de criação de uma instância Vegetables sem nenhum argumento do construtor e com vários argumentos.
fun main() {
    val noodles = Noodles()
    val vegetables = Vegetables("Cabbage", "Sprouts", "Onion")
    val vegetables2 = Vegetables()
    println(noodles)
    println(vegetables)
    println(vegetables2)
}
  1. Verifique se o resultado é o esperado.
Noodles
Vegetables Cabbage, Sprouts, Onion
Vegetables Chef's Choice

Criar um pedido

Agora que você já tem alguns itens de comida, pode criar um pedido. Encapsule a lógica de um pedido em uma classe Order no programa.

  1. Pense em quais propriedades e métodos fazem sentido para a classe Order. Se isso ajudar, veja um exemplo da saída do código final novamente:
Order #1
Noodles: $10
Total: $10

Order #2
Noodles: $10
Vegetables Chef's Choice: $5
Total: $15

Order #3
Noodles: $10
Vegetables Carrots, Beans, Celery: $5
Total: $15

Order #4
Noodles: $10
Vegetables Cabbage, Onion: $5
Total: $15

Order #5
Noodles: $10
Noodles: $10
Vegetables Spinach: $5
Total: $25
  1. Talvez você tenha pensado no seguinte:

Classe pedido

Propriedades: número do pedido, lista de itens

Métodos: adicionar item, adicionar vários itens, exibir o resumo do pedido (incluindo o preço)

  1. Pensando primeiro nas propriedades, qual é o tipo de dado de cada uma delas? As propriedades precisam ser públicas ou privadas para a classe? Elas precisam ser transmitidas como argumentos ou definidas na classe?
  2. Há várias maneiras de implementar isso, veja uma possível solução. Crie uma class Order que tenha um parâmetro de construtor orderNumber inteiro.
class Order(val orderNumber: Int)
  1. Como você talvez não conheça todos os itens do pedido antecipadamente, não peça que a lista de itens seja transmitida como argumento. Em vez disso, ela pode ser declarada como uma variável de classe de nível superior e inicializada como uma MutableList vazia que pode conter elementos do tipo Item. Marque a variável como private para que apenas essa classe possa modificar a lista de itens diretamente. Isso impedirá que a lista seja modificada de modos inesperados por códigos fora dessa classe.
class Order(val orderNumber: Int) {
    private val itemList = mutableListOf<Item>()
}
  1. Adicione os métodos à definição de classe também. Você pode escolher nomes comuns para cada método e pode deixar a lógica de implementação de cada método em branco por enquanto. Decida também quais argumentos da função e valores de retorno são necessários.
class Order(val orderNumber: Int) {
   private val itemList = mutableListOf<Item>()

   fun addItem(newItem: Item) {
   }

   fun addAll(newItems: List<Item>) {
   }

   fun print() {
   }
}
  1. O método addItem() parece ser o mais simples. Portanto, implemente essa função primeiro. Ela usa um novo Item, e o método precisa adicioná-lo à itemList.
fun addItem(newItem: Item) {
    itemList.add(newItem)
}
  1. Em seguida, implemente o método addAll(). Ele recebe uma lista somente leitura de itens. Adicione todos esses itens à lista interna de itens.
fun addAll(newItems: List<Item>) {
    itemList.addAll(newItems)
}
  1. Em seguida, implemente o método print(), que exibe um resumo de todos os itens e preços na saída, assim como o preço total do pedido.

Primeiro exiba o número do pedido. Depois, use uma repetição para iterar em todos os itens da lista de pedidos. Exiba cada item e o respectivo preço. Guarde também o preço total até o momento e continue adicionando itens a ele ao iterar a lista. Exiba o preço total no final. Tente implementar essa lógica por conta própria. Se precisar de ajuda, verifique a solução abaixo.

Recomendamos que você inclua o símbolo da moeda para facilitar a leitura da saída. Veja uma maneira de implementar a solução. Este código usa o símbolo de moeda $, mas você pode modificá-lo para sua moeda local.

fun print() {
    println("Order #${orderNumber}")
    var total = 0
    for (item in itemList) {
        println("${item}: $${item.price}")
        total += item.price
    }
    println("Total: $${total}")
}

Para cada item na itemList, exiba o item (que aciona o método toString() a ser chamado no item), seguido pelo price do item. Também antes da repetição, inicialize uma variável inteira total para que seja 0. Depois, adicione o preço do item atual ao valor total do pedido.

Criar pedidos

  1. Teste o código criando instâncias de Order na função main(). Primeiro, exclua o que você já criou na função main().
  2. Você pode usar esses exemplos de pedidos ou criar o seu. Experimente diferentes combinações de itens nos pedidos e teste todos os caminhos de código. Por exemplo, teste os métodos addItem() e addAll() na classe Order, crie instâncias de Vegetables sem argumentos e com argumentos e assim por diante.
fun main() {
    val order1 = Order(1)
    order1.addItem(Noodles())
    order1.print()

    println()

    val order2 = Order(2)
    order2.addItem(Noodles())
    order2.addItem(Vegetables())
    order2.print()

    println()

    val order3 = Order(3)
    val items = listOf(Noodles(), Vegetables("Carrots", "Beans", "Celery"))
    order3.addAll(items)
    order3.print()
}
  1. A saída para o código acima será a seguinte. Verifique se o preço total está aumentando corretamente.
Order #1
Noodles: $10
Total: $10

Order #2
Noodles: $10
Vegetables Chef's Choice: $5
Total: $15

Order #3
Noodles: $10
Vegetables Carrots, Beans, Celery: $5
Total: $15

Muito bem! Já estão parecendo pedidos de comida.

Guardar uma lista de pedidos

Se você estiver criando um programa para ser realmente usado em uma loja de macarrão oriental, é útil manter uma lista de todos os pedidos dos clientes.

  1. Crie uma lista para armazenar todos os pedidos. É uma lista somente leitura ou mutável?
  2. Adicione esse código à função main(). Inicialize a lista para que ela comece vazia. Depois que cada pedido for criado, adicione-o à lista.
fun main() {
    val ordersList = mutableListOf<Order>()

    val order1 = Order(1)
    order1.addItem(Noodles())
    ordersList.add(order1)

    val order2 = Order(2)
    order2.addItem(Noodles())
    order2.addItem(Vegetables())
    ordersList.add(order2)

    val order3 = Order(3)
    val items = listOf(Noodles(), Vegetables("Carrots", "Beans", "Celery"))
    order3.addAll(items)
    ordersList.add(order3)
}

Como os pedidos são adicionados ao longo do tempo, a lista precisa ser uma MutableList do tipo Order. Em seguida, use o método add() na MutableList para adicionar cada pedido.

  1. Após criar uma lista de pedidos, você pode usar uma repetição para exibir cada um deles. Exiba uma linha em branco entre os pedidos para facilitar a leitura da saída.
fun main() {
    val ordersList = mutableListOf<Order>()

    ...

    for (order in ordersList) {
        order.print()
        println()
    }
}

Isso remove o código duplicado na função main() e facilita a leitura do código. A saída precisa ser a mesma de antes.

Implementar um padrão Builder para pedidos

Para tornar seu código Kotlin mais conciso, use o padrão Builder na criação de pedidos. O Builder é um padrão de design na programação que permite criar um objeto complexo passo a passo.

  1. Em vez de retornar uma Unit (ou nada) dos métodos addItem() e addAll() na classe Order, retorne o Order alterado. O Kotlin oferece a palavra-chave this para fazer referência à instância de objeto atual. Nos métodos addItem() e addAll(), o Order atual é retornado por this.
fun addItem(newItem: Item): Order {
    itemList.add(newItem)
    return this
}

fun addAll(newItems: List<Item>): Order {
    itemList.addAll(newItems)
    return this
}
  1. Na função main(), agora você pode encadear as chamadas, conforme mostrado no código a seguir. Esse código cria um novo Order e usa o padrão Builder.
val order4 = Order(4).addItem(Noodles()).addItem(Vegetables("Cabbage", "Onion"))
ordersList.add(order4)

O Order(4) retorna uma instância de Order em que você pode chamar addItem(Noodles()). O método addItem() retorna a mesma instância de Order (com o novo estado), e você pode chamar o addItem() novamente nela com os vegetais. O resultado do Order retornado pode ser armazenado na variável order4.

O código existente para criar Orders ainda funciona, então não precisa ser alterado. Embora não seja obrigatório encadear essas chamadas, essa é uma prática comum e recomendada que usa o valor de retorno da função.

  1. Neste ponto, não é preciso armazenar o pedido em uma variável. Na função main() (antes da última repetição para exibir os pedidos), crie um Order diretamente e adicione-o à orderList. O código também será mais fácil de ler se cada chamada de método for colocada na própria linha.
ordersList.add(
    Order(5)
        .addItem(Noodles())
        .addItem(Noodles())
        .addItem(Vegetables("Spinach")))
  1. Execute o código. Esta é a saída esperada:
Order #1
Noodles: $10
Total: $10

Order #2
Noodles: $10
Vegetables Chef's Choice: $5
Total: $15

Order #3
Noodles: $10
Vegetables Carrots, Beans, Celery: $5
Total: $15

Order #4
Noodles: $10
Vegetables Cabbage, Onion: $5
Total: $15

Order #5
Noodles: $10
Noodles: $10
Vegetables Spinach: $5
Total: $25

Parabéns por concluir este codelab!

Agora você sabe como pode ser útil armazenar dados em listas, transformar listas e fazer repetições nelas. Aplique esse conhecimento no contexto de um app Android para exibir uma lista de dados na tela no próximo codelab.

Este é o código da solução para as classes Item, Noodles, Vegetables e Order. A função main() também mostra como usar essas classes. Há várias abordagens para implementar esse programa, portanto, seu código pode ser um pouco diferente.

open class Item(val name: String, val price: Int)

class Noodles : Item("Noodles", 10) {
    override fun toString(): String {
        return name
    }
}

class Vegetables(vararg val toppings: String) : Item("Vegetables", 5) {
    override fun toString(): String {
        if (toppings.isEmpty()) {
            return "$name Chef's Choice"
        } else {
            return name + " " + toppings.joinToString()
        }
    }
}

class Order(val orderNumber: Int) {
    private val itemList = mutableListOf<Item>()

    fun addItem(newItem: Item): Order {
        itemList.add(newItem)
        return this
    }

    fun addAll(newItems: List<Item>): Order {
        itemList.addAll(newItems)
        return this
    }

    fun print() {
        println("Order #${orderNumber}")
        var total = 0
        for (item in itemList) {
            println("${item}: $${item.price}")
            total += item.price
        }
        println("Total: $${total}")
    }
}

fun main() {
    val ordersList = mutableListOf<Order>()

    // Add an item to an order
    val order1 = Order(1)
    order1.addItem(Noodles())
    ordersList.add(order1)

    // Add multiple items individually
    val order2 = Order(2)
    order2.addItem(Noodles())
    order2.addItem(Vegetables())
    ordersList.add(order2)

    // Add a list of items at one time
    val order3 = Order(3)
    val items = listOf(Noodles(), Vegetables("Carrots", "Beans", "Celery"))
    order3.addAll(items)
    ordersList.add(order3)

    // Use builder pattern
    val order4 = Order(4)
        .addItem(Noodles())
        .addItem(Vegetables("Cabbage", "Onion"))
    ordersList.add(order4)

    // Create and add order directly
    ordersList.add(
        Order(5)
            .addItem(Noodles())
            .addItem(Noodles())
            .addItem(Vegetables("Spinach"))
    )

    // Print out each order
    for (order in ordersList) {
        order.print()
        println()
    }
}

O Kotlin oferece funcionalidades para ajudar a gerenciar e manipular conjuntos de dados com mais facilidade com a biblioteca padrão do Kotlin. Uma conjunto pode ser definido como uma série de objetos do mesmo tipo de dados. Existem vários tipos de conjuntos básicos no Kotlin: listas, conjuntos e mapas. Esse codelab se concentrou especificamente em listas. Você aprenderá mais sobre conjuntos e mapas em codelabs futuros.

  • Uma lista é um conjunto ordenado de elementos de um tipo específico, como uma lista de Strings..
  • O índice é a posição inteira que reflete a posição do elemento (por exemplo, myList[2]).
  • Em uma lista, o primeiro elemento fica no índice 0 (por exemplo, myList[0]) e o último no myList.size-1 (por exemplo, myList[myList.size-1] ou myList.last()).
  • Há dois tipos de listas: List e MutableList..
  • Uma List é somente leitura e não pode ser modificada depois de inicializada. No entanto, você pode aplicar operações como sorted() e reversed(), que retornam uma nova lista sem mudar a original.
  • Uma MutableList pode ser modificada após a criação, como ao adicionar, remover ou modificar elementos.
  • É possível adicionar uma lista de itens a uma lista mutável usando addAll().
  • Use uma repetição while para executar um bloco de código até que a expressão seja avaliada como falsa e a repetição seja concluída.

while (expression) {

// While the expression is true, execute this code block

}

  • Use uma repetição for para iterar todos os itens de uma lista:

for (item in myList) {

// Execute this code block for each element of the list

}

  • O modificador vararg permite que você transmita um número variável de argumentos para uma função ou um construtor.