Functional Programming
Method References
Shorthand for Lambdas
Your lambda is just x -> x.toString(). Method reference Object::toString is
cleaner. When lambda only calls an existing method, use method reference instead.
Same result, more readable.
Static method reference
Reference a static method.
import java.util.*;
import java.util.function.*;
public class Static {
private static int parseIntSafe(String s) {
return Integer.parseInt(s.trim());
}
public static void main(String[] args) {
Function<String, Integer> a = s -> Integer.parseInt(s);
Function<String, Integer> b = Integer::parseInt;
String numericInput = ;
System.out.println("a('" + numericInput + "') = " + a.apply(numericInput));
System.out.println("b('" + numericInput + "') = " + b.apply(numericInput));
Function<String, Integer> safe = Static::parseIntSafe;
System.out.println("safe(' 7 ') = " + safe.apply(" 7 "));
List<String> nums = new ArrayList<>(Arrays.asList("10", "2", "30"));
nums.sort(Comparator.comparingInt(Integer::parseInt));
System.out.println("sorted numeric strings: " + nums);
}
}
import java.util.*;
import java.util.function.*;
public class Static {
private static int parseIntSafe(String s) {
return Integer.parseInt(s.trim());
}
public static void main(String[] args) {
Function<String, Integer> a = s -> Integer.parseInt(s);
Function<String, Integer> b = Integer::parseInt;
String numericInput = ;
System.out.println("a('" + numericInput + "') = " + a.apply(numericInput));
System.out.println("b('" + numericInput + "') = " + b.apply(numericInput));
Function<String, Integer> safe = Static::parseIntSafe;
System.out.println("safe(' 7 ') = " + safe.apply(" 7 "));
List<String> nums = new ArrayList<>(Arrays.asList("10", "2", "30"));
nums.sort(Comparator.comparingInt(Integer::parseInt));
System.out.println("sorted numeric strings: " + nums);
}
}
import java.util.*;
import java.util.function.*;
public class Static {
private static int parseIntSafe(String s) {
return Integer.parseInt(s.trim());
}
public static void main(String[] args) {
Function<String, Integer> a = s -> Integer.parseInt(s);
Function<String, Integer> b = Integer::parseInt;
String numericInput = ;
System.out.println("a('" + numericInput + "') = " + a.apply(numericInput));
System.out.println("b('" + numericInput + "') = " + b.apply(numericInput));
Function<String, Integer> safe = Static::parseIntSafe;
System.out.println("safe(' 7 ') = " + safe.apply(" 7 "));
List<String> nums = new ArrayList<>(Arrays.asList("10", "2", "30"));
nums.sort(Comparator.comparingInt(Integer::parseInt));
System.out.println("sorted numeric strings: " + nums);
}
}
ClassName::staticMethod - equivalent to x -> ClassName.staticMethod(x).
Instance method reference
Reference an instance method.
import java.util.function.*;
public class Instance {
static class Prefixer {
private final String prefix;
Prefixer(String prefix) {
this.prefix = prefix;
}
String addPrefix(String s) {
return prefix + s;
}
}
public static void main(String[] args) {
String prefix = ;
Prefixer p = new Prefixer(prefix);
Function<String, String> f1 = s -> p.addPrefix(s);
Function<String, String> f2 = p::addPrefix;
System.out.println(f1.apply("Alice"));
System.out.println(f2.apply("Bob"));
Supplier<String> supplier1 = () -> " hi ".trim();
Supplier<String> supplier2 = " hi "::trim;
System.out.println("supplier1 = '" + supplier1.get() + "'");
System.out.println("supplier2 = '" + supplier2.get() + "'");
}
}
import java.util.function.*;
public class Instance {
static class Prefixer {
private final String prefix;
Prefixer(String prefix) {
this.prefix = prefix;
}
String addPrefix(String s) {
return prefix + s;
}
}
public static void main(String[] args) {
String prefix = ;
Prefixer p = new Prefixer(prefix);
Function<String, String> f1 = s -> p.addPrefix(s);
Function<String, String> f2 = p::addPrefix;
System.out.println(f1.apply("Alice"));
System.out.println(f2.apply("Bob"));
Supplier<String> supplier1 = () -> " hi ".trim();
Supplier<String> supplier2 = " hi "::trim;
System.out.println("supplier1 = '" + supplier1.get() + "'");
System.out.println("supplier2 = '" + supplier2.get() + "'");
}
}
import java.util.function.*;
public class Instance {
static class Prefixer {
private final String prefix;
Prefixer(String prefix) {
this.prefix = prefix;
}
String addPrefix(String s) {
return prefix + s;
}
}
public static void main(String[] args) {
String prefix = ;
Prefixer p = new Prefixer(prefix);
Function<String, String> f1 = s -> p.addPrefix(s);
Function<String, String> f2 = p::addPrefix;
System.out.println(f1.apply("Alice"));
System.out.println(f2.apply("Bob"));
Supplier<String> supplier1 = () -> " hi ".trim();
Supplier<String> supplier2 = " hi "::trim;
System.out.println("supplier1 = '" + supplier1.get() + "'");
System.out.println("supplier2 = '" + supplier2.get() + "'");
}
}
object::method - equivalent to x -> object.method(x).
Unbound instance method
Reference method on parameter object.
import java.util.*;
import java.util.function.*;
public class Unbound {
public static void main(String[] args) {
BiFunction<String, String, Boolean> startsWith1 = (s, prefix) -> s.startsWith(prefix);
BiFunction<String, String, Boolean> startsWith2 = String::startsWith;
String prefixToCheck = ;
System.out.println("startsWith2('Alice','" + prefixToCheck + "') = " +
startsWith2.apply("Alice", prefixToCheck));
System.out.println("startsWith2('Bob','" + prefixToCheck + "') = " +
startsWith2.apply("Bob", prefixToCheck));
List<String> names = new ArrayList<>(Arrays.asList("Charlie", "alice", "Bob"));
names.sort(String::compareToIgnoreCase);
System.out.println("sorted ignore-case: " + names);
List<String> raw = Arrays.asList(" a ", " b", "c ");
List<String> trimmed = new ArrayList<>();
raw.forEach(s -> trimmed.add(s.trim()));
System.out.println("trimmed via lambda: " + trimmed);
trimmed.clear();
raw.forEach(s -> trimmed.add(s.trim()));
System.out.println("trimmed again: " + trimmed);
}
}
import java.util.*;
import java.util.function.*;
public class Unbound {
public static void main(String[] args) {
BiFunction<String, String, Boolean> startsWith1 = (s, prefix) -> s.startsWith(prefix);
BiFunction<String, String, Boolean> startsWith2 = String::startsWith;
String prefixToCheck = ;
System.out.println("startsWith2('Alice','" + prefixToCheck + "') = " +
startsWith2.apply("Alice", prefixToCheck));
System.out.println("startsWith2('Bob','" + prefixToCheck + "') = " +
startsWith2.apply("Bob", prefixToCheck));
List<String> names = new ArrayList<>(Arrays.asList("Charlie", "alice", "Bob"));
names.sort(String::compareToIgnoreCase);
System.out.println("sorted ignore-case: " + names);
List<String> raw = Arrays.asList(" a ", " b", "c ");
List<String> trimmed = new ArrayList<>();
raw.forEach(s -> trimmed.add(s.trim()));
System.out.println("trimmed via lambda: " + trimmed);
trimmed.clear();
raw.forEach(s -> trimmed.add(s.trim()));
System.out.println("trimmed again: " + trimmed);
}
}
import java.util.*;
import java.util.function.*;
public class Unbound {
public static void main(String[] args) {
BiFunction<String, String, Boolean> startsWith1 = (s, prefix) -> s.startsWith(prefix);
BiFunction<String, String, Boolean> startsWith2 = String::startsWith;
String prefixToCheck = ;
System.out.println("startsWith2('Alice','" + prefixToCheck + "') = " +
startsWith2.apply("Alice", prefixToCheck));
System.out.println("startsWith2('Bob','" + prefixToCheck + "') = " +
startsWith2.apply("Bob", prefixToCheck));
List<String> names = new ArrayList<>(Arrays.asList("Charlie", "alice", "Bob"));
names.sort(String::compareToIgnoreCase);
System.out.println("sorted ignore-case: " + names);
List<String> raw = Arrays.asList(" a ", " b", "c ");
List<String> trimmed = new ArrayList<>();
raw.forEach(s -> trimmed.add(s.trim()));
System.out.println("trimmed via lambda: " + trimmed);
trimmed.clear();
raw.forEach(s -> trimmed.add(s.trim()));
System.out.println("trimmed again: " + trimmed);
}
}
String::length - equivalent to s -> s.length(). First param becomes receiver.
Constructor reference
Reference a constructor.
import java.util.*;
import java.util.function.*;
public class Constructor {
static class User {
final String name;
User(String name) {
this.name = name;
}
@Override
public String toString() {
return "User(" + name + ")";
}
}
public static void main(String[] args) {
Function<String, User> makeUser1 = name -> new User(name);
Function<String, User> makeUser2 = User::new;
String userName = ;
System.out.println(makeUser1.apply(userName));
System.out.println(makeUser2.apply("Bob"));
Supplier<List<String>> listSupplier = ArrayList::new;
List<String> list = listSupplier.get();
list.add("x");
list.add("y");
System.out.println("list: " + list);
Supplier<Map<String, Integer>> mapSupplier = LinkedHashMap::new;
Map<String, Integer> map = mapSupplier.get();
map.put("a", 1);
System.out.println("map: " + map);
}
}
import java.util.*;
import java.util.function.*;
public class Constructor {
static class User {
final String name;
User(String name) {
this.name = name;
}
@Override
public String toString() {
return "User(" + name + ")";
}
}
public static void main(String[] args) {
Function<String, User> makeUser1 = name -> new User(name);
Function<String, User> makeUser2 = User::new;
String userName = ;
System.out.println(makeUser1.apply(userName));
System.out.println(makeUser2.apply("Bob"));
Supplier<List<String>> listSupplier = ArrayList::new;
List<String> list = listSupplier.get();
list.add("x");
list.add("y");
System.out.println("list: " + list);
Supplier<Map<String, Integer>> mapSupplier = LinkedHashMap::new;
Map<String, Integer> map = mapSupplier.get();
map.put("a", 1);
System.out.println("map: " + map);
}
}
import java.util.*;
import java.util.function.*;
public class Constructor {
static class User {
final String name;
User(String name) {
this.name = name;
}
@Override
public String toString() {
return "User(" + name + ")";
}
}
public static void main(String[] args) {
Function<String, User> makeUser1 = name -> new User(name);
Function<String, User> makeUser2 = User::new;
String userName = ;
System.out.println(makeUser1.apply(userName));
System.out.println(makeUser2.apply("Bob"));
Supplier<List<String>> listSupplier = ArrayList::new;
List<String> list = listSupplier.get();
list.add("x");
list.add("y");
System.out.println("list: " + list);
Supplier<Map<String, Integer>> mapSupplier = LinkedHashMap::new;
Map<String, Integer> map = mapSupplier.get();
map.put("a", 1);
System.out.println("map: " + map);
}
}
ClassName::new - equivalent to () -> new ClassName() or x -> new ClassName(x).
Method references with collections
Common patterns in collection operations.
import java.util.*;
import java.util.function.*;
public class Collections {
private static <T> void forEach(List<T> items, Consumer<T> action) {
for (T item : items) {
action.accept(item);
}
}
private static <T, R> List<R> map(List<T> items, Function<T, R> fn) {
List<R> out = new ArrayList<>();
for (T item : items) {
out.add(fn.apply(item));
}
return out;
}
public static void main(String[] args) {
List<String> raw = Arrays.asList(" Alice ", " Bob", "CHARLIE ");
List<String> trimmed = map(raw, String::trim);
System.out.println("trimmed: " + trimmed);
List<String> lowered = map(trimmed, String::toLowerCase);
System.out.println("lowered: " + lowered);
forEach(lowered, System.out::println);
List<String> sorted = new ArrayList<>(lowered);
sorted.sort(String::compareTo);
System.out.println("sorted: " + sorted);
}
}
list.forEach(System.out::println) - clean iteration with method reference.
Exercise: Practical.java
Refactor lambdas to method references