Você já pensou como a linguagem Java evoluiu nos últimos anos?
Neste artigo vamos relembrar os principais recursos que o Java trouxe ao longo das versões para tornar a vida do desenvolvedor mais fácil, permitindo escrever código mais limpo, conciso e simples.
Para começar, o primeiro grande salto! Do Java 4 para Java 5/6
Há muito tempo tive a oportunidade de trabalhar em um projeto com java 4 em um mundo que o java 5/6 já eram presentes.
Nesse tempo distante não existia a conversão automática de tipos primitivos para seu par em forma de objeto.
List lista = new ArrayList();
// Adicionar número: precisa criar explicitamente o objeto
lista.add(new Integer(10));
lista.add(new Integer(20));
// Recuperar número: precisa fazer cast + chamar intValue()
Integer obj = (Integer) lista.get(0);
int valor = obj.intValue();
A partir do Java 5 nossa vida de desenvolvedor melhorou muito com Autoboxing/Unboxing e Generics.
List<Integer> nums = new ArrayList<Integer>();
nums.add(10); // autoboxing
int x = nums.get(0); // unboxing
Generics ajuda a eliminar casts manuais e tornara o código mais seguro.
Além disso tivemos a introdução de Enum, Varargs, Annotations, for-each e Static import.
na categoria qualidade de código, para mim a feature mais marcante do Java 6 foi o "Pluggable Annotation Processing Tool(APT)". Ela abriu caminho para frameworks como Lombok e JPA criarem código automaticamente.
Esquecido mas não menos importante, Java 7
Nós ganhamos o "Diamond Operator (<>)", "Multi-catch" e "Try-with-resources (ARM – Automatic Resource Management)".
Diamond Operator (<>) nos permite evitar a repetição de generics na criação de objetos.
// Antes
Map<String, List<Integer>> mapa = new HashMap<String, List<Integer>>();
// Java 7
Map<String, List<Integer>> mapa = new HashMap<>();
Multi-catch permite tratar múltiplas exceções diferentes em um único bloco.
try {
metodoArriscado();
} catch (IOException | SQLException e) {
e.printStackTrace();
}
Try-with-resources ajuda no fechamento automático de recursos que implementam AutoCloseable. (Quem nunca esqueceu de fechar um arquivo?)
// Antes
BufferedReader br = null;
try {
br = new BufferedReader(new FileReader("dados.txt"));
System.out.println(br.readLine());
} finally {
if (br != null) br.close();
}
// Java 7
try (BufferedReader br = new BufferedReader(new FileReader("dados.txt"))) {
System.out.println(br.readLine());
} // fecha sozinho
O Java 7 trouxe também Strings no switch (finalmente!).
Vamos falar do segundo grande salto! Java 8
Neste salto, para mim os maiores destaques vão para "Lambda", "Streams API", "Optional" e "java.time".
Também tivemos os Default Methods em Interfaces — um recurso útil para evoluir APIs sem quebrar compatibilidade, embora seu uso ainda gere discussões entre desenvolvedores.
Podemos começar com "java.time".
Inspirada no Joda-Time. Surgiu para resolver o problema histórico das classes antigas de data e hora.
É sério! Já tentou somar X dias em um Date?
import java.util.Calendar;
import java.util.Date;
public class ExemploAntesJava8 {
public static void main(String[] args) {
// Data atual
Date hoje = new Date();
System.out.println("Hoje: " + hoje);
// Usando Calendar para somar 18 dias
Calendar cal = Calendar.getInstance();
cal.setTime(hoje);
cal.add(Calendar.DAY_OF_MONTH, 18);
Date daqui18dias = cal.getTime();
System.out.println("Daqui a 18 dias: " + daqui18dias);
}
}
Sempre que eu usava o Calendar precisava lembrar que os meses eram de 0 a 11 e não de 1 a 12.
import java.util.Calendar;
import java.util.Date;
public class ExemploAntesJava8 {
public static void main(String[] args) {
// Olha a pegadinha!
Calendar cal2 = Calendar.getInstance();
cal2.set(2025, Calendar.SEPTEMBER, 19); // Setembro = 8
}
}
java.time trouxe um modelo moderno, imutável e muito mais claro com classes imutáveis e thread-safe.
import java.time.LocalDate;
public class ExemploComJavaTime {
public static void main(String[] args) {
// Data atual
LocalDate hoje = LocalDate.now();
System.out.println("Hoje: " + hoje);
// Somando 18 dias
LocalDate daqui18dias = hoje.plusDays(18);
System.out.println("Daqui a 18 dias: " + daqui18dias);
}
}
Streams API, mudou a forma de como fazemos loops no Java.
// Antes
List<String> nomes = Arrays.asList("Ana", "Bruno", "Alberto", "Carla");
int cont = 0;
for (String n : nomes) {
if (n.startsWith("A")) cont++;
}
// Java 8
long cont = nomes.stream()
.filter(n -> n.startsWith("A"))
.count();
Optional chegou para lidar de forma segura com valores que podem estar ausentes.
// Antes (null check em cascata)
String cidade = null;
if (usuario != null && usuario.getEndereco() != null) {
cidade = usuario.getEndereco().getCidade();
}
// Java 8 (Optional)
String cidade = Optional.ofNullable(usuario)
.map(Usuario::getEndereco)
.map(Endereco::getCidade)
.orElse("DESCONHECIDA");
Quem nunca retornou um Optional em um query method do Spring Data Jpa? Se não deveria tentar.
Para finalizar, Lambda!
No passado, sem os lambdas precisávamos de classes anônimas para implementar comportamentos simples.
List<String> nomes = Arrays.asList("Carlos", "Ana", "Bruno");
// Antes
Collections.sort(nomes, new Comparator<String>() {
@Override
public int compare(String a, String b) {
return a.compareTo(b);
}
});
// Usando Java 8 (lambda)
List<String> nomes = Arrays.asList("Carlos", "Ana", "Bruno");
nomes.sort((a, b) -> a.compareTo(b));
// Ou ainda mais curto com method reference
nomes.sort(String::compareTo);
Veja a parte dois aqui.
Top comments (0)