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

JAVA
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

JAVA
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)
    }
}
▶ Experimente

Métodos Estáticos de Interface

Java 8 introduziu métodos estáticos que são chamados diretamente através do nome da interface.

JAVA
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

JAVA
@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

JAVA
(parâmetros) -> { corpo }

Exemplo: Lambda

JAVA
// 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
▶ Experimente

Várias Formas de Lambda

JAVA
// 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

JAVA
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());
    }
}
▶ Experimente

Lambda com Coleções

Lambda torna as operações com coleções mais concisas.

Exemplo: Iteração

JAVA
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);
    }
}
▶ Experimente

Exemplo: Ordenação

JAVA
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]
    }
}
▶ Experimente

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

JAVA
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();
    }
}
▶ Experimente

Exemplo Abrangente

JAVA
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

📝 Exercícios

  1. Prática com Lambda: Use Lambda para converter strings para maiúsculas, obter comprimento, verificar se está vazio
  2. Operações com coleções: Use Lambda para filtrar, transformar e ordenar coleções
  3. 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.

100%