Sumário
- Introdução ao Java 8
- Expressões Lambda
- Referências de Método
- API Stream
- Classe Optional
- Métodos Padrão em Interfaces
- Interfaces Fnucionais
- Motor JavaScript Nashorn
- API Aprimorada de Data e Hora
- Referências
Introdução ao Java 8
O Java 8 introduziu várias funcionalidades importantes que modernizaram a linguagem e facilitaram o desenvolvimento de código mais conciso e expressivo. As principais inovações incluem:
- Expressões Lambda: Permitem tratar funcionalidades como argumentos de método
- Referências de Método: Facilitam a referência a métodos ou construtores existentes
- API Stream: Habilita processamento funcional de coleções
- Classe Optional: Fornece uma alternativa segura para valores nulos
- Métodos Padrão: Permitem implementações em interfaces
- Interfaces Funcionais: Interfaces com um único método abstrato
- API de Data e Hora: Novas classes para manipulação de datas e horas
- Nashorn: Motor JavaScript para execução no ambiente Java
Expressões Lambda
As expressões Lambda são uma das características mais poderosas do Java 8, permitindo escrever código funcional mais conciso. A sintaxe básica é:
(parâmetros) -> {expressão}
Quando o tipo do parâmetro pode ser inferido, ele pode ser omitido. Se houver apenas um parâmetro, os parênteses também podem ser omitidos.
Exemplos Práticos
import java.util.Arrays;
import java.util.List;
import java.util.stream.Collectors;
class DemonstracaoLambda {
public static void main(String[] args) throws InterruptedException {
// Criando thread com sintaxe tradicional
new Thread(new Runnable() {
@Override
public void run() {
System.out.println("Execução tradicional");
}
}).start();
// Criando thread com Lambda
new Thread(() -> System.out.println("Execução com Lambda")).start();
List<string> frutas = Arrays.asList("laranja", "maçã", "banana");
// Ordenação com Lambda
frutas.sort((a, b) -> a.compareToIgnoreCase(b));
System.out.println("Lista ordenada: " + frutas);
// Filtragem com Lambda
List<string> filtradas = frutas.stream()
.filter(f -> f.startsWith("m"))
.collect(Collectors.toList());
System.out.println("Frutas filtradas: " + filtradas);
// Mapeamento com Lambda
List<integer> tamanhos = frutas.stream()
.map(String::length)
.collect(Collectors.toList());
System.out.println("Tamanhos das frutas: " + tamanhos);
}
}</integer></string></string>
Saída do Exemplo
Execução tradicional
Execução com Lambda
Lista ordenada: [maçã, banana, laranja]
Frutas filtradas: [maçã]
Tamanhos das frutas: [4, 6, 7]
Referências de Método
As referências de método permitem referenciar métodos ou construtores diretamente usando a notação ::. Existem quatro tipos de referências:
- Referência a método estático:
Classe::metodoEstatico - Referência a método de instância:
objeto::metodoInstancia - Referência a método de instância de objeto arbitrário:
Classe::metodoInstancia - Referência a construtor:
Classe::new
Exemplos Práticos
import java.util.Arrays;
import java.util.List;
import java.util.function.Function;
import java.util.function.Supplier;
class DemonstracaoReferencia {
static class Calculadora {
public static int quadrado(int n) {
return n * n;
}
public int dobro(int n) {
return n * 2;
}
}
public static void main(String[] args) {
// Referência a método estático
Function<integer integer=""> quadradoFuncao = Calculadora::quadrado;
System.out.println("Quadrado de 5: " + quadradoFuncao.apply(5));
// Referência a método de instância
Calculadora calc = new Calculadora();
Function<integer integer=""> dobroFuncao = calc::dobro;
System.out.println("Dobro de 7: " + dobroFuncao.apply(7));
// Referência a construtor
Supplier<list>> listaSupplier = ArrayList::new;
List<string> novaLista = listaSupplier.get();
System.out.println("Nova lista criada: " + novaLista.getClass().getSimpleName());
// Referência a método de instância de objeto arbitrário
List<string> palavras = Arrays.asList("java", "python", "javascript");
palavras.sort(String::compareToIgnoreCase);
System.out.println("Lista ordenada: " + palavras);
}
}</string></string></list></integer></integer>
Saída do Exemplo
Quadrado de 5: 25
Dobro de 7: 14
Nova lista criada: ArrayList
Lista ordenada: [java, javascript, python]
API Stream
A API Stream, introduzida no Java 8, permite processar coleções de forma declarativa e funcional. As operações Stream podem ser divididas em:
- Operações intermediárias: filter, map, sorted, limit, etc.
- Operações terminais: forEach, collect, reduce, etc.
Exemplos Práticos
import java.util.Arrays;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;
class DemonstracaoStream {
public static void main(String[] args) {
List<integer> numeros = Arrays.asList(5, 2, 8, 1, 9, 3, 7);
// Filtragem
List<integer> pares = numeros.stream()
.filter(n -> n % 2 == 0)
.collect(Collectors.toList());
System.out.println("Números pares: " + pares);
// Mapeamento
List<string> quadrados = numeros.stream()
.map(n -> "Quadrado de " + n + " é " + (n * n))
.collect(Collectors.toList());
System.out.println("Quadrados: " + quadrados);
// Ordenação
List<integer> ordenados = numeros.stream()
.sorted()
.collect(Collectors.toList());
System.out.println("Números ordenados: " + ordenados);
// Redução
int soma = numeros.stream()
.reduce(0, Integer::sum);
System.out.println("Soma total: " + soma);
// Agrupamento
Map<boolean list="">> agrupados = numeros.stream()
.collect(Collectors.partitioningBy(n -> n % 2 == 0));
System.out.println("Agrupados por paridade: " + agrupados);
// Estatísticas
double media = numeros.stream()
.mapToInt(Integer::intValue)
.average()
.orElse(0.0);
System.out.println("Média: " + media);
}
}</boolean></integer></string></integer></integer>
Saída do Exemplo
Números pares: [2, 8]
Quadrados: [Quadrado de 5 é 25, Quadrado de 2 é 4, Quadrado de 8 é 64, Quadrado de 1 é 1, Quadrado de 9 é 81, Quadrado de 3 é 9, Quadrado de 7 é 49]
Números ordenados: [1, 2, 3, 5, 7, 8, 9]
Soma total: 35
Agrupados por paridade: {false=[5, 1, 9, 3, 7], true=[2, 8]}
Média: 5.0
Classe Optional
A classe Optional é um contêiner que pode conter ou não um valor não nulo. Foi projetada para ajudar a evitar exceções NullPointerException e encorajar código mais explícito para lidar com casos ausentes.
Exemplos Práticos
import java.util.Optional;
class DemonstracaoOptional {
public static void main(String[] args) {
// Criando Optional
Optional<string> vazio = Optional.empty();
Optional<string> presente = Optional.of("Valor presente");
Optional<string> nulo = Optional.ofNullable(null);
// Verificando presença
System.out.println("Vazio está presente? " + vazio.isPresent());
System.out.println("Presente está presente? " + presente.isPresent());
// Obtendo valores ou padrões
System.out.println("Valor padrão: " + vazio.orElse("Padrão"));
System.out.println("Valor padrão supplier: " + nulo.orElseGet(() -> "Valor gerado"));
// Executando ações condicionais
presente.ifPresent(valor -> System.out.println("Ação com valor: " + valor));
// Transformação
Optional<string> maiusculas = presente.map(String::toUpperCase);
System.out.println("Valor em maiúsculas: " + maiusculas.orElse("Nenhum valor"));
// Encadeamento de Optional
Optional<string> resultado = presente
.map(String::toUpperCase)
.filter(valor -> valor.length() > 5);
System.out.println("Resultado filtrado: " + resultado.orElse("Não atende ao critério"));
}
}</string></string></string></string></string>
Saída do Exemplo
Vazio está presente? false
Presente está presente? true
Valor padrão: Padrão
Valor padrão supplier: Valor gerado
Ação com valor: Valor presente
Valor em maiúsculas: VALOR PRESENTE
Resultado filtrado: VALOR PRESENTE
Métodos Padrão em Interfaces
O Java 8 permite que interfaces tenham métodos com implementação, conhecidos como métodos padrão. Isso é útil para adicionar funcionalidades a interfaces existentes sem quebrar implementações existentes.
Exemplos Práticos
interface InterfacePadrao {
// Método abstrato
void metodoAbstrato();
// Método padrão
default void metodoPadrao() {
System.out.println("Implementação padrão do método");
}
// Método estático
static void metodoEstatico() {
System.out.println("Método estático da interface");
}
}
class Implementacao implements InterfacePadrao {
@Override
public void metodoAbstrato() {
System.out.println("Implementação do método abstrato");
}
// Sobrescrevendo método padrão
@Override
public void metodoPadrao() {
System.out.println("Implementação customizada do método padrão");
InterfacePadrao.super.metodoPadrao(); // Chamando implementação padrão
}
}
class DemonstracaoMetodoPadrao {
public static void main(String[] args) {
InterfacePadrao obj = new Implementacao();
obj.metodoAbstrato();
obj.metodoPadrao();
InterfacePadrao.metodoEstatico();
}
}
Saída do Exemplo
Implementação do método abstrato
Implementação customizada do método padrão
Implementação padrão do método
Método estático da interface
Interfaces Funcionais
Interfaces funcionais são interfaces que possuem exatamente um método abstrato. Elas podem ser implementadas por expressões Lambda. O pacote java.util.function contém várias interfaces funcionais úteis.
Principais Interfaces Funcionais
- Function: Recebe T e retorna R
- Consumer: Recebe T e não retorna nada
- Supplier: Não recebe nada e retorna T
- Predicate: Recebe T e retorna boolean
- UnaryOperator: Recebe T e retorna T
Exemplos Práticos
import java.util.List;
import java.util.function.Function;
import java.util.function.Predicate;
import java.util.stream.Collectors;
class DemonstracaoInterfacesFuncionais {
public static void main(String[] args) {
List<integer> numeros = List.of(1, 2, 3, 4, 5, 6, 7, 8, 9, 10);
// Usando Predicate para filtrar
Predicate<integer> ehPar = n -> n % 2 == 0;
List<integer> pares = numeros.stream()
.filter(ehPar)
.collect(Collectors.toList());
System.out.println("Números pares: " + pares);
// Usando Function para transformar
Function<integer integer=""> quadrado = n -> n * n;
List<integer> quadrados = numeros.stream()
.map(quadrado)
.collect(Collectors.toList());
System.out.println("Quadrados: " + quadrados);
// Usando Consumer para imprimir
numeros.forEach(System.out::println);
// Interface funcional personalizada
OperacaoMatematica soma = (a, b) -> a + b;
OperacaoMatematica multiplicacao = (a, b) -> a * b;
System.out.println("5 + 3 = " + soma.executar(5, 3));
System.out.println("5 * 3 = " + multiplicacao.executar(5, 3));
}
@FunctionalInterface
interface OperacaoMatematica {
int executar(int a, int b);
}
}</integer></integer></integer></integer></integer>
Saída do Exemplo
Números pares: [2, 4, 6, 8, 10]
Quadrados: [1, 4, 9, 16, 25, 36, 49, 64, 81, 100]
1
2
3
4
5
6
7
8
9
10
5 + 3 = 8
5 * 3 = 15
Motor JavaScript Nashorn
O Nashorn é um motor JavaScript que executa no ambiente Java, permitindo a execução de código JavaScript diretamente no Java.
Exemplos Práticos
import javax.script.*;
import java.io.FileReader;
class DemonstracaoNashorn {
public static void main(String[] args) {
ScriptEngineManager manager = new ScriptEngineManager();
ScriptEngine engine = manager.getEngineByName("nashorn");
try {
// Executando código JavaScript simples
engine.eval("print('Olá do JavaScript!');");
// Passando variáveis do Java para JavaScript
engine.eval("var msg = 'Mensagem do Java';");
engine.eval("print(msg);");
// Chamando função JavaScript do Java
engine.eval("function soma(a, b) { return a + b; }");
Invocable invocable = (Invocable) engine;
Object resultado = invocable.invokeFunction("soma", 10, 5);
System.out.println("Resultado da soma: " + resultado);
// Executando arquivo JavaScript
// engine.eval(new FileReader("script.js"));
} catch (ScriptException e) {
System.out.println("Erro na execução do JavaScript: " + e.getMessage());
} catch (NoSuchMethodException e) {
System.out.println("Método não encontrado: " + e.getMessage());
}
}
}
Saída do Exemplo
Olá do JavaScript!
Mensagem do Java
Resultado da soma: 15
API Aprimorada de Data e Hora
O Java 8 introduziu um novo pacote java.time para substituir as antigas classes java.util.Date e java.util.Calendar. As novas classes são imutáveis e thread-safe.
Classes Principais
- LocalDate: Representa uma data (ano-mês-dia)
- LocalTime: Representa um tempo (hora-minuto-segundo)
- LocalDateTime: Combina data e tempo
- ZonedDateTime: Data, tempo e fuso horário
- Duration: Representa uma quantidade de tempo
Exemplos Práticos
import java.time.*;
import java.time.format.DateTimeFormatter;
import java.time.temporal.ChronoUnit;
class DemonstracaoDataHora {
public static void main(String[] args) {
// Data e hora atual
LocalDate hoje = LocalDate.now();
LocalTime agora = LocalTime.now();
LocalDateTime agoraCompleto = LocalDateTime.now();
System.out.println("Data atual: " + hoje);
System.out.println("Hora atual: " + agora);
System.out.println("Data e hora atuais: " + agoraCompleto);
// Criando datas específicas
LocalDate dataEspecifica = LocalDate.of(2024, 12, 25);
LocalTime horaEspecifica = LocalTime.of(15, 30);
LocalDateTime dataHoraEspecifica = LocalDateTime.of(2024, 12, 25, 15, 30);
System.out.println("Data específica: " + dataEspecifica);
System.out.println("Hora específica: " + horaEspecifica);
System.out.println("Data e hora específicas: " + dataHoraEspecifica);
// Manipulando datas
LocalDate amanha = hoje.plusDays(1);
LocalDate semanaPassada = hoje.minusWeeks(1);
System.out.println("Amanhã: " + amanha);
System.out.println("Semana passada: " + semanaPassada);
// Formatando datas
DateTimeFormatter formatter = DateTimeFormatter.ofPattern("dd/MM/yyyy HH:mm");
String formatado = agoraCompleto.format(formatter);
System.out.println("Data formatada: " + formatado);
// Diferença entre datas
long diasEntre = ChronoUnit.DAYS.between(semanaPassada, hoje);
System.out.println("Dias entre as datas: " + diasEntre);
// Fuso horário
ZoneId fusoSaoPaulo = ZoneId.of("America/Sao_Paulo");
ZonedDateTime agoraSaoPaulo = ZonedDateTime.now(fusoSaoPaulo);
System.out.println("Horário em São Paulo: " + agoraSaoPaulo);
// Conversão entre fusos
ZoneId fusoNovaYork = ZoneId.of("America/New_York");
ZonedDateTime agoraNovaYork = agoraSaoPaulo.withZoneSameInstant(fusoNovaYork);
System.out.println("Horário em Nova York: " + agoraNovaYork);
}
}
Saída do Exemplo
Data atual: 2024-10-28
Hora atual: 17:26:47.282
Data e hora atuais: 2024-10-28T17:26:47.282
Data específica: 2024-12-25
Hora específica: 15:30
Data e hora específicas: 2024-12-25T15:30
Amanhã: 2024-10-29
Semana passada: 2024-10-21
Data formatada: 28/10/2024 17:26
Dias entre as datas: 7
Horário em São Paulo: 2024-10-28T17:26:47.282-03:00[America/Sao_Paulo]
Horário em Nova York: 2024-10-28T15:26:47.282-04:00[America/New_York]