From Java8 to Java11

This is a list of the changes at Java’s API I found interesting or that I may use frecuently. Not all the changes from Java9, 10 & 11 are listed here.

Java 9

Java REPL (JShell)

It stands for Java Shell. It’s used to easily execute and test any Java construction like a class, interface, enum, etc.

Module System

The way we deploy Java-Based applications using jars has a lot of limitations & drawbacks. Some of these are: The JRE & JDK are too big; JAR files are too big to use in small devices and applications; There’s no strong encapsulation, public is open to everyone.

The new Module System introduces new features to avoid all this. More information here.

Factory Methods for Inmutable Collections (List, Map, Set & Map.Entry)

(I’ll use Lists as an example in this file, but this is valid for Maps and Sets too)

Until Java8 we could use Collections.unmodifiableList() to achieve this, but this is really verbose. Now we can do the same with

List inmutableList = List.of("bla", "ble", "bli");

Private methods in Interfaces

To avoid redundant code and more re-usability we can use private and private static methods directly in interfaces now. Their behaviour is the same as in a normal class

public interface Card {

	private Long createcardID() {
		// calculate and return ID
	}

	private static void displayCardDetails() {
		// implement
	}

}

Try-with resources improvements

The new version improves the one which was implemented in Java SE 7 with better automatic resource management.

Java SE 7 Example

BufferedReader reader1 = new BufferedReader(new FileReader("file.txt"));
try(BufferedReader reader2 = reader1) {
	// do something
}

Java 9 Example

BufferedReader reader1 = new BufferedReader(new FileReader("file.txt"));
try(reader1) {
	// do something
}

Optionals’ improvements

#stream()

If a value is present in the given Optional object, stream returns a sequential Stream with that value. Otherwise, it returns an empty Stream.

Stream<Optional> employee = this.getEmployee(id);
Stream employeeStream = employee  
	.flatMap(Optional::stream);

#ifPresentOrElse(Consumer<? super Tl> action, Runnable emptyAction)

If a value is present, performs the given action with the value, otherwise performs the given empty-based action.

Optional<Integer> opt = Optional.of(4);
opt.ifPresentOrElse(System.out::println,  
	() -> System.out.println("Not found"));

#or(Supplier<? extends Optional<? extends T» supplier)

Returns the value contained by the Optional if it has one, or the value given by the supplier if empty.

Optional<String> opt = Optional.of("bla");
Supplier<Optional<String>> supplier =  
	() -> Optional.of("ble");
System.out.println(opt.or(supplier)); // bla
Optional<String> opt = Optional.empty()
Supplier<Optional<String>> supplier =  
	() -> Optional.of("ble");
System.out.println(opt.or(supplier)); // ble

Streams

#takeWhile(Predicate) / #dropWhile(Predicate)

Takes a predicate as an argument and returns a Stream of the subset until the predicate returns false for the first time. If the first value is false, it gives an empty Stream back.

Stream.of(1, 2, 3, 4, 5)
	  .takeWhile(i -> i < 4)
	  .forEach(System.out::println); // 1  
					 // 2  
					 // 3  

#iterate(T seed, Predicate<? super T> hasNext, UnaryOperator next)

It’s similar to the for loop. First parameter is init value, second is the condition and third is to generate the next element.

// start value = 2; while value < 20;   
// then value *= value  
IntStream.iterate(2, x -> x < 20, x -> x * x)
		 .forEach(System.out::println);

#ofNullable()

Returns a sequential Stream containing a single element, or an empty Stream if null.

Stream<Integer> i = Stream.ofNullable(9);
i.forEach(System.out::println); // 9
Stream<Integer> i = Stream.ofNullable(null);
i.forEach(System.out::println); //

References

https://www.journaldev.com/13121/java-9-features-with-examples

Java 10

Local-variable Type Inference (var)

It adds type inference to declarations of local variables with initializers. It can only be used in the following scenarios:

  • Limited only to local variable with initializer
    var numbers = List.of(1, 2, 3, 4);
    
  • Indexes of foreach loops
    for(var number : numbers) {
      // do something
    }
    
  • Local declared in for loop
    for(var i = 0; i < numbers.size(); i++) {
      // do something
    }
    

API Improvements

Collection#copyOf(Collection)

Returns an unmodifiable list, map or set containing the entries provided. For a List, if the original list is subsequently modified, the returned List will not reflect this modifications.

List<String> strings = new ArrayList<>();
strings.add("bla");
strings.add("ble");

List<String> copy = List.copyOf(strings);
strings.add("bli");

System.out.println(strings); // bla, ble, bli
System.out.println(copy); // bla, ble

Collectors#toUnmodifiable…()

Different methods to collect into a unmodifiable collection.

List<String> strings = new ArrayList<>();
strings.add("bla");
strings.add("ble");

List<String> unmodifiableStrings =  
   strings.stream()  
  .collect(Collectors.toUnmodifiableList());

Optional#orElseThrow()

Is the same as Optional#get() but the doc states that this is a preferred alternative.

String name = "bla";
Optional<String> optionalName = Optional.ofNullable(name);
String s = optionalName.orElseThrow(); // bla
Optional<String> empty = Optional.empty();
// throws java.util.NoSuchElementException  
String s = empty.orElseThrow();  

References

https://www.journaldev.com/20395/java-10-features#local-variable-type-inference-jep-286 https://howtodoinjava.com/java10/java10-features/

Java 11

Single-file Applications

Now it’s possible to run single-file applications without the need to compile. It really simplifies the process to test new features.

In the file we have to write the following shebang

#!/usr/bin/java --source11

To run

java HelloWorld.java

Parameters before the name of the source file are passed as parameters to the java launcher. Parameters after are passed as parameters to the program.

java -classpath /home/bla/java HelloWorld.java Param1

Type inference for lambdas

var was introduced in Java10. The ability to use it in lambdas has been introduced in Java11.

list.stream()
	.map((var s) -> s.toLowerCase())
	.collect(Collectors.toList());

We already had type inference in lambdas, the difference is that with var we can use now type annotations to Lambda parameters.

lists.stream()
	.map((@Notnull var s) -> s.toLowerCase())
	.collect(Collectors.toList());

More info on type annotations.

String API improvements

#repeat

Allows concatenating a String with itself a number x of times

String s = "bla ";
String result = s.repeat(2); // bla bla

#isBlank

Checks wether a String is empty or contains a whitespace

String s = "";
boolean result = s.isBlank(); // true

#strip

Deletes whitespace on the start & end of a String. The difference with String#trim is that this is unicode aware. It relies on the same definition of whitespace as String#isBlank. This is a preparation for raw strings.

String s = " bla ";
String result = s.strip(); // bla (without spaces)

#lines

To easily split a String into a Stream of separate lines.

String s = "bla\nble";
List<String> lines = s.lines()  
	.collect(Collectors.toList()); // bla  
  				                 // ble

Reference(s)

https://www.azul.com/90-new-features-and-apis-in-jdk-11/
https://4comprehension.com/java-11-string-api-updates/