Aula 01 — Programação para Web I
Você já parou para pensar no que acontece quando o sistema aceita um valor que não deveria existir? Hoje vamos ver três recursos do Java que resolvem isso por caminhos diferentes:
Queremos guardar a plataforma de um jogo, e a saída óbvia é usar texto.
public class Jogo { private String titulo; private String plataforma; public Jogo(String titulo, String plataforma) { this.titulo = titulo; this.plataforma = plataforma; } public String getPlataforma() { return plataforma; } }
Agora três jogos foram cadastrados.
Jogo j1 = new Jogo("Hollow Knight", "PC"); Jogo j2 = new Jogo("Celeste", "pc"); Jogo j3 = new Jogo("Hades", "Playstatiom"); if (j1.getPlataforma() == j2.getPlataforma()) { System.out.println("mesma plataforma"); } if (j1.getPlataforma().equals(j2.getPlataforma())) { System.out.println("mesma plataforma"); }
Jogo j1 = new Jogo("Hollow Knight", "PC"); Jogo j2 = new Jogo("Celeste", "pc"); Jogo j3 = new Jogo("Hades", "Playstatiom"); if (j1.getPlataforma() == j2.getPlataforma()) { System.out.println("mesma plataforma"); } if (j1.getPlataforma().equals(j2.getPlataforma())) { System.out.println("mesma plataforma"); }
Quando o conjunto de valores possíveis é fechado, podemos declará-lo por inteiro.
Ou seja, o compilador passa a conhecer a lista.
public enum Plataforma { PC, PLAYSTATION, XBOX, SWITCH }
A mesma classe, agora com o atributo tipado.
a última linha não compila
Ou seja, o erro saiu da execução e foi para a compilação.
public class Jogo { private String titulo; private Plataforma plataforma; public Jogo(String titulo, Plataforma plataforma) { this.titulo = titulo; this.plataforma = plataforma; } public Plataforma getPlataforma() { return plataforma; } } Jogo j1 = new Jogo("Hollow Knight", Plataforma.PC); Jogo j2 = new Jogo("Celeste", Plataforma.SWITCH); Jogo j3 = new Jogo("Hades", Plataforma.PLAYSTATIOM);
public class Jogo { private String titulo; private Plataforma plataforma; public Jogo(String titulo, Plataforma plataforma) { this.titulo = titulo; this.plataforma = plataforma; } public Plataforma getPlataforma() { return plataforma; } } Jogo j1 = new Jogo("Hollow Knight", Plataforma.PC); Jogo j2 = new Jogo("Celeste", Plataforma.SWITCH); Jogo j3 = new Jogo("Hades", Plataforma.PLAYSTATIOM);
Comparamos enums com ==. Cada valor de um enum existe uma única vez na memória, logo comparar a referência já compara o valor.
Com String a história é outra, porque ali precisamos de equals.
Plataforma p1 = Plataforma.PC; Plataforma p2 = Plataforma.PC; p1 == p2 // true p1.equals(p2) // true String s1 = "PC"; String s2 = new String("PC"); s1 == s2 // false s1.equals(s2) // true
Um enum oferece algumas operações que usamos com frequência:
Plataforma.values() // [PC, PLAYSTATION, XBOX, SWITCH] Plataforma.valueOf("PC") // Plataforma.PC Plataforma.PC.name() // "PC" Plataforma.PC.ordinal() // 0 for (Plataforma p : Plataforma.values()) { System.out.println(p); }
Um enum também pode carregar informação. Não precisamos disso hoje, mas aparece com frequência em código de mercado.
public enum Plataforma { PC("Computador"), PLAYSTATION("Sony"), XBOX("Microsoft"), SWITCH("Nintendo"); private final String fabricante; Plataforma(String fabricante) { this.fabricante = fabricante; } public String getFabricante() { return fabricante; } } Plataforma.XBOX.getFabricante()
Observe o código ao lado e responda:
public enum Situacao { AGENDADO, EM_ATENDIMENTO, CONCLUIDO, CANCELADO } Situacao s1 = Situacao.AGENDADO; Situacao s2 = Situacao.valueOf("AGENDADO"); Situacao s3 = Situacao.valueOf("agendado");
Resolvida a plataforma, queremos guardar vários jogos, e o array dá conta enquanto soubermos o tamanho. Observe o código ao lado.
Jogo[] jogos = new Jogo[3]; jogos[0] = new Jogo("Hollow Knight", Plataforma.PC); jogos[1] = new Jogo("Celeste", Plataforma.SWITCH); jogos[2] = new Jogo("Hades", Plataforma.PC); jogos[3] = new Jogo("Blasphemous", Plataforma.PC);
Uma List cresce e encolhe sozinha, sem que precisemos administrar espaço.
import java.util.List; import java.util.ArrayList; List<Jogo> jogos = new ArrayList<>(); jogos.add(new Jogo("Hollow Knight", Plataforma.PC)); jogos.add(new Jogo("Celeste", Plataforma.SWITCH)); jogos.add(new Jogo("Hades", Plataforma.PC)); jogos.add(new Jogo("Blasphemous", Plataforma.PC)); jogos.size(); jogos.get(0); jogos.remove(1); jogos.size();
Os dois laços ao lado fazem a mesma coisa. O jeito normal é o de cima, já que na prática quase nunca precisamos do índice.
for (Jogo jogo : jogos) { System.out.println(jogo.getTitulo()); } for (int i = 0; i < jogos.size(); i++) { System.out.println(jogos.get(i).getTitulo()); }
Observe a classe ao lado. O que acontece ao chamar adicionar? Isso acontece pois declarar apenas reserva o nome, e enquanto não escrevermos new ArrayList<>() a variável não aponta para lugar nenhum.
public class Biblioteca { private String nome; private List<Jogo> jogos; public void adicionar(Jogo jogo) { jogos.add(jogo); } }
public class Biblioteca { private String nome; private List<Jogo> jogos; public void adicionar(Jogo jogo) { jogos.add(jogo); } } NullPointerException
Dentro de uma classe, o hábito seguro é criar a lista já na declaração do atributo. Ou seja, ela nasce pronta e não há como esquecer.
public class Biblioteca { private String nome; private List<Jogo> jogos = new ArrayList<>(); public Biblioteca(String nome) { this.nome = nome; } public void adicionar(Jogo jogo) { jogos.add(jogo); } public int quantidade() { return jogos.size(); } }
Uma festa está com a lista de convidados no sistema. Observe o código ao lado e responda:
List<String> convidados = new ArrayList<>(); convidados.add("Ana"); convidados.add("Bruno"); convidados.add("Ana"); convidados.remove("Ana");
Enquanto a List guarda uma sequência acessada por posição, o Map é o que usamos quando queremos buscar por uma chave.
É a agenda telefônica: buscamos pelo nome para achar o telefone.
import java.util.Map; import java.util.HashMap; Map<Plataforma, Integer> contagem = new HashMap<>(); contagem.put(Plataforma.PC, 12); contagem.put(Plataforma.SWITCH, 5); contagem.put(Plataforma.PC, 20); contagem.get(Plataforma.PC); contagem.get(Plataforma.XBOX); contagem.containsKey(Plataforma.XBOX); for (Plataforma p : contagem.keySet()) { System.out.println(p + ": " + contagem.get(p)); }
Agora a lista foi declarada sem o tipo. Observe o código ao lado.
List jogos = new ArrayList(); jogos.add(new Jogo("Hades", Plataforma.PC)); jogos.add("isso não é um jogo"); jogos.add(42); Jogo jogo = (Jogo) jogos.get(1);
List jogos = new ArrayList(); jogos.add(new Jogo("Hades", Plataforma.PC)); jogos.add("isso não é um jogo"); jogos.add(42); Jogo jogo = (Jogo) jogos.get(1); // ClassCastException
O trecho entre os sinais de menor e maior serve para dizer ao compilador o que a coleção guarda.
Ou seja, é o mesmo movimento que fizemos com o enum.
List<Jogo> jogos = new ArrayList<>(); jogos.add(new Jogo("Hades", Plataforma.PC)); jogos.add("isso não é um jogo"); jogos.add(42); Jogo jogo = jogos.get(0);
Não são exclusividade das coleções, já que qualquer tipo pode ser parametrizado. Não precisamos escrever os nossos agora, e sim reconhecê-los quando aparecerem:
List<Jogo> // uma lista de jogos Map<Plataforma, Double> // um mapa de plataforma para número Optional<Jogo> // um jogo que pode não existir
Um petshop está controlando o estoque de produtos. Observe o código ao lado e responda:
Desafio: como você somaria tudo que existe no estoque?
Map<String, Integer> estoque = new HashMap<>(); estoque.put("racao", 12); estoque.put("coleira", 5); estoque.put("racao", 20); estoque.get("racao"); estoque.get("shampoo"); estoque.size();
Os três recursos resolvem a mesma coisa por caminhos diferentes:
Podemos afirmar que os três antecipam o erro, tirando-o da execução e trazendo-o para a compilação.

Quer trocar mais uma ideia? Me chama!