There are a couple of improvements in Java 9. We will cover it one by one with the examples of each.
1.) Interface private methods: In Java 9, we can create private methods in interfaces. The use case is very simple: similar to classes where you put the repetitive code in a private method and reuse that in other methods, you can simply create one private helper method in the interface as well, that can be used by other default methods, which helps us to prevent writing boilerplate code.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
interface Util { | |
default int numberOfStudents() { | |
return helper(); | |
} | |
private int helper() { | |
return 10; | |
} | |
static int getCores() { | |
return helper1(); | |
} | |
private static int helper1() { | |
return 2; | |
} | |
} | |
public class Sample { | |
public static void main(String[] args) { | |
System.out.println("OK"); | |
} | |
} |
2.) No _(underscore): In Java 9, underscore is a keyword. As such you can’t use this as a variable name as you can in Java8. Even in Java8, using this as a variable name generates a warning but in Java9 it throws an error. So, the _(underscore ) is no more used as an identifier.
Now, the question is why did they do that?
The answer is very simple: to give a meaningful and appropriate name to the variable.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
public class Sample { | |
public static void main(String[] args) { | |
int _ = 4; | |
System.out.println(_); | |
} | |
} |
This will throw a compile time error: As of release 9, ‘_’ is a keyword, and may not be used as an identifier.
3.) takeWhile: In Java 9, we have a method takeWhile that is similar to limit() in Java 8 with the only difference, that is:
limit() is based on the count value that we are passing to it as a parameter. Let’s take an example.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
public class Limit { | |
public static void main(String[] args) { | |
List<Integer> numbers = Arrays.asList(11,22,33,44,55,66,77,88,99); | |
numbers.stream() | |
.limit(4) | |
.forEach(System.out::println); | |
} | |
} |
In the above example, I said the limit is 4 so it will only allow me to pass 4 values.
i.e: 11, 22, 33, 44
Now, let’s take a scenario: what if I want to use limit, not on a fixed number but I want a limit based on a condition?
takeWhile(predicate) is the solution to this problem. It will close the gate as soon as the predicate is false. Let’s go through this with one example:
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
public class Sample { | |
public static void main(String[] args) { | |
List<Integer> numbers = Arrays.asList(11, 22, 33, 44, 55, 66, 77, 88, 99); | |
numbers.stream() | |
.takeWhile(e -> e < 66) | |
.forEach(System.out::println); | |
} | |
} |
4.) dropWhile: In Java 9, we have a method dropWhile() that is similar to skip() in Java 8 with the only difference, that is:
skip(count) is based on the count value that we are passing to it as a parameter. Let’s take an example.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
public class SkipExample { | |
public static void main(String[] args) { | |
List<Integer> numbers = Arrays.asList(11, 22, 33, 44, 55, 66, 77, 88, 99); | |
numbers.stream() | |
.skip(5) | |
.forEach(System.out::println); | |
} | |
} |
In the above example, it will skip the first five values and it will start printing from the 6th value. Same as earlier situation, what if I want to use skip(count) on condition basis, then dropWhile(predicate) is the solution. Let’s go through this with one example:
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
public class DropWhileExample { | |
public static void main(String[] args) { | |
List<Integer> numbers = Arrays.asList(11, 22, 33, 44, 55, 66, 77, 88, 99); | |
numbers.stream() | |
.dropWhile(e -> e < 55) | |
.forEach(System.out::println); | |
} | |
} |
5.) IntStream iterate: In Java 8 we have an alternative to for-loop i.e.
IntStream.range(0, 5) .forEach(System.out::println);
The above statement does same work as below for-loop.
for(int i=0; i<5; i++){ System.out.println(i); }
Let’s take one scenario where I want to increment the counter value in for-loop by 2 each time as below:
for(int i=0; i<5; i = i+2){ System.out.println(i); }
In Java 8, this is not really easy to do. Java 9 overcame this kind of situation as below:
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
public class IntStreamIterateExample { | |
public static void main(String[] args) { | |
IntStream.iterate(0, i -> i<5, i -> i+2 ) | |
.forEach(System.out::println); | |
} | |
} |
In the above example, we can see how we can write code in a functional way to solve this kind of problem. It’s good to know what type of arguments iterate() method takes.
IntStream.iterate(seed, predicate, function)
The first argument is the seed value and the second argument is the predicate and the last argument is a function.
6.) Improved Optional: In Java8, there were some problems with Optional that we will cover with the below example.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
public class IfPresentOrElseExample { | |
public static void main(String[] args) { | |
List<Integer> numbers = Arrays.asList(10, 20, 30, 40, 50, 60, 70, 80, 90); | |
Optional<Integer> first = numbers.stream().filter(a -> a > 500).findFirst(); | |
if (first.isPresent()) { | |
System.out.println(first.get()); | |
} else { | |
System.out.println("Value not found"); | |
} | |
} | |
} |
In the above example, there is one limitation, that is, we are writing code in an imperative pattern instead of the functional pattern because we are following best practices instead of using get() on optional. This is the limitation in Java 8.
Now, we will see the example how Java 9 overcomes this problem.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
public class IfPresentExample { | |
public static void main(String[] args) { | |
List<Integer> numbers = Arrays.asList(10, 20, 30, 40, 50, 60, 70, 80, 90); | |
Optional<Integer> first = numbers.stream().filter(a -> a > 500).findFirst(); | |
first.ifPresentOrElse( a-> System.out.println(a), () -> System.out.println("")); | |
} | |
} |
These were just the basic yet powerful new features of Java 9. We will come up with more advanced features such as Jlink and modularity in the next series of this blog.
References: https://www.youtube.com/watch?v=Yacu1yUktjY&t=3498s