java Archives - tsayao.com.br
Lombok: Eliminando código "clichê" no Java
Em um código fonte Java típico são necessárias várias classes para troca de informações, seja para o mapeamento objeto/relacional, para retorno de um serviço REST/web-service ou até mesmo para troca de informações em uma API interna.
Geralmente são utilizadas classes "POJO" ou "Poor Old Java Object", traduzido como "Simples e velho objeto java". Se parece como a classe abaixo:
class Montanha {
O problema é que para uma classe ter o encapsulamento correto, é necessário criar o código para acessar as propriedade, os getters/setters. A classe fica assim:
public String getPais() {
public void setPais(String pais) {
Montanha montanha = (Montanha) o;
return Double.compare(montanha.latitude, latitude) == 0 &&
Double.compare(montanha.longitude, longitude) == 0 &&
Objects.equals(nome, montanha.nome) &&
Objects.equals(pais, montanha.pais);
return Objects.hash(nome, latitude, longitude, pais);
return "Montanha{" +
"nome='" + nome + '\'' +
", latitude=" + latitude +
", pais='" + pais + '\'' +
Um tanto grande para uma simples classe, não é mesmo?
Muitas IDEs possuem a funcionalidade para inserir este código automaticamente. Este foi gerado no Intellij Idea, e tudo que precisei fazer foi pressionar ALT+INS, escolher a opção para gerar os getters/setters e aceitar. Depois fiz a mesma coisa para gerar o equals/hashCode e o toString.
Apresentando o Projeto Lombok
Tudo pode ficar mais simples e elegante com o Project Lombok.
O @Data
Montanha montanha = new Montanha();
montanha.setNome("Monte Everest");
montanha.setLatitude(27.986065);
montanha.setLongitude(86.922623);
montanha.setPais("Nepal");
Montanha montanha2 = new Montanha();
montanha2.setNome("Monte Everest");
montanha2.setLatitude(27.986065);
montanha2.setLongitude(86.922623);
montanha2.setPais("Nepal");
System.out.println(montanha);
System.out.println(montanha.equals(montanha2));
Montanha(nome=Monte Everest, latitude=27.986065, longitude=86.922623, pais=Nepal)
Para que isto funcione:
Adicione o lombok ao seu projeto (exemplo utilizando o maven):
Instale o plugin no seu editor favorito (para que possa completar automaticamente o código).
O lombok irá atuar em tempo de compilação e gerar o código automaticamente.
OBS: Apenas o lombok 1.6.21 (ainda em desenvolvimento na data deste texto) suporta o Java 9+.
O @Value
O @Value cria uma classe imutável, ou seja, todos os parâmetros são necessários no construtor e a classe não possui os setters.
Montanha montanha = new Montanha("Monte Everest", 27.986065,86.922623, "Nepal");
Veja na página do lombok todos os outros recursos que ele oferece!
Publicado em maio 20, 2018 maio 20, 2018 Categorias JavaTags java, lombok
API Stream do Java 8
Similar ao Linq do .net / C#, o Java possui a stream API, que a grosso modo, permite que coleções de dados sejam manipuladas utilizando uma API fluente com expressões lambdas.
Como funcionam os streams?
As operações "stream" são intermediárias ou finais, onde as intermediárias retornam o tipo genérico Stream<> e as finais retornam void ou algum tipo diferente de Stream<>, como no caso abaixo o forEach.
List<String> atores = Arrays.asList("Brad Pitt",
"Edward Norton",
"Christian Bale",
"Chris Pratt",
"Buttercup Cumbersnatch");
atores.stream()
.filter(a -> a.startsWith("B"))
No exemplo acima acontece o seguinte:
o .stream() transforma a lista de atores no tipo Stream<String>, permitindo o uso da API;
.filter() irá realizar um filtro na coleção de dados, onde neste caso estamos buscando atores que começam com "B";
o .map() irá mapear o resultado – é similar ao SELECT do SQL, onde podemos transformar o objeto na saída desejada;
.sorted() ordena os itens;
.forEach() sai da API stream e enumera os itens.
Geralmente o stream será utilizado a partir do .stream() de uma coleção de dados, mas pode-se criar um stream diretamente, como neste exemplo:
Stream.of("Brad Pitt",
"Buttercup Cumbersnatch")
O resultado é o mesmo do exemplo anterior.
A API stream provê algumas facilidades, como no exemplo abaixo:
String entrada = "1234";
System.out.println(entrada.chars().allMatch(Character::isDigit));
Este trecho irá verificar se a String de entrada possui apenas números. Pode ser facilmente adaptada para verificar se é letra com o Character::isLetter, ou se é minúsculo com Character::isLowercase, etc.
O método String.chars() retorna um IntStream, onde cada caractere é representado por um inteiro.
Existem outros tipos especializados de streams, como o LongStream e o DoubleStream.
Os exemplos utilizados serão baseados no seguinte trecho de código:
Pessoa(String nome, int idade) {
List<Pessoa> pessoas =
new Pessoa("João", 15),
new Pessoa("Pedro", 30),
new Pessoa("Oscar", 30),
new Pessoa("Lucas", 10));
O collect é uma função terminal que converte o stream em um resultado, podendo ser uma Lista, um Set ou um Map.
List<String> collect = pessoas.stream()
.map(Pessoa::getNome)
collect.forEach(System.out::println);
Map<Integer, List<Pessoa>> collect = pessoas.stream()
.collect(Collectors.groupingBy(Pessoa::getIdade));
Este exemplo usa um Collector para agrupar as pessoas por idade.
Double collect = pessoas.stream()
.collect(Collectors.averagingInt(Pessoa::getIdade));
Este irá retornar a média de idade das pessoas.
Existem outros métodos na classe estática Collectors que podem ser úteis.
Encontrar o primeiro item
O finalizador findFirst() irá retornar o primeiro item do stream, que pode ser combinado com um filtro (ou qualquer outra função intermediária).
Optional<Pessoa> first = pessoas.stream()
.filter(p -> p.getIdade() == 30).findFirst();
Um Optional<> é retornado, pois pode não existir o item.
Encontrar o maior/menor item
.max(Comparator.comparing(Pessoa::getIdade));
Para encontrar a pessoa de menor idade, basta substituir o max por min.
Distinct / Eliminar duplicados
List<Integer> collect = pessoas.stream()
.map(Pessoa::getIdade)
O exemplo acima retorna todas as idades da coleção, sem repeti-las.
Verificar se existe algum item
if(pessoas.stream().anyMatch(pessoa -> pessoa.getIdade() == 30))
System.out.println("Existe uma pessoa com 30 anos");
O anyMatch() irá verificar se existe ao menos um item que satisfaça a(s) condição(ções).
Filtrar por vários itens
List<String> nomesProcurados = Arrays.asList("João", "Pedro");
pessoas.stream()
.filter(p -> nomesProcurados.contains(p.getNome()))
Este exemplo irá procurar os nomes João e Pedro entre as pessoas.
O método reduce() irá reduzir a stream a apenas um item final. Veja o exemplo:
List<BigDecimal> nums = Arrays.asList(new BigDecimal("10"),
new BigDecimal("20"),
new BigDecimal("30"));
Optional<BigDecimal> reduce = nums.stream().reduce(BigDecimal::add);
System.out.println(reduce.get());
A saída será a soma dos itens (neste caso 60), pois foi passado como argumento BigDecimal::add.
O método flatMap() achata uma coleção dentro de uma coleção.
new Pessoa("João", 15, Arrays.asList("Gabriel", "Vinícius")),
new Pessoa("Pedro", 30, Arrays.asList("Joaquim", "Túlio")),
new Pessoa("Oscar", 30, Arrays.asList("Lorenzo", "Guilherme")),
new Pessoa("Lucas", 10, Arrays.asList("Marcos", "Fernando")));
.flatMap(f -> f.getPrimos().stream())
A saída deste exemplo será uma lista com o nome de todos os primos.
Complicando um pouco mais, podemos combinar um novo objeto juntando o "pai" com os "filhos":
.flatMap(f -> f.getPrimos().stream().map(m -> String.join(" -> ",f.getNome(), m)))
João -> Gabriel
João -> Vinícius
Pedro -> Joaquim
Pedro -> Túlio
Oscar -> Lorenzo
Oscar -> Guilherme
Lucas -> Marcos
Lucas -> Fernando
A função sorted() recebe um comparador para ordenar os itens.
.sorted(Comparator.comparingInt(Pessoa::getIdade))
Para ordenar decrescente:
.sorted(Comparator.comparingInt(Pessoa::getIdade).reversed())
Esta outra forma pode ser utilizado para casos mais complicados, onde o desenvolvedor deseja customizar a ordenação:
if(o1.getIdade() == o2.getIdade())
else if(o1.getIdade() > o2.getIdade())
Reutilizando Streams
O Java não permite que streams sejam reutilizados.
Stream<Pessoa> pessoaStream= pessoas.stream().filter(p -> p.getIdade() == 30);
List<Pessoa> collect1 = pessoaStream.collect(Collectors.toList());
Optional<Pessoa> oscar = pessoaStream.filter(p -> p.getNome().equals("Oscar")).findFirst();
Este código gera a seguinte exceção:
Exception in thread "main" java.lang.IllegalStateException: stream has already been operated upon or closed
at java.util.stream.AbstractPipeline.<init>(AbstractPipeline.java:203)
at java.util.stream.ReferencePipeline.<init>(ReferencePipeline.java:94)
at java.util.stream.ReferencePipeline$StatelessOp.<init>(ReferencePipeline.java:618)
at java.util.stream.ReferencePipeline$2.<init>(ReferencePipeline.java:163)
at java.util.stream.ReferencePipeline.filter(ReferencePipeline.java:162)
at com.company.Main.main(Main.java:53)
Pode ser resolvido utilizando o Supplier<>:
Supplier<Stream<Pessoa>> pessoaStreamSupplier = () -> pessoas.stream().filter(p -> p.getIdade() == 30);
List<Pessoa> collect1 = pessoaStreamSupplier.get().collect(Collectors.toList());
Optional<Pessoa> oscar = pessoaStreamSupplier.get().filter(p -> p.getNome().equals("Oscar")).findFirst();
Publicado em maio 5, 2018 maio 6, 2018 Categorias JavaTags java, java-8, stream
Java: diferença entre variavel++ e ++variavel
Qual a diferença entre ++variavel e variavel++ ?
++var1;
System.out.println(String.format("var1: %d, var2: %d", var1, var2));
var1: 2, var2: 2
System.out.println(String.format("var1: %d, var2: %d", var1++, ++var2));
Com a mudança do lugar (foi para a mesma linha da saída), o resultado agora é diferente:
A diferença é que var++ retorna o valor atual e depois incrementa enquanto ++var incrementa e depois retorna o valor. Portanto no primeiro exemplo, como o println ocorria após a operação de incremento de ambas as variáveis, o resultado ficou igual.
No "for"
Adivinhe a saída destas linhas de código:
Se você respondeu que são iguais, acertou! Ambas irão imprimir de 0 a 9. Isto porque como o incremento ocorre separadamente, não tem diferença.
No "do… while"
Adivinhe novamente. Tem diferença?
} while (++i < 10);
Neste caso tem! No primeiro "do…while" na comparaçã de while (i++ < 10) o valor primeiro é comparado e depois é incrementado, então o valor vai de 0 a 10.
No segundo "do…while" o valor é incrementado e depois comparado então só vai até 9, pois é o último valor menor que 10.
Java para Iniciantes: Crie, Compile e Execute Programas Java Rapidamente
Preço: R$ 113,88
Java a referência completa
Preço: R$ 171,05
OCA/ OCP Java se 7 programador I e II
Publicado em fevereiro 17, 2018 fevereiro 17, 2018 Categorias JavaTags java
Java: varargs / variadic functions / funções variádicas
Funções variádicas (ou função com quantidade variável de argumentos) são funções que aceitam número de parâmetros variáveis, ou seja, a quantidade de parâmetros não é fixada. A abreviação "varargs" é comumente utilizada para referir-se a estes tipos de funções.
No Java o argumento de funções variádicas é representado por "…" (três pontos). Veja o exemplo abaixo:
m.imprimir();
m.imprimir("Banana", "Maça", "Abacate");
m.imprimirIntStrings(10, "Laranja", "Tomate");
public void imprimir(String... strs) {
for(String s : strs) {
public void imprimirIntStrings(int inteiro, String... strs) {
System.out.println(String.format("%d", inteiro));
imprimir(strs);
public void imprimir(String... strs) { }
public void imprimir(String[] strs) {}
Ou seja, na realidade "String…" é um array de Strings com ZERO ou mais elementos.
A própria implementação do String.format utiliza este "artifício":
public static String format(String format, Object... args) {
return new Formatter().format(format, args).toString();
Uma função "varargs" pode ter mais de um parâmetro, mas apenas um parâmetro vararg que deverá ser sempre o último, como ocorre nesta parte do exemplo:
Na realidade é apenas uma abreviação, ou uma forma mais "limpa" de escrever. Sem os "varargs" teríamos que fazer:
m.imprimir(new String[] {"Banana", "Maça", "Abacate"});
m.imprimirIntStrings(10, new String[] {"Laranja", "Tomate"});
No entanto, observe que isto não é válido:
public void imprimir(String[] strs) {
O compilador irá gerar erro nas duas linhas:
Sendo, respectivamente:
Error:(8, 10) java: method imprimir in class com.company.Main cannot be applied to given types;
required: java.lang.String[]
Error:(9, 10) java: method imprimir in class com.company.Main cannot be applied to given types;
found: java.lang.String,java.lang.String,java.lang.String
Então apesar de "String… strs" ser equivalente a "String[] strs", existem diferenças:
"String… strs" aceita ZERO parâmetros, enquanto "String[] strs" aceitaria apenas um array vazio;
"String… strs" aceita ser chamada com vários parâmetros do tipo String, enquanto "String[] strs" aceita apenas um parâmetro que deve ser um array de String.
Publicado em fevereiro 14, 2018 fevereiro 14, 2018 Categorias JavaTags java, varargs
Java: para comparar, utilizar == ou .equals() ?
Em linhas gerais no Java o "==" compara se o objeto é a mesma referência e o .equals() faz a comparação dos valores. Mas existem algumas formas de complicar..
O .equals() é uma função da classe base Object e é implementado de forma diferente em algumas sub classes.
Veja, por exemplo, a implementação do .equals do objeto String:
A implementação primeiro compara se é a mesma referência, pois se for, certamente são iguais. Depois verifica se o parâmetro é uma instância de String, pois se não for, não é igual. Sem seguida compara o tamanho, se for diferente, não é igual. E por último compara caractere por caractere, e retorna false se algum for diferente.
Veja as variações de comparações no exemplo abaixo:
Integer integer1 = 1;
Integer integer2 = 1;
Integer integer3 = new Integer(1);
Integer integergrande1 = 123456;
Integer integergrande2 = 123456;
String str1 = "Olá";
String str2 = "Olá";
String str3 = new String("Olá");
//ambos true - tipos primitivos
System.out.println(String.format("int1 == int2: %s", int1 == int2));
System.out.println(String.format("int1 == 1: %s", int1 == 1));
//true - caching
System.out.println(String.format("integer1 == integer2: %s", integer1 == integer2));
//true - autoboxing
System.out.println(String.format("integer1 == 1: %s", integer1 == 1));
System.out.println(String.format("integer1 == integer3: %s", integer1 == integer3));
System.out.println(String.format("integergrande1 == integergrande2: %s", integergrande1 == integergrande2));
//true - pool de strings
System.out.println(String.format("str1 == str2: %s", str1 == str2));
//false - não apontam para mesma referência
System.out.println(String.format("str1 == str3: %s", str1 == str3));
//true - são iguais
System.out.println(String.format("str1.equals(str2): %s", str1.equals(str2)));
System.out.println(String.format("str1.equals(str3): %s", str1.equals(str3)));
1) Tipos primitivos (int, double, long, etc..) sempre comparam o valor, portanto a parte abaixo retorna true:
2) Neste trecho (onde integer1 e integer2 são objetos Integer) retorna true, pois o Java faz cache da faixa -127 até 128 (um tanto estranho, não é?):
3) Neste trecho (comparando objeto Integer com constante) retorna true, pois ocorre o autoboxing:
O autoboxing surgiu no Java 5.0 com a introdução dos tipos de Objeto equivalentes aos tipos primitivos, por exemplo int -> Integer justamente para facilitar o uso entre os tipos equivalentes.
Então o integer1 será transformado em int antes da comparação, retornando true, pois são iguais.
4) Retorna false, pois apesar de terem valores iguais, ambos são Integer, portanto não ocorre o autoboxing e a comparação é feita por referência:
5) Retorna false, pois não cai no intervalo de cache de -127 a 128:
6) Retorna true por causa do pool de Strings. Este método é chamado de "string interning" e consiste em apontar para a mesma referência strings iguais, como ocorre neste caso:
7) Porém, nem todas as situações fazem uso do string pool. No caso abaixo retorna false, pois str3, apesar de ser igual a str1, foi criada de forma diferente:
8) O método .equals() sempre compara o valor (a não ser que exista uma implementação que faça diferente, já que este método pode ser sobrescrito):
Na real, o .equals de Object compara a referência:
9) Também retorna true, já que o .equals() de string compara os valores:
Se tiver dúvida, utilize o .equals() para comparar valores e o == para comparar referências, a não ser para os tipos primitivos onde pode ser utilizado == para comparar valores.