Introduction:
Java 8 is a major milestone in the history of the Java programming language released in 2014. It introduced many new features and improvements over its previous version, Java 7. Java 8 was a significant upgrade to the language and offered developers several new capabilities to improve their code's quality, readability, and maintainability.
key differences between Java 8 and its predecessor, Java 7:
1. In Java 7, when you wanted to sort a list of strings, you had to create an instance of a Comparator class and pass it to the sort method of the Collections class. The Comparator class has a single method called compare, which takes two strings as input and returns an integer value that represents the order of the two strings. In Java 7, you had to define this method by creating an anonymous inner class, which made the code verbose
List<String> names = Arrays.asList("John", "Jane", "Mary");
Collections.sort(names, new Comparator<String>() {
public int compare(String s1, String s2)
{
return s1.compareTo(s2);
} }
In Java 8, you can use lambda expressions to create a Comparator instance in a more concise way. A lambda expression is a shorthand way of creating an instance of a functional interface, which is an interface with a single abstract method. In this case, the Comparator interface is a functional interface because it has a single method called compare.
List<String> names = Arrays.asList("John", "Jane", "Mary");
Collections.sort(names, (s1, s2) -> s1.compareTo(s2));
The lambda expression (s1, s2) -> s1.compareTo(s2) is equivalent to the anonymous inner class that we used in Java 7. The two parameters s1 and s2 represent the two strings that we want to compare, and the body of the lambda expression simply calls the compareTo method on s1 and passes s2 as an argument. By using a lambda expression instead of an anonymous inner class, the code becomes more concise and easier to read. The lambda expression is also more expressive because it directly expresses the intent of the code, which is to sort the list based on the natural order of the strings.
2. In the code snippet for Java 7, we can see that to calculate the sum of all the elements in the List, we need to use a for loop and iterate through each element in the List. We need to declare a variable 'sum' and initialize it to zero. Inside the loop, we add each element to the 'sum' variable.
List< Integer> numbers = Arrays.asList(1, 2, 3, 4, 5);
int sum = 0;
for (int n : numbers)
{
sum += n;
}
However, in Java 8, we can use the Stream API to achieve the same result in a more concise and functional manner. We first convert the List of Integer values to a Stream using the stream() method. We then use the mapToInt() method to convert each Integer value to an int value. Finally, we use the sum() method to calculate the sum of all the int values in the stream.
List<Integer> numbers = Arrays.asList(1, 2, 3, 4, 5);
int sum = numbers.stream() .mapToInt(Integer::intValue) .sum();
The code in Java 8 is shorter, more readable, and more functional than the code in Java 7. It also makes use of the new features in Java 8 that make it easier to work with collections of data.
Streams are used to perform operations on a collection of objects. Intermediate operations and Terminal operations are two types of operations that can be performed on a stream of elements. Intermediate operations are operations that transform the stream into a new stream. They do not produce a result immediately but rather return a new stream that can be further operated upon. Examples of intermediate operations are map(), filter(), distinct(), sorted(), flatMap() etc.
Terminal operations, on the other hand, are operations that produce a result or a side-effect. They are the final operations that can be performed on a stream and they trigger the processing of the stream. Examples of terminal operations are forEach(), reduce(), collect(), count(), min(), max() etc.
Here is an example how intermediate and terminal operators can be used.
List<String> names = Arrays.asList("John", "Jane", "Adam", "Mary");
long count = names.stream() .filter(name ->
name.startsWith("J")) .map(String::toUpperCase) .sorted() .count();
System.out.println(count);
Output:2
3. In Java 7, when dealing with null values, developers would typically use an if statement to check if a variable is null before performing any operations on it. This can lead to verbose code and potential bugs if null checks are forgotten.
String str = null;
if (str != null)
{
System.out.println(str.length());
} else
{
System.out.println("String is null");
}
In Java 8, the Optional class provides a cleaner and more explicit way to handle null values. The ofNullable method creates an Optional object that may or may not contain a non-null value. We can then use the isPresent method to check if the value is present, and get method to retrieve the value if it is present.
Optional<String> optionalStr = Optional.ofNullable(null);
if (optionalStr.isPresent())
{
System.out.println(optionalStr.get().length());
} else
{
System.out.println("String is null");
}
In the code example above, the optionalStr variable is created using the ofNullable method and is assigned a null value. The if statement then checks if optionalStr contains a value using isPresent method. If it contains a value, we retrieve its length using the get method. If not, we print a message indicating that the string is null.
This approach helps to reduce the likelihood of null pointer exceptions and leads to cleaner and more readable code.
4. In Java 7,working with dates and times required the use of the Calendar class, which could be cumbersome and error-prone. The code example below shows how to set a specific date using the Calendar class and then convert it to a Date object for output.
Calender cal = Calendar.getInstance(); cal.set(Calendar.YEAR, 2023);
cal.set(Calendar.MONTH, Calendar.JANUARY);
cal.set(Calendar.DAY_OF_MONTH, 1);
Date date = cal.getTime(); System.out.println(date);
In Contrast, Java 8 introduced a new Date and Time API that provides a more streamlined and readable way to work with dates and times. The code example above shows how to create a LocalDate object with a specific date and output it directly, without the need for conversions or complicated calculations.
LocalDate date = LocalDate.of(2023, Month.JANUARY, 1); System.out.println(date);
The new Date and Time API allows you to work with dates and times in a more intuitive and readable way.
5. In Java 7, there is no support for default methods. If you wanted to add a new method to an existing interface in Java 7, you would have to create a new interface that extends the original interface and defines the new method. Any classes that implemented the original interface would have to be updated to implement the new interface as well. This could lead to a lot of extra work and potential problems if existing code relied on the old interface. But in Java 8 default methods provides a convenient way to add new methods to existing interfaces without breaking existing code
Here's an example of default methods in core Java 8:
public interface MyInterface {
void myMethod();
default void myDefaultMethod()
{
System.out.println("This is a default method in MyInterface");
} }
In the above Example, MyInterface defines a default method called myDefaultMethod(). This method has an implementation, but classes that implement MyInterface can override this implementation if needed. Note that the default keyword is used to define the default method.
" Last but not the least, while core Java 8 introduced several new features and improvements over core Java 7, such as lambda expressions, functional interfaces, default methods, the stream API etc. , it is important to note that both versions have their own strengths and use cases. Ultimately, the choice of which version to use depends on the specific needs of the project and the preferences of the development team."