Have you ever thought about how the Java language has evolved in recent years?
In this article, we'll revisit the key features Java introduced in recent years that make developers’ lives easier and allow writing cleaner code.
To start, the first great leap! From Java 4 to Java 5/6.
A long time ago, I had the opportunity to work with Java 4 in a world where Java 5/6 already existed.
At this time, automatic conversion from primitive types did not exist.
List items = new ArrayList();
// add a number: needs to explicitly instantiate a new object.
items.add(new Integer(10));
items.add(new Integer(20));
// recover a number: needs to cast + call intValue()
Integer obj = (Integer) items.get(0);
int value = obj.intValue();
From Java 5, our lives became better with Autoboxing/Unboxing and Generics.
List<Integer> nums = new ArrayList<Integer>();
nums.add(10); // autoboxing
int x = nums.get(0); // unboxing
Generics help to eliminate manual casts and make the code safer.
Furthermore we got Enum, Varargs, Annotations, for-each and Static import.
The forgotten but not less significant, Java 7
Java 7 introduced "Diamond Operator (<>)", "Multi-catch" and "Try-with-resources (ARM – Automatic Resource Management)".
Diamond Operator (<>) allows us to reduce repetition from Generic expressions when we are declaring objects.
// Before
Map<String, List<Integer>> mapp =
new HashMap<String, List<Integer>>();
// Java 7
Map<String, List<Integer>> mapp = new HashMap<>();
Multi-catch allows us to catch different exceptions in a single block.
try {
someExplosiveMethod();
} catch (IOException | SQLException e) {
e.printStackTrace();
}
Try-with-resources automatically closes resources that implement AutoCloseable. (Who hasn't ever forgotten to close a file?)
// Before
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());
} // close automatically
Java 7 finally introduced Strings in switch statements.
Let's talk about the second great leap! Java 8
In this version for me the greatest highlights were "Lambda", "Streams API", "Optional" e "java.time".
So we got to Default Methods in Interfaces - It's a useful resource to evolve APIs without breaking compatibility.
Let's start with java.time
Inspired by Joda-time. It came to solve the long-standing problems of the old date and time classes.
Seriously! Have you ever tried adding days to a Date?
// before Java 8
import java.util.Calendar;
import java.util.Date;
public class ExampleBeforeJava8 {
public static void main(String[] args) {
// Current date
Date today = new Date();
System.out.println("Today: " + today);
// Using Calendar to add 18 days
Calendar cal = Calendar.getInstance();
cal.setTime(today);
cal.add(Calendar.DAY_OF_MONTH, 18);
Date in18days = cal.getTime();
System.out.println("In 18 days: " + in18days);
}
}
Whenever you use Calendar, you need to remember that months start at 0 (January) and go up to 11 (December).
import java.util.Calendar;
import java.util.Date;
public class ExampleBeforeJava8 {
public static void main(String[] args) {
// See the trap!
Calendar cal2 = Calendar.getInstance();
cal2.set(2025, Calendar.SEPTEMBER, 19); // September = 8
}
}
java.time comes with a modern model: immutable, clearer, and thread-safe.
import java.time.LocalDate;
public class ExampleWithJavaTime {
public static void main(String[] args) {
// Data atual
LocalDate today = LocalDate.now();
System.out.println("Today: " + today);
// Adding 18 days
LocalDate in18days = today.plusDays(18);
System.out.println("In 18 days: " + in18days);
}
}
Streams API changed how we do loops in Java.
// Before
List<String> names = Arrays.asList("Ana", "Bruno", "Alberto", "Carla");
int count = 0;
for (String n : names) {
if (n.startsWith("A")) count++;
}
// Java 8
long count = names.stream()
.filter(n -> n.startsWith("A"))
.count();
Optional helps us deal with missing values in a safe way.
// Before
String city = null;
if (user != null && user.getAddress() != null) {
city = user.getAddress().getCity();
}
// Java 8 (Optional)
String city = Optional.ofNullable(user)
.map(User::getAddress)
.map(Address::getCity)
.orElse("UNKNOWN");
Who hasn't used Optional in a Spring Data query method? If you haven't, you should give it a try.
To finish, Lambda
In the past we needed to use anonymous classes to implement simple behavior. Now with Lambda we can write less code.
List<String> names = Arrays.asList("Carlos", "Ana", "Bruno");
// Before
Collections.sort(names, new Comparator<String>() {
@Override
public int compare(String a, String b) {
return a.compareTo(b);
}
});
// Using Java 8 (lambda)
names.sort((a, b) -> a.compareTo(b));
// OR
names.sort(String::compareTo);
Thanks for reading! Please follow me for the next part, where we’ll talk about Java 9 and 10.
Top comments (0)