Skip to main content

lambda Usage

Java 8 Functional Programming

NOTES: All content taken from Java 8 Functional Programming

Refactoring Legacy Code

public Set<String> findLongTracks(List<Album> albums) {
Set<String> trackNames=new HashSet<>();
for(Album album : albums) {
for (Track track : album.getTrackList()) {
if (track.getLength() > 60) {
String name=track.getName();
trackNames.add(name);
}
}
}
return trackNames;
}

Refactor 1 for to forEach

public Set<String> findLongTracks(List<Album> albums) {
Set<String> trackNames=new HashSet<>();
albums.stream()
.forEach(album-> {
album.getTracks()
.forEach(track-> {
if (track.getLength() > 60) {
String name=track.getName();
trackNames.add(name);
}
});
});
return trackNames;
}

Refactor 2 if to filter

public Set<String> findLongTracks(List<Album> albums) {
Set<String> trackNames=new HashSet<>();
albums.stream()
.forEach(album-> {
album.getTracks()
.filter(track-> track.getLength() > 60)
.map(track-> track.getName())
.forEach(name-> trackNames.add(name));
});
return trackNames;
}

Refactor 3 Outermost forEach to flatMap

public Set<String> findLongTracks(List<Album> albums) {
Set<String> trackNames=new HashSet<>();
albums.stream()
.flatMap(album-> album.getTracks())
.filter(track-> track.getLength() > 60)
.map(track-> track.getName())
.forEach(name-> trackNames.add(name));
return trackNames;
}

Refactor 4 forEach adding elements to collect for collection

public Set<String> findLongTracks(List<Album> albums) {
return albums.stream()
.flatMap(album-> album.getTracks())
.filter(track-> track.getLength() > 60)
.map(track-> track.getName())
.collect(toSet());
}

Function Method Parameter Overloading

overloadedMethod((x, y)-> x+y);

private interface IntegerBiFunction extends BinaryOperator<Integer> {
}
private void overloadedMethod(BinaryOperator<Integer> Lambda) {
System.out.print("BinaryOperator");
}
private void overloadedMethod(IntegerBiFunction Lambda) {
System.out.print("IntegerBinaryOperator");
}

Accept BinaryOperator and a subclass of this interface as parameters respectively. When calling these methods, the type of Lambda expression inferred by Java is exactly the type of the most specific functional interface. For example, when Example 4-7 chooses between the two methods in Example 4-8, the output is IntegerBinaryOperator.

General Principles

  • If there is only one possible target type, it is inferred from the parameter type in the corresponding functional interface;

  • If there are multiple possible target types, it is inferred from the most specific type;

  • If there are multiple possible target types and the most specific type is ambiguous, the type needs to be manually specified.

Interface Multiple Inheritance

public interface Jukebox {
public default String rock() {
return "... all over the world! ";
}
}

public interface Carriage {
public default String rock() {
return "... from side to side";
}
}

public class MusicalCarriage implements Carriage, Jukebox {
}

javac is not clear which interface's method should be inherited, so the compiler will report an error: class Musical Carriage inherits unrelated defaults for rock() from types Carriage and Jukebox.

public class MusicalCarriage
implements Carriage, Jukebox {
@Override
public String rock() {
return Carriage.super.rock();
}
}

General Principles

  • Classes win over interfaces. If there is a method body or abstract method declaration in the inheritance chain, then the method defined in the interface can be ignored.

  • Subclasses win over parent classes. If an interface inherits another interface, and both interfaces define a default method, then the method defined in the subclass wins.

  • No rule three. If the above two rules do not apply, the subclass must either implement the method or declare the method as abstract.

  1. For example, when using parallel streams, forEach method cannot guarantee that elements are processed in order. If processing in order is required, forEachOrdered method should be used.

Sub-collector groupingBy

public Map<Artist, List<String>> nameOfAlbums(Stream<Album> albums) {
return albums.collect(groupingBy(Album::getMainMusician, mapping(Album::getName, toList())));
}

mapping allows perform map-like operations on the collector's container. But need to specify what kind of collection class to use to store results, such as toList.

Refactoring Domain Methods

public long countFeature(ToLongFunction<Album> function) {
return albums.stream()
.mapToLong(function)
.sum();
}
public long countTracks() {
return countFeature(album-> album.getTracks().count());
}
public long countRunningTime() {
return countFeature(album-> album.getTracks()
.mapToLong(track-> track.getLength())
.sum());
}
public long countMusicians() {
return countFeature(album-> album.getMusicians().count());
}

Tips

  • It is simple to judge whether an operation is lazy evaluation or eager evaluation: just look at its return value. If the return value is Stream, then it is lazy evaluation; if the return value is another value or empty, then it is eager evaluation.

  • After extracting the logic of lambda into a method, you can test the method and cover all boundary cases.

  • Use peek method to observe lambda intermediate values.

Set<String> nationalities = album.getMusicians()
.filter(artist-> artist.getName().startsWith("The"))
.map(artist-> artist.getNationality())
.peek(nation-> System.out.println("Found nationality: "+nation))
.collect(Collectors.<String>toSet());
Agreement
The code part of this work is licensed under Apache License 2.0 . You may freely modify and redistribute the code, and use it for commercial purposes, provided that you comply with the license. However, you are required to:
  • Attribution: Retain the original author's signature and code source information in the original and derivative code.
  • Preserve License: Retain the Apache 2.0 license file in the original and derivative code.
The documentation part of this work is licensed under Creative Commons Attribution-NonCommercial-ShareAlike 4.0 International License . You may freely share, including copying and distributing this work in any medium or format, and freely adapt, remix, transform, and build upon the material. However, you are required to:
  • Attribution: Give appropriate credit, provide a link to the license, and indicate if changes were made.
  • NonCommercial: You may not use the material for commercial purposes. For commercial use, please contact the author.
  • ShareAlike: If you remix, transform, or build upon the material, you must distribute your contributions under the same license as the original.