Interfaces Avançadas e Lambda
Esta lição cobre recursos avançados de interfaces e expressões Lambda para tornar seu código mais conciso.
Métodos Padrão de Interface
Java 8 introduziu métodos padrão, permitindo que interfaces tenham corpos de métodos.
Sintaxe
public interface MinhaInterface {
// Método abstrato
void metodoAbstrato();
// Método padrão
default void metodoPadrao() {
System.out.println("Implementação padrão");
}
}
Exemplo: Métodos Padrão
public interface Vehicle {
void start();
default void horn() {
System.out.println("Bip bip!");
}
default void stop() {
System.out.println("Veículo parado");
}
}
public class Car implements Vehicle {
@Override
public void start() {
System.out.println("Carro iniciado");
}
// Pode opcionalmente sobrescrever métodos padrão
@Override
public void horn() {
System.out.println("Fon fon!");
}
}
public class DefaultMethodDemo {
public static void main(String[] args) {
Car car = new Car();
car.start(); // Carro iniciado
car.horn(); // Fon fon! (método sobrescrito)
car.stop(); // Veículo parado (padrão da interface)
}
}
Métodos Estáticos de Interface
Java 8 introduziu métodos estáticos que são chamados diretamente através do nome da interface.
public interface MathUtils {
static int add(int a, int b) {
return a + b;
}
static int multiply(int a, int b) {
return a * b;
}
}
public class StaticMethodDemo {
public static void main(String[] args) {
System.out.println(MathUtils.add(3, 5)); // 8
System.out.println(MathUtils.multiply(3, 5)); // 15
}
}
Interfaces Funcionais
Uma interface com apenas um método abstrato pode ser implementada com uma expressão Lambda.
Anotação @FunctionalInterface
@FunctionalInterface
public interface Calculator {
int calculate(int a, int b);
}
Interfaces Funcionais Comuns
| Interface | Método | Descrição |
|---|---|---|
Predicate<T> |
boolean test(T t) |
Testa uma condição |
Function<T,R> |
R apply(T t) |
Transforma entrada em saída |
Consumer<T> |
void accept(T t) |
Consome entrada |
Supplier<T> |
T get() |
Fornece saída |
Expressões Lambda
Lambda é uma abreviação para funções anônimas, introduzida no Java 8.
Sintaxe
(parâmetros) -> { corpo }
Exemplo: Lambda
// Abordagem tradicional
Calculator add = new Calculator() {
@Override
public int calculate(int a, int b) {
return a + b;
}
};
// Expressão Lambda
Calculator add = (a, b) -> a + b;
// Chamar
int result = add.calculate(3, 5); // 8
Várias Formas de Lambda
// Sem parâmetros
Runnable r = () -> System.out.println("Hello");
// Um parâmetro
Consumer<String> print = s -> System.out.println(s);
// Múltiplos parâmetros
Comparator<String> comp = (a, b) -> a.length() - b.length();
// Corpo de múltiplas linhas
Function<String, String> upper = s -> {
String result = s.trim();
return result.toUpperCase();
};
Exemplo: Usando Lambda
import java.util.Arrays;
import java.util.List;
import java.util.function.*;
public class LambdaDemo {
public static void main(String[] args) {
// Predicate: testar condição
Predicate<Integer> isEven = n -> n % 2 == 0;
System.out.println(isEven.test(4)); // true
System.out.println(isEven.test(7)); // false
// Function: transformar
Function<String, Integer> length = s -> s.length();
System.out.println(length.apply("Hello")); // 5
// Consumer: consumir
Consumer<String> print = s -> System.out.println("Saída: " + s);
print.accept("Java"); // Saída: Java
// Supplier: fornecer
Supplier<Double> random = () -> Math.random();
System.out.println(random.get());
}
}
Lambda com Coleções
Lambda torna as operações com coleções mais concisas.
Exemplo: Iteração
import java.util.Arrays;
import java.util.List;
public class LambdaCollection {
public static void main(String[] args) {
List<String> names = Arrays.asList("Alice", "Bob", "Charlie");
// Abordagem tradicional
for (String name : names) {
System.out.println(name);
}
// Abordagem Lambda
names.forEach(name -> System.out.println(name));
// Referência de método (mais conciso)
names.forEach(System.out::println);
}
}
Exemplo: Ordenação
import java.util.Arrays;
import java.util.Collections;
import java.util.Comparator;
import java.util.List;
public class LambdaSort {
public static void main(String[] args) {
List<String> names = Arrays.asList("Charlie", "Alice", "Bob");
// Abordagem tradicional
Collections.sort(names, new Comparator<String>() {
@Override
public int compare(String a, String b) {
return a.compareTo(b);
}
});
// Abordagem Lambda
names.sort((a, b) -> a.compareTo(b));
// Referência de método
names.sort(String::compareTo);
System.out.println(names); // [Alice, Bob, Charlie]
}
}
Referências de Métodos
Referências de métodos são uma abreviação para expressões Lambda.
Quatro Tipos de Referências de Métodos
| Tipo | Sintaxe | Exemplo |
|---|---|---|
| Método estático | NomeClasse::metodoEstático |
Math::abs |
| Método de instância | objeto::metodoInstância |
System.out::println |
| Classe específica | NomeClasse::metodoInstância |
String::length |
| Construtor | NomeClasse::new |
ArrayList::new |
Exemplo: Referências de Métodos
import java.util.Arrays;
import java.util.List;
import java.util.function.*;
public class MethodRefDemo {
public static void main(String[] args) {
// Referência de método estático
Function<Integer, Integer> abs = Math::abs;
System.out.println(abs.apply(-5)); // 5
// Referência de método de instância
Consumer<String> print = System.out::println;
print.accept("Hello"); // Hello
// Referência de método de instância de classe específica
Function<String, Integer> length = String::length;
System.out.println(length.apply("Hello")); // 5
// Referência de construtor
Supplier<List<String>> listFactory = ArrayList::new;
List<String> list = listFactory.get();
}
}
Exemplo Abrangente
import java.util.Arrays;
import java.util.List;
import java.util.stream.Collectors;
public class LambdaPractice {
public static void main(String[] args) {
List<Integer> numbers = Arrays.asList(1, 2, 3, 4, 5, 6, 7, 8, 9, 10);
// Filtrar números pares
List<Integer> evens = numbers.stream()
.filter(n -> n % 2 == 0)
.collect(Collectors.toList());
System.out.println("Pares: " + evens); // [2, 4, 6, 8, 10]
// Quadrado
List<Integer> squares = numbers.stream()
.map(n -> n * n)
.collect(Collectors.toList());
System.out.println("Quadrados: " + squares); // [1, 4, 9, 16, 25, 36, 49, 64, 81, 100]
// Soma
int sum = numbers.stream()
.reduce(0, (a, b) -> a + b);
System.out.println("Soma: " + sum); // 55
// Máximo
int max = numbers.stream()
.reduce(Integer::max)
.orElse(0);
System.out.println("Máximo: " + max); // 10
}
}
❓ Perguntas Frequentes
P: Qual é a diferença entre Lambda e classes anônimas internas? R: Lambda é mais conciso, mas só funciona com interfaces funcionais. Classes anônimas internas podem funcionar com qualquer interface ou classe.
P: Quando devo usar Lambda? R: Ao implementar interfaces funcionais, especialmente para operações com coleções e tratamento de eventos.
P: Como escolher entre referências de métodos e Lambda? R: Se Lambda apenas chama um método existente, use referência de método para concisão.
📖 Resumo
- Métodos padrão de interface: introduzidos no Java 8, interfaces podem ter corpos de métodos
- Métodos estáticos de interface: chamados diretamente através do nome da interface
- Interfaces funcionais: interfaces com apenas um método abstrato
- Expressões Lambda: abreviação para funções anônimas
- Referências de métodos: abreviação para Lambda
📝 Exercícios
- Prática com Lambda: Use Lambda para converter strings para maiúsculas, obter comprimento, verificar se está vazio
- Operações com coleções: Use Lambda para filtrar, transformar e ordenar coleções
- Interface funcional personalizada: Defina uma interface de operação matemática, implemente adição/subtração/multiplicação/divisão com Lambda
Próxima Lição
Na próxima lição, aprenderemos sobre Enums e Classes Internas — classes especiais em Java.



