Java 8 Concepts¶
Note
Java 8 was released in 18th March 2014, so it’s high time to look into Java 8 Features. here we will look into Java 8 features with examples
Some of the important Java 8 features are;¶
- forEach() method in Iterable interface -- go
- default and static methods in Interfaces
- Functional Interfaces and Lambda Expressions
- Java Stream API for Bulk Data Operations on Collections
- Java Time API
- Collection API improvements
- Concurrency API improvements
- Java IO improvements
- Miscellaneous Core API improvements
1. forEach() method in Iterable interface¶
Whenever we need to traverse through a Collection, we need to create an Iterator whose whole purpose is to iterate over and then we have business logic in a loop for each of the elements in the Collection. We might get ConcurrentModificationException if iterator is not used properly.
Java 8 has introduced forEach method in java.lang.Iterable interface so that while writing code we focus on business logic only. forEach method takes java.util.function.Consumer object as argument, so it helps in having our business logic at a separate location that we can reuse. Let’s see forEach usage with simple example
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 | |
java.util.ConcurrentModificationException¶
java.util.ConcurrentModificationException is a very common exception when working with java collection classes. Java Collection classes are fail-fast, which means if the Collection will be changed while some thread is traversing over it using iterator, the iterator.next() will throw ConcurrentModificationException.
Let’s see the concurrent modification exception scenario with an example.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 | |
Above program will throw java.util.ConcurrentModificationException when execute List Value:1 List Value:2 List Value:3 Exception in thread "main" java.util.ConcurrentModificationException at java.util.AbstractList$Itr.checkForComodification(AbstractList.java:372) at java.util.AbstractList$Itr.next(AbstractList.java:343) at com.journaldev.java.IteratorExample.main(IteratorExample.java:27)
From the output stack trace, its clear that the concurrent modification exception is coming when we call iterator next() function. If you are wondering how Iterator checks for the modification, its implementation is present in AbstractList class where an int variable modCount is defined. modCount provides the number of times list size has been changed. modCount value is used in every next() call to check for any modifications in a function checkForComodification().
Let us run an example using Concurrent Collection classes:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 | |
**How to use forEach() method in Java 8 **
1 2 3 4 5 6 7 8 9 10 11 12 13 | |
2. default and static methods in Interfaces¶
Java 8 interface changes include static methods and default methods in interfaces. Prior to Java 8, we could have only method declarations in the interfaces. But from Java 8, we can have default methods and static methods in the interfaces.
Designing interfaces have always been a tough job because if we want to add additional methods in the interfaces, it will require change in all the implementing classes. As interface grows old, the number of classes implementing it might grow to an extent that it’s not possible to extend interfaces. That’s why when designing an application, most of the frameworks provide a base implementation class and then we extend it and override methods that are applicable for our application.
Java Interface Default Method:----
For creating a default method in java interface, we need to use “default” keyword with the method signature. For example,
1 2 3 4 5 6 7 8 | |
Notice that log(String str) is the default method in the Interface1. Now when a class will implement Interface1, it is not mandatory to provide implementation for default methods of interface. This feature will help us in extending interfaces with additional methods, all we need is to provide a default implementation.
1 2 3 4 5 6 7 8 9 | |
We know that Java doesn’t allow us to extend multiple classes because it will result in the “Diamond Problem” where compiler can’t decide which superclass method to use. With the default methods, the diamond problem would arise for interfaces too. Because if a class is implementing both Interface1 and Interface2 and doesn’t implement the common default method, compiler can’t decide which one to chose.
A simple class that is implementing both Interface1 and Interface2 will be:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 | |
Important points about java interface default methods:
- Java interface default methods will help us in extending interfaces without having the fear of breaking implementation classes.
- Java interface default methods has bridge down the differences between interfaces and abstract classes.
- Java 8 interface default methods will help us in avoiding utility classes, such as all the Collections class method can be provided in the interfaces itself.
- Java interface default methods will help us in removing base implementation classes, we can provide default implementation and the implementation classes can chose which one to override.
- One of the major reason for introducing default methods in interfaces is to enhance the Collections API in Java 8 to support lambda expressions.
Java Interface Static Method:--
Java interface static method is similar to default method except that we can’t override them in the implementation classes. This feature helps us in avoiding undesired results incase of poor implementation in implementation classes. Let’s look into this with a simple example.
1 2 3 4 5 6 7 8 9 10 11 12 13 | |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 | |
Java interface static method is visible to interface methods only, if we remove the isNull() method from the MyDataImpl class, we won’t be able to use it for the MyDataImpl object. However like other static methods, we can use interface static methods using class name. For example, a valid statement will be:
Important points about java interface static method:
- Java interface static method is part of interface, we can’t use it for implementation class objects.
- Java interface static methods are good for providing utility methods, for example null check, collection sorting etc.
- Java interface static method helps us in providing security by not allowing implementation classes to override them. 4.We can use java interface static methods to remove utility classes such as Collections and move all of it’s static methods to the corresponding interface, that would be easy to find and use.
3. Functional Interfaces and Lambda Expressions¶
Functional interfaces are new concept introduced in Java 8. An interface with exactly one abstract method becomes Functional Interface. We don’t need to use @FunctionalInterface annotation to mark an interface as Functional Interface. @FunctionalInterface annotation is a facility to avoid accidental addition of abstract methods in the functional interfaces. You can think of it like @Override annotation and it’s best practice to use it. java.lang.Runnable with single abstract method run() is a great example of functional interface.
One of the major benefits of functional interface is the possibility to use lambda expressions to instantiate them. We can instantiate an interface with anonymous class but the code looks bulky.
1 2 3 4 5 | |
Since functional interfaces have only one method, lambda expressions can easily provide the method implementation. We just need to provide method arguments and business logic. For example, we can write above implementation using lambda expression as:
1 2 3 | |
we can know more about lambda expressions ----Java 8 Lambda Expressions
So lambda expressions are means to create anonymous classes of functional interfaces easily.
A new package java.util.function has been added with bunch of functional interfaces to provide target types for lambda expressions and method references.
4. Java Stream API for Bulk Data Operations on Collections¶
A new java.util.stream has been added in Java 8 to perform filter/map/reduce like operations with the collection. Stream API will allow sequential as well as parallel execution. This is one of the best feature for Collections and usually with Big Data, we need to filter out them based on some conditions.
Collection interface has been extended with stream() and parallelStream() default methods to get the Stream for sequential and parallel execution. Let’s see their usage with simple example.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 | |
let’s see why it was required. Suppose we want to iterate over a list of integers and find out sum of all the integers greater than 10.
Prior to Java 8, the approach to do it would be:
1 2 3 4 5 6 7 8 9 10 11 | |
There are three major problems with the above approach:
1.We just want to know the sum of integers but we would also have to provide how the iteration will take place, this is also called external iteration because client program is handling the algorithm to iterate over the list. 2.The program is sequential in nature, there is no way we can do this in parallel easily. 3.There is a lot of code to do even a simple task.
To overcome all the above shortcomings, Java 8 Stream API was introduced. We can use Java Stream API to implement internal iteration, that is better because java framework is in control of the iteration.
Internal iteration provides several features such as sequential and parallel execution, filtering based on the given criteria, mapping etc.
Most of the Java 8 Stream API method arguments are functional interfaces, so lambda expressions work very well with them. Let’s see how can we write above logic in a single line statement using Java Streams.
1 2 3 | |
To know more about Streams goTo --- Java 8 Stream API features
5. Java Time API¶
It has always been hard to work with Date, Time and Time Zones in java. There was no standard approach or API in java for date and time in Java. One of the nice addition in Java 8 is the java.time package that will streamline the process of working with time in java.
Just by looking at Java Time API packages, it will be very easy to use. It has some sub-packages java.time.format that provides classes to print and parse dates and times and java.time.zone provides support for time-zones and their rules.
The new Time API prefers enums over integer constants for months and days of the week. One of the useful class is DateTimeFormatter for converting datetime objects to strings
Java 8 Date Time API is JSR-310 implementation. It is designed to overcome all the flaws in the legacy date time implementations. Some of the design principles of new Date Time API are:
- Immutability: All the classes in the new Date Time API are immutable and good for multithreaded environments.
- Separation of Concerns: The new API separates clearly between human readable date time and machine time (unix timestamp). It defines separate classes for Date, Time, DateTime, Timestamp, Timezone etc.
-
Clarity: The methods are clearly defined and perform the same action in all the classes. For example, to get the current instance we have now() method. There are format() and parse() methods defined in all these classes rather than having a separate class for them. All the classes use Factory Pattern and Strategy Pattern for better handling. Once you have used the methods in one of the class, working with other classes won’t be hard.
-
Utility operations: All the new Date Time API classes comes with methods to perform common tasks, such as plus, minus, format, parsing, getting separate part in date/time etc.
- Extendable: The new Date Time API works on ISO-8601 calendar system but we can use it with other non ISO calendars as well.
Java 8 Date Time API Packages
Java 8 Date Time API consists of following packages.
- java.time Package: This is the base package of new Java Date Time API. All the major base classes are part of this package, such as LocalDate, LocalTime, LocalDateTime, Instant, Period, Duration etc. All of these classes are immutable and thread safe. Most of the times, these classes will be sufficient for handling common requirements.
- java.time.chrono Package: This package defines generic APIs for non ISO calendar systems. We can extend AbstractChronology class to create our own calendar system.
- java.time.format Package: This package contains classes used for formatting and parsing date time objects. Most of the times, we would not be directly using them because principle classes in java.time package provide formatting and parsing methods.
- java.time.temporal Package: This package contains temporal objects and we can use it for find out specific date or time related to date/time object. For example, we can use these to find out the first or last day of the month. You can identify these methods easily because they always have format “withXXX”.
- java.time.zone Package: This package contains classes for supporting different time zones and their rules.
Java 8 Date Time API Examples
We have looked into most of the important parts of Java Date Time API. It’s time now to look into most important classes of Date Time API with examples.
- LocalDate
LocalDate is an immutable class that represents Date with default format of yyyy-MM-dd. We can use now() method to get the current date. We can also provide input arguments for year, month and date to create LocalDate instance. This class provides overloaded method for now() where we can pass ZoneId for getting date in specific time zone. This class provides the same functionality as java.sql.Date. Let’s look at a simple example for it’s usage.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 | |
- LocalTime
LocalTime is an immutable class whose instance represents a time in the human readable format. It’s default format is hh:mm:ss.zzz. Just like LocalDate, this class provides time zone support and creating instance by passing hour, minute and second as input arguments. Let’s look at it’s usage with a simple program.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 | |
- LocalDateTime
LocalDateTime is an immutable date-time object that represents a date-time, with default format as yyyy-MM-dd-HH-mm-ss.zzz. It provides a factory method that takes LocalDate and LocalTime input arguments to create LocalDateTime instance. Let’s look it’s usage with a simple example.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 | |
In all the three examples, we have seen that if we provide invalid arguments for creating Date/Time, then it throws java.time.DateTimeException that is a RuntimeException, so we don’t need to explicitly catch it.
We have also seen that we can get Date/Time data by passing ZoneId, you can get the list of supported ZoneId values from it’s javadoc.
To know other most important classes of Date Time API go through --- Java 8 Date Time API Features
6. Collection API improvements¶
We have already seen forEach() method and Stream API for collections. Some new methods added in Collection API are:
- Iterator default method forEachRemaining(Consumer action) to perform the given action for each remaining element until all elements have been processed or the action throws an exception.
- Collection default method removeIf(Predicate filter) to remove all of the elements of this collection that satisfy the given predicate.
- Collection spliterator() method returning Spliterator instance that can be used to traverse elements sequentially or parallel.
- Map replaceAll(), compute(), merge() methods.
- Performance Improvement for HashMap class with Key Collisions
7. Concurrency API improvements¶
Some important concurrent API enhancements are:
- ConcurrentHashMap compute(), forEach(), forEachEntry(), forEachKey(), forEachValue(), merge(), reduce() and search() methods.
- CompletableFuture that may be explicitly completed (setting its value and status).
- Executors newWorkStealingPool() method to create a work-stealing thread pool using all available processors as its target parallelism level.
8. Java IO improvements¶
Some IO improvements known to me are:
- Files.list(Path dir) that returns a lazily populated Stream, the elements of which are the entries in the directory.
- Files.lines(Path path) that reads all lines from a file as a Stream.
- Files.find() that returns a Stream that is lazily populated with Path by searching for files in a file tree rooted at a given starting file.
- BufferedReader.lines() that return a Stream, the elements of which are lines read from this BufferedReader.
9. Miscellaneous Core API improvements¶
Some misc API improvements that might come handy are:
- ThreadLocal static method withInitial(Supplier supplier) to create instance easily.
- Comparator interface has been extended with a lot of default and static methods for natural ordering, reverse order etc.
- min(), max() and sum() methods in Integer, Long and Double wrapper classes.
- logicalAnd(), logicalOr() and logicalXor() methods in Boolean class.
- ZipFile.stream() method to get an ordered Stream over the ZIP file entries. Entries appear in the Stream in the order they appear in the central directory of the ZIP file.
- Several utility methods in Math class.
- jjs command is added to invoke Nashorn Engine.
- jdeps command is added to analyze class files
- JDBC-ODBC Bridge has been removed.
- PermGen memory space has been removed