📖 Sobre o Java 16
Java 16 marcou um marco importante ao finalizar Records e Pattern Matching for instanceof, duas das features mais transformadoras da linguagem. Também introduziu Vector API em incubador, melhorias em Sealed Classes e Unix Domain Socket Channels para melhor performance de IPC.
💡 O que são JEPs?
Java 16 incluiu 17 JEPs focados em finalização de features preview, performance SIMD, e melhorias de conectividade.
🏗️ Modelagem de Dados Finalizada
🔹 JEP 395: Records (Final)
❓ O que é?
Records se tornaram feature final após refinamentos nas versões preview, oferecendo data classes imutáveis e concisas.
⚠️ Por que é importante?
Elimina definitivamente boilerplate para data classes, estabelecendo um padrão moderno para transferência e armazenamento de dados.
// Records finais no Java 16 com todas as funcionalidadespublic record Customer( String name, String email, LocalDate birthDate, List<String> preferences, Address address) { // Compact constructor com validação completa public Customer { // Validações robustas if (name == null || name.isBlank()) { throw new IllegalArgumentException("Name cannot be null or blank"); } if (email == null || !isValidEmail(email)) { throw new IllegalArgumentException("Invalid email format"); } if (birthDate == null || birthDate.isAfter(LocalDate.now())) { throw new IllegalArgumentException("Invalid birth date"); }
// Normalização e cópias defensivas name = name.trim(); email = email.toLowerCase().trim(); preferences = preferences != null ? List.copyOf(preferences) : List.of(); }
// Métodos de negócio public int getAge() { return Period.between(birthDate, LocalDate.now()).getYears(); }
public boolean isAdult() { return getAge() >= 18; }
public boolean hasPreference(String preference) { return preferences.contains(preference.toLowerCase()); }
// Métodos "with" para imutabilidade public Customer withEmail(String newEmail) { return new Customer(name, newEmail, birthDate, preferences, address); }
public Customer withPreference(String preference) { var newPreferences = new ArrayList<>(preferences); newPreferences.add(preference.toLowerCase()); return new Customer(name, email, birthDate, newPreferences, address); }
public Customer withoutPreference(String preference) { var newPreferences = preferences.stream() .filter(p -> !p.equals(preference.toLowerCase())) .toList(); return new Customer(name, email, birthDate, newPreferences, address); }
// Factory methods public static Customer guest(String name, String email) { return new Customer( name, email, LocalDate.of(1900, 1, 1), // Data placeholder List.of("guest"), Address.defaultAddress() ); }
public static Customer premium(String name, String email, LocalDate birthDate) { return new Customer( name, email, birthDate, List.of("premium", "notifications", "newsletter"), null ); }
// Validação customizada private static boolean isValidEmail(String email) { return email.contains("@") && email.contains(".") && email.indexOf("@") < email.lastIndexOf("."); }}
// Record para endereçopublic record Address( String street, String city, String zipCode, String country) { public Address { if (street == null || street.isBlank()) { throw new IllegalArgumentException("Street cannot be null or blank"); } if (city == null || city.isBlank()) { throw new IllegalArgumentException("City cannot be null or blank"); }
street = street.trim(); city = city.trim(); zipCode = zipCode != null ? zipCode.trim() : ""; country = country != null ? country.trim() : "Unknown"; }
public String getFullAddress() { return String.format("%s, %s %s, %s", street, city, zipCode, country); }
public static Address defaultAddress() { return new Address("Unknown", "Unknown", "", "Unknown"); }}
// Record para configuração com builder patternpublic record ServerConfig( String host, int port, boolean ssl, Duration timeout, Map<String, String> properties, List<String> allowedOrigins) { public ServerConfig { if (host == null || host.isBlank()) { throw new IllegalArgumentException("Host cannot be null or blank"); } if (port < 1 || port > 65535) { throw new IllegalArgumentException("Port must be between 1 and 65535"); } if (timeout == null || timeout.isNegative()) { throw new IllegalArgumentException("Timeout must be positive"); }
// Cópias defensivas properties = properties != null ? Map.copyOf(properties) : Map.of(); allowedOrigins = allowedOrigins != null ? List.copyOf(allowedOrigins) : List.of(); }
// Builder inner class public static class Builder { private String host = "localhost"; private int port = 8080; private boolean ssl = false; private Duration timeout = Duration.ofSeconds(30); private Map<String, String> properties = new HashMap<>(); private List<String> allowedOrigins = new ArrayList<>();
public Builder host(String host) { this.host = host; return this; }
public Builder port(int port) { this.port = port; return this; }
public Builder ssl(boolean ssl) { this.ssl = ssl; return this; }
public Builder timeout(Duration timeout) { this.timeout = timeout; return this; }
public Builder property(String key, String value) { this.properties.put(key, value); return this; }
public Builder allowOrigin(String origin) { this.allowedOrigins.add(origin); return this; }
public ServerConfig build() { return new ServerConfig(host, port, ssl, timeout, properties, allowedOrigins); } }
public static Builder builder() { return new Builder(); }
public String getBaseUrl() { String protocol = ssl ? "https" : "http"; return String.format("%s://%s:%d", protocol, host, port); }}
// Uso prático dos Records finalizadospublic class RecordsInAction {
public static void demonstrateRecords() { // Customer com validação automática var customer = Customer.premium( "João Silva", "joao@example.com", LocalDate.of(1990, 5, 15) );
System.out.println("Customer: " + customer.name()); System.out.println("Age: " + customer.getAge()); System.out.println("Is adult: " + customer.isAdult()); System.out.println("Has premium: " + customer.hasPreference("premium"));
// Immutable updates var updatedCustomer = customer .withPreference("mobile-app") .withEmail("joao.silva@newdomain.com");
System.out.println("Updated customer: " + updatedCustomer);
// Server config com builder var config = ServerConfig.builder() .host("api.example.com") .port(443) .ssl(true) .timeout(Duration.ofSeconds(60)) .property("max-connections", "1000") .property("keep-alive", "true") .allowOrigin("https://app.example.com") .allowOrigin("https://mobile.example.com") .build();
System.out.println("Server URL: " + config.getBaseUrl()); System.out.println("Timeout: " + config.timeout().toSeconds() + "s"); }}
📚 **Saiba mais**: [Records Final JEP](https://openjdk.org/jeps/395)
---
## 🔍 Pattern Matching Finalizado
### 🔹 JEP 394: Pattern Matching for instanceof (Final)
#### ❓ O que é?Pattern matching para `instanceof` se tornou **feature final**, simplificando verificações de tipo.
#### ⚠️ Por que é importante?Elimina casting manual redundante, torna código mais legível e menos propenso a erros.
```java// Pattern matching final no Java 16public class PatternMatchingExamples {
// Processamento de diferentes tipos de dados public static String processData(Object data) { if (data instanceof String str && !str.isBlank()) { return "Text: " + str.toUpperCase(); } else if (data instanceof Integer num && num > 0) { return "Positive number: " + num; } else if (data instanceof Double dbl && !dbl.isNaN() && !dbl.isInfinite()) { return "Valid double: " + String.format("%.2f", dbl); } else if (data instanceof List<?> list && !list.isEmpty()) { return "List with " + list.size() + " elements: " + list; } else if (data instanceof Map<?, ?> map && !map.isEmpty()) { return "Map with " + map.size() + " entries"; } else if (data instanceof LocalDate date) { return "Date: " + date.format(DateTimeFormatter.ISO_LOCAL_DATE); } return "Unknown or invalid data: " + data; }
// Processamento de hierarquia de shapes com records public static double calculateArea(Object shape) { if (shape instanceof Circle(var radius)) { return Math.PI * radius * radius; } else if (shape instanceof Rectangle(var width, var height)) { return width * height; } else if (shape instanceof Triangle(var a, var b, var c)) { // Fórmula de Heron double s = (a + b + c) / 2; return Math.sqrt(s * (s - a) * (s - b) * (s - c)); } throw new IllegalArgumentException("Unknown shape: " + shape); }
// Processamento de AST com pattern matching public static Object evaluateExpression(Object expr, Map<String, Object> vars) { return switch (expr) { case NumberLiteral(var value) -> value; case StringLiteral(var value) -> value; case Variable(var name) -> vars.get(name); case BinaryOperation(var left, var op, var right) -> { var leftVal = evaluateExpression(left, vars); var rightVal = evaluateExpression(right, vars); yield evaluateBinaryOp(leftVal, op, rightVal); } case UnaryOperation(var op, var operand) -> { var operandVal = evaluateExpression(operand, vars); yield evaluateUnaryOp(op, operandVal); } default -> throw new IllegalArgumentException("Unknown expression: " + expr); }; }
private static Object evaluateBinaryOp(Object left, String op, Object right) { if (left instanceof Number l && right instanceof Number r) { return switch (op) { case "+" -> l.doubleValue() + r.doubleValue(); case "-" -> l.doubleValue() - r.doubleValue(); case "*" -> l.doubleValue() * r.doubleValue(); case "/" -> l.doubleValue() / r.doubleValue(); case "%" -> l.doubleValue() % r.doubleValue(); case "^" -> Math.pow(l.doubleValue(), r.doubleValue()); default -> throw new UnsupportedOperationException("Unknown operator: " + op); }; } else if (left instanceof String || right instanceof String) { return left.toString() + right.toString(); } throw new IllegalArgumentException("Invalid operands for " + op); }
private static Object evaluateUnaryOp(String op, Object operand) { return switch (op) { case "-" -> -(((Number) operand).doubleValue()); case "+" -> ((Number) operand).doubleValue(); case "!" -> !((Boolean) operand); case "abs" -> Math.abs(((Number) operand).doubleValue()); case "sqrt" -> Math.sqrt(((Number) operand).doubleValue()); default -> throw new UnsupportedOperationException("Unknown operator: " + op); }; }
// Validação de entrada com pattern matching public static Result<User> validateUser(Object input) { if (input instanceof Map<?, ?> map) { if (map.get("name") instanceof String name && !name.isBlank() && map.get("email") instanceof String email && email.contains("@") && map.get("age") instanceof Integer age && age >= 0) {
return Result.success(new User(name.trim(), email.toLowerCase(), age)); } } return Result.error("Invalid user data"); }}
// Records para shapesrecord Circle(double radius) {}record Rectangle(double width, double height) {}record Triangle(double a, double b, double c) {}
// Records para ASTrecord NumberLiteral(double value) {}record StringLiteral(String value) {}record Variable(String name) {}record BinaryOperation(Object left, String operator, Object right) {}record UnaryOperation(String operator, Object operand) {}
// Record para Userrecord User(String name, String email, int age) {}
// Result typerecord Result<T>(T value, String error, boolean success) { public static <T> Result<T> success(T value) { return new Result<>(value, null, true); }
public static <T> Result<T> error(String error) { return new Result<>(null, error, false); }}
📚 **Saiba mais**: [Pattern Matching Final JEP](https://openjdk.org/jeps/394)
---
## 🏗️ Hierarquias Aprimoradas
### 🔹 JEP 397: Sealed Classes (Second Preview)
#### ❓ O que é?Segunda preview de Sealed Classes com melhorias na sintaxe e funcionalidades.
#### ⚠️ Por que é importante?Refinamentos baseados no feedback da comunidade, preparando para finalização.
```java// Sealed classes melhoradas no Java 16public sealed class Result<T, E> permits Result.Success, Result.Failure {
public static final class Success<T, E> extends Result<T, E> { private final T value;
public Success(T value) { this.value = Objects.requireNonNull(value, "Value cannot be null"); }
public T getValue() { return value; }
@Override public String toString() { return "Success{value=" + value + "}"; }
@Override public boolean equals(Object obj) { return obj instanceof Success<?, ?> other && Objects.equals(value, other.value); }
@Override public int hashCode() { return Objects.hash("Success", value); } }
public static final class Failure<T, E> extends Result<T, E> { private final E error;
public Failure(E error) { this.error = Objects.requireNonNull(error, "Error cannot be null"); }
public E getError() { return error; }
@Override public String toString() { return "Failure{error=" + error + "}"; }
@Override public boolean equals(Object obj) { return obj instanceof Failure<?, ?> other && Objects.equals(error, other.error); }
@Override public int hashCode() { return Objects.hash("Failure", error); } }
// Factory methods public static <T, E> Result<T, E> success(T value) { return new Success<>(value); }
public static <T, E> Result<T, E> failure(E error) { return new Failure<>(error); }
// Pattern matching helpers public boolean isSuccess() { return this instanceof Success<T, E>; }
public boolean isFailure() { return this instanceof Failure<T, E>; }
public <U> Result<U, E> map(Function<T, U> mapper) { return switch (this) { case Success<T, E> success -> Result.success(mapper.apply(success.getValue())); case Failure<T, E> failure -> Result.failure(failure.getError()); }; }
public <F> Result<T, F> mapError(Function<E, F> mapper) { return switch (this) { case Success<T, E> success -> Result.success(success.getValue()); case Failure<T, E> failure -> Result.failure(mapper.apply(failure.getError())); }; }
public T getOrElse(T defaultValue) { return switch (this) { case Success<T, E> success -> success.getValue(); case Failure<T, E> failure -> defaultValue; }; }
public T getOrThrow() { return switch (this) { case Success<T, E> success -> success.getValue(); case Failure<T, E> failure -> throw new RuntimeException(failure.getError().toString()); }; }}
// Sealed interface para comandospublic sealed interface Command permits CreateCommand, UpdateCommand, DeleteCommand, QueryCommand {
String getEntityType(); LocalDateTime getTimestamp();}
public record CreateCommand( String entityType, Map<String, Object> data, LocalDateTime timestamp) implements Command {}
public record UpdateCommand( String entityType, String id, Map<String, Object> changes, LocalDateTime timestamp) implements Command {}
public record DeleteCommand( String entityType, String id, LocalDateTime timestamp) implements Command {}
public record QueryCommand( String entityType, Map<String, Object> criteria, int limit, LocalDateTime timestamp) implements Command {}
// Command processor com pattern matchingpublic class CommandProcessor {
public Result<String, String> processCommand(Command command) { return switch (command) { case CreateCommand(var type, var data, var timestamp) -> { if (data.isEmpty()) { yield Result.failure("Create command requires data"); } yield Result.success("Created " + type + " with " + data.size() + " fields"); } case UpdateCommand(var type, var id, var changes, var timestamp) -> { if (id == null || id.isBlank()) { yield Result.failure("Update command requires valid ID"); } yield Result.success("Updated " + type + " " + id + " with " + changes.size() + " changes"); } case DeleteCommand(var type, var id, var timestamp) -> { if (id == null || id.isBlank()) { yield Result.failure("Delete command requires valid ID"); } yield Result.success("Deleted " + type + " " + id); } case QueryCommand(var type, var criteria, var limit, var timestamp) -> { yield Result.success("Queried " + type + " with " + criteria.size() + " criteria, limit " + limit); } }; }}🚀 Performance SIMD
🔹 JEP 338: Vector API (First Incubator)
❓ O que é?
API incubator para operações vetorizadas SIMD, permitindo computação paralela em nível de CPU.
⚠️ Por que é importante?
Habilita operações matemáticas de alta performance que aproveitam instruções SIMD modernas.
// Vector API incubator no Java 16import jdk.incubator.vector.*;
public class VectorOperationsExample {
private static final VectorSpecies<Float> SPECIES = FloatVector.SPECIES_256;
// Operações vetorizadas básicas public static void basicVectorOperations() { float[] a = {1.0f, 2.0f, 3.0f, 4.0f, 5.0f, 6.0f, 7.0f, 8.0f}; float[] b = {2.0f, 3.0f, 4.0f, 5.0f, 6.0f, 7.0f, 8.0f, 9.0f}; float[] result = new float[a.length];
// Operação vetorizada: result = a + b for (int i = 0; i < a.length; i += SPECIES.length()) { var va = FloatVector.fromArray(SPECIES, a, i); var vb = FloatVector.fromArray(SPECIES, b, i); var vr = va.add(vb); vr.intoArray(result, i); }
System.out.println("Vector addition result: " + Arrays.toString(result)); }
// Multiplicação de matrizes vetorizada public static float[] matrixVectorMultiply(float[][] matrix, float[] vector) { int rows = matrix.length; int cols = matrix[0].length; float[] result = new float[rows];
for (int row = 0; row < rows; row++) { float sum = 0.0f; int i = 0;
// Processar com vetores for (; i < SPECIES.loopBound(cols); i += SPECIES.length()) { var vm = FloatVector.fromArray(SPECIES, matrix[row], i); var vv = FloatVector.fromArray(SPECIES, vector, i); var product = vm.mul(vv); sum += product.reduceLanes(VectorOperators.ADD); }
// Processar elementos restantes for (; i < cols; i++) { sum += matrix[row][i] * vector[i]; }
result[row] = sum; }
return result; }
// Operações matemáticas complexas public static void complexMathOperations() { float[] values = new float[1024]; float[] results = new float[1024];
// Inicializar com valores for (int i = 0; i < values.length; i++) { values[i] = (float) Math.random() * 100; }
// Operação complexa vetorizada: result = sqrt(x^2 + 1) * sin(x) for (int i = 0; i < SPECIES.loopBound(values.length); i += SPECIES.length()) { var v = FloatVector.fromArray(SPECIES, values, i);
// x^2 + 1 var squared = v.mul(v); var plusOne = squared.add(1.0f);
// sqrt(x^2 + 1) var sqrt = plusOne.lanewise(VectorOperators.SQRT);
// sin(x) - aproximação usando lanewise var sin = v.lanewise(VectorOperators.SIN);
// Resultado final var result = sqrt.mul(sin); result.intoArray(results, i); }
System.out.println("Complex operation completed on " + values.length + " elements"); }
// Comparação de performance public static void performanceComparison() { int size = 10_000_000; float[] a = new float[size]; float[] b = new float[size]; float[] result1 = new float[size]; float[] result2 = new float[size];
// Inicializar arrays Random random = new Random(); for (int i = 0; i < size; i++) { a[i] = random.nextFloat(); b[i] = random.nextFloat(); }
// Operação escalar tradicional long startTime = System.nanoTime(); for (int i = 0; i < size; i++) { result1[i] = a[i] * b[i] + 1.0f; } long scalarTime = System.nanoTime() - startTime;
// Operação vetorizada startTime = System.nanoTime(); for (int i = 0; i < SPECIES.loopBound(size); i += SPECIES.length()) { var va = FloatVector.fromArray(SPECIES, a, i); var vb = FloatVector.fromArray(SPECIES, b, i); var result = va.mul(vb).add(1.0f); result.intoArray(result2, i); }
// Processar elementos restantes for (int i = SPECIES.loopBound(size); i < size; i++) { result2[i] = a[i] * b[i] + 1.0f; } long vectorTime = System.nanoTime() - startTime;
System.out.printf("Scalar time: %.2f ms%n", scalarTime / 1_000_000.0); System.out.printf("Vector time: %.2f ms%n", vectorTime / 1_000_000.0); System.out.printf("Speedup: %.2fx%n", (double) scalarTime / vectorTime); }}📚 Saiba mais: Vector API Incubator JEP
🔌 Conectividade Melhorada
🔹 JEP 380: Unix Domain Socket Channels
❓ O que é?
Suporte nativo para Unix Domain Sockets para comunicação inter-processo eficiente.
⚠️ Por que é importante?
Oferece comunicação IPC mais rápida e segura em sistemas Unix/Linux.
// Unix Domain Sockets no Java 16import java.nio.channels.*;import java.net.*;
public class UnixDomainSocketExample {
// Servidor usando Unix Domain Socket public static void startServer(String socketPath) throws IOException { UnixDomainSocketAddress address = UnixDomainSocketAddress.of(socketPath);
try (ServerSocketChannel serverChannel = ServerSocketChannel.open(StandardProtocolFamily.UNIX)) { serverChannel.bind(address); System.out.println("Server listening on: " + socketPath);
while (true) { try (SocketChannel clientChannel = serverChannel.accept()) { handleClient(clientChannel); } catch (IOException e) { System.err.println("Error handling client: " + e.getMessage()); } } } }
private static void handleClient(SocketChannel clientChannel) throws IOException { ByteBuffer buffer = ByteBuffer.allocate(1024);
while (clientChannel.read(buffer) > 0) { buffer.flip();
// Echo back to client while (buffer.hasRemaining()) { clientChannel.write(buffer); }
buffer.clear(); } }
// Cliente usando Unix Domain Socket public static void connectClient(String socketPath, String message) throws IOException { UnixDomainSocketAddress address = UnixDomainSocketAddress.of(socketPath);
try (SocketChannel channel = SocketChannel.open(StandardProtocolFamily.UNIX)) { channel.connect(address);
// Enviar mensagem ByteBuffer buffer = ByteBuffer.wrap(message.getBytes()); channel.write(buffer);
// Ler resposta buffer.clear(); int bytesRead = channel.read(buffer); buffer.flip();
String response = new String(buffer.array(), 0, bytesRead); System.out.println("Server response: " + response); } }
// Exemplo de uso para comunicação entre microsserviços public static class MicroserviceCommunication { private final String socketPath;
public MicroserviceCommunication(String serviceName) { this.socketPath = "/tmp/" + serviceName + ".sock"; }
public void startService() throws IOException { // Limpar socket existente Files.deleteIfExists(Path.of(socketPath));
UnixDomainSocketAddress address = UnixDomainSocketAddress.of(socketPath);
try (ServerSocketChannel serverChannel = ServerSocketChannel.open(StandardProtocolFamily.UNIX)) { serverChannel.bind(address); System.out.println("Microservice listening on: " + socketPath);
// Configurar permissões do socket Path socketFile = Path.of(socketPath); socketFile.toFile().setReadable(true, false); socketFile.toFile().setWritable(true, false);
while (!Thread.currentThread().isInterrupted()) { try (SocketChannel clientChannel = serverChannel.accept()) { processRequest(clientChannel); } } } }
private void processRequest(SocketChannel channel) throws IOException { ByteBuffer lengthBuffer = ByteBuffer.allocate(4); channel.read(lengthBuffer); lengthBuffer.flip();
int messageLength = lengthBuffer.getInt(); ByteBuffer messageBuffer = ByteBuffer.allocate(messageLength); channel.read(messageBuffer); messageBuffer.flip();
String request = new String(messageBuffer.array()); String response = handleBusinessLogic(request);
// Enviar resposta byte[] responseBytes = response.getBytes(); ByteBuffer responseBuffer = ByteBuffer.allocate(4 + responseBytes.length); responseBuffer.putInt(responseBytes.length); responseBuffer.put(responseBytes); responseBuffer.flip();
channel.write(responseBuffer); }
private String handleBusinessLogic(String request) { // Simular processamento return "Processed: " + request.toUpperCase(); }
public String sendRequest(String targetService, String request) throws IOException { String targetSocketPath = "/tmp/" + targetService + ".sock"; UnixDomainSocketAddress address = UnixDomainSocketAddress.of(targetSocketPath);
try (SocketChannel channel = SocketChannel.open(StandardProtocolFamily.UNIX)) { channel.connect(address);
// Enviar request com tamanho byte[] requestBytes = request.getBytes(); ByteBuffer buffer = ByteBuffer.allocate(4 + requestBytes.length); buffer.putInt(requestBytes.length); buffer.put(requestBytes); buffer.flip();
channel.write(buffer);
// Ler resposta ByteBuffer lengthBuffer = ByteBuffer.allocate(4); channel.read(lengthBuffer); lengthBuffer.flip();
int responseLength = lengthBuffer.getInt(); ByteBuffer responseBuffer = ByteBuffer.allocate(responseLength); channel.read(responseBuffer); responseBuffer.flip();
return new String(responseBuffer.array()); } } }}📚 Saiba mais: Unix Domain Socket Channels JEP
🎯 Impacto e Importância
Java 16 consolidou mudanças fundamentais:
- ✅ Records final estabeleceram padrão moderno para data classes
- ✅ Pattern Matching final simplificaram verificações de tipo
- ✅ Vector API introduziram computação SIMD
- ✅ Unix Domain Sockets melhoraram comunicação IPC
- ✅ Sealed Classes refinadas prepararam para finalização
- ✅ Encapsulation by default melhoraram segurança
📅 Informações da Versão
- 📅 Lançamento: 16 de março de 2021
- 🔧 Tipo: Feature Release
- ⚡ Suporte: Terminado em setembro de 2021
- 🎯 Status: Marco - Records e Pattern Matching finais
- 🔄 Migração: Simples - principalmente consolidação
🔗 Links Úteis
📚 Documentação Oficial
🏗️ Records Final
🔍 Pattern Matching Final
🚀 Vector API
🔌 Unix Domain Sockets
💻 Exemplos e Práticas
Arquitetura, código e aprendizado contínuo.