As many know, the new JAVA 1.8 version was released in October 2021, with improvements, new features and fixes for recurring bugs from the old version.
Below, see the latest news and practical, objective examples. Good reading!
Main new features introduced in JAVA 1.8 Next, we will exemplify the main new features in JAVA 1.8. They are: Default Methods in interfaces; Lambda and Functional interfaces; Method References; Stream; New Dates API; Default Methods in interfaces Below, we will create a List of type String containing titles of some remarkable games that will be used in the examples of the content.
List games = new ArrayList<>(); games.add(“Top Gear”); games.add(“The Legend of Zelda: Ocarina of Time”); games.add(“Shadow of the Colossus”); In the past, the common way to sort objects in a Listlist was to use the Ccollections class and the static sort method.
System.out.println(games);// [Top Gear, The Legend of Zelda: Ocarina of Time, Shadow of the Colossus]Collections.sort(games);System.out.println(games);// [Shadow of the Colossus, The Legend of Zelda: Ocarina of Time, Top Gear] It is important to remember that the code above compiled without the need for any changes. This happens because the String class implements an interface called Comparable, which requires the declaration of the compareTo method.
Within this technique, the configuration is done in lexicographic order.
What if we want to sort the words in a different order? If we want to sort words by size, for example, we need to create a comparator to inform the sort method.
It is necessary to create a class. Its name doesn't matter much, but it is mandatory that it has implemented the Comparator interface and a declaration for the compare method.
class StringComparatorBySize implements Comparator { public int compare(String s1, String s2) { if(s1.length() < s2.length()) return –1; if(s1.length() > s2.length()) return 1; return 0; }} Now, we need to pass this comparator to the sort method, as it has a rewrite that receives a List and a Comparator.
comparator comparator = new StringComparerBySize();System.out.println(games); // [Top Gear, The Legend of Zelda: Ocarina of Time, Shadow of the Colossus]Collections.sort(games, comparator);System.out.println (games);// [Top Gear, Shadow of the Colossus, The Legend of Zelda: Ocarina of Time] With the new Default Methods feature, we can now create bodied methods within an interface. From this, the List interface now has an implementation of the sort method.
// Sort method inside the Listdefault interface void sort(Comparator<? super E> c) { Object[] a = this.toArray(); Arrays.sort(a, (Comparator) c); ListIterator i = this.listIterator(); for (Object e : a) { i.next(); i.set((E) e);} Note that, to implement sort through List, we must inform a Comparator.
comparator comparator = new StringComparerBySize();games.sort(comparator);System.out.println(games);// [Top Gear, Shadow of the Colossus, The Legend of Zelda: Ocarina of Time] Lambda and Functional interfaces In this topic, let's continue in the same context as the last example.
public class App { public static void main(String[] args) throws Exception { Comparator comparator = new StringComparerBySize(); List games = new ArrayList<>(); games.add(“Top Gear”); games.add(“The Legend of Zelda: Ocarina of Time”); games.add(“Shadow of the Colossus”); games.sort(comparator); System.out.println(games); // [Top Gear, Shadow of the Colossus, The Legend of Zelda: Ocarina of Time] }}class StringBySize Comparator implements Comparator { public int compare(String s1, String s2) { if(s1.length() < s2.length()) return –1; if(s1.length() > s2.length()) return 1; return 0; }} Is it possible to make the lambda code a little better?
public class App { public static void main(String[] args) throws Exception { List games = new ArrayList<>(); games.add(“Top Gear”); games.add(“The Legend of Zelda: Ocarina of Time”); games.add(“Shadow of the Colossus”); games.sort(new Comparator (){ @Override public int compare(String s1, String s2) { if(s1.length() < s2.length()) return –1; if(s1.length() > s2.length()) return 1; return 0; } }); System.out.println(games); // [Top Gear, Shadow of the Colossus, The Legend of Zelda: Ocarina of Time] }} Because the class approach is simple, we can have the java compiler create an Anonymous Classes.
But we can still improve a little more. Note that the compare method has some conditionals testing the sizes of the titles. Essentially, we are testing numbers with numbers and in Integer we have a compare method that does exactly that.
games.sort(new Comparator (){ @Override public int compare(String s1, String s2) { return Integer.compare(s1.length(), s2.length()); }} Attention, the Double type also implements this method. Even though our last code got a little more compact, it's still a little verbose. In this context, a resource called lambda was added, but its operation is only possible through Functional interfaces. These interfaces have 1 single abstract method, but in addition to this method, they can have other methods, as long as they are default or static.
This structure is critical, as this way the compiler knows exactly that the body of the lambda expression we write is the implementation of its only abstract method. So, as the Comparator is a Functional interfaces, we can use a lambda.
games.sort((String s1, String s2) -> { return Integer.compare(s1.length(), s2.length());}); The Java compiler allows us to make the code a little shorter.
games.sort((game1, game2) -> Integer.compare(game1.length(), game2.length())); Method References In this topic, we will continue in the same context as the last example.
public class App { public static void main(String[] args) throws Exception { List games = new ArrayList<>(); games.add(“Top Gear”); games.add(“The Legend of Zelda: Ocarina of Time”); games.add(“Shadow of the Colossus”); games.sort((game1, game2) -> Integer.compare(game1.length(), game2.length())); System.out.println(games); // [Top Gear, Shadow of the Colossus, The Legend of Zelda: Ocarina of Time] }} The Comparator interface has a new method (comparing), which must receive a Function interface, which in turn is a Functional interface.
games.sort(Comparator.comparing(game -> game.length())); It's like we wrote it like that.
//We are talking below: Given a String, we will return an Integer which will be the return of the lambda game -> game.length()Function function = game -> game.length();games.sort(Comparator.comparing(function); In this case, as we have a simple invocation of String's length method, it is possible to use Method References.
matches.sort(Comparator.comparing(String::length)); One highlight, the use of Method References needs attention. This practice only works for simple invocations. In the next topic, we will see an example where it cannot be used.
Stream In this topic, let's continue in the same context as the last example. But now we need a Game class.
public class Game { private String title; private int launch; public Jogo(String title, int launch) { this.title = title; this.launch = launch; } public String getTitle() { return title; } public void setTitle(String title) { this.title = title; } public int getLancamento() { return lancamento; } public void setLancamento(int lancamento) { this.lancamento = lancamento; } } public class App { public static void main(String[] args) throws Exception { List GameList = new ArrayList<>(); listaDeJogos.add(new Jogo(“Top Gear”, 1992)); listaDeJogos.add(new Jogo(“The Legend of Zelda: Ocarina of Time”, 1998)); listaDeJogos.add(new Jogo(“Shadow of the Colossus”, 2005)); }} Stream is a new interface that allows the use of several very interesting methods. Let's filter our list so that only games with releases less than the 2000s are displayed on the terminal.
A simple way to do this, in the old days, would be to create a foreach with a conditional testing each value.
for (Game game : listOfGames) { if (game.getLaunch() < 2000) { System.out.println(game.getTitle()); }} But there is a better way: Invoking the filter from the Stream.
listaDeJogos.stream() .filter(game -> game.getLancamento() < 2000); In this case, we are unable to use Method References, as we are not using a simple filter invocation.
This piece of code does not solve our problem, it is testing item by item with our conditional, but a modification to a stream does not modify the collection/object that generated it.
To work, we can use forEach, which Stream itself implements.
listOfGames.stream() .filter(game -> game.getLaunch() < 2000) .forEach(game -> System.out.println(game.getTitle())); // Top Gear // The Legend of Zelda: Ocarina of Time As we don't have the toString method in the Game class, we can't use the Method References, because we need to inform which parameter System.out should print in the terminal.
New Date API We now have several classes with different methods that make work related to dates in Java much easier.
Here are some examples of LocalDate: LocalDate date = LocalDate.now();System.out.println(date);// 2021-08-17date = LocalDate.of(2021, Month.DECEMBER, 15);System. out.println(date);// 2021-12-15 To use the DateTimeFormatter to format a LocalDate, you can do it as follows: DateTimeFormatter formador = DateTimeFormatter.ofPattern(“dd/MM/yyyy”);String novaDate = date.format(formatator);System.out.println(newDate);// 15/12/2021 Example of how to extract the nominal day of the week using DayOfWeek DayOfWeek week = DayOfWeek.from(date);System.out.println (week); // WEDNESDAY Examples of LocalDateTime: LocalDateTime time = LocalDateTime.now();System.out.println(time); // 2021-08-17T11:29:56.861110time = LocalDateTime.of(2021, Month. DECEMBER, 15, 21, 30, 0);System.out.println(time);// 2021-12-15T21:30 Example of how to format a LocalDateTime using DateTimeFormatter: DateTimeFormatter formadorTime = DateTimeFormatter.ofPattern(“dd/MM/ yyyy hh:mm:ss");String novaTime = time.format(formatadorTime);System.out.println(novaTime); // 15/12/2021 09:30:00 Example of how to compare LocalDate using Period: LocalDate date1 = LocalDate.of(2021, Month.DECEMBER, 15);LocalDate date2 = LocalDate.of(2051, Month.JANUARY, 22);Period period = Period.between(date1, date2);System.out.println(period) ;// P29Y1M7DSystem.out.printf( “Missing %d years, %d month and %d days.”, periodo.getYears(), periodo.getMonths(), periodo.getDays());// Missing 29 years, 1 month and 7 days.
Conclusion about JAVA 1.8 What was exemplified in the topics is just a very superficial view of all the power of these new changes. There are many other methods that provide good solutions to various problems that eventually arise in the development of our applications. The advice that remains is: always keep in mind that, if we are racking our brains to write some method, it is likely that there is already a ready-made method to do it for us. It is always worth checking the existing documentation and solutions.
Learn more on our Blog!