Overview
The much-awaited Java 8 evolution focuses on the shortcode, ease of programming and smart use of multicore processors. The significant features include Lambda expressions, Streams API and Default Methods in interfaces, ...
Features
Let's get started by introducing the new concept in Java and comparing it with the equivalent in C#
Lambda Expressions
Before Lambda, let's get familiarized about the history of passing methods as parameters.
C++ introduced function pointer
C# 3 introduced lambdas and supports delegates, anonymous methods and interface implementations
Java 8 introduced functional interfaces and support anonymous methods implementation
Java supports the passing of behaviours using class implementations. Anonymous allows the class implementations without a name.
Suppose Dogs in dog care are represented by the below Dog class.
public class Dog {
public enum Sex {
MALE, FEMALE
}
String name;
Sex gender;
public int getAge() {
// ...
}
public int getGender() {
// ...
}
public void printDog() {
// ...
}
}
Basic Filters
Let's search for a dog less than 1 years
public static void printDogsLessThan(List<Dog> dogs, int age) {
for (Dog d : dogs) {
if (d.getAge() <= age) {
d.printDog();
}
}
}
Let's search for a male dog greater than 2 years
public static void printDogsGreaterThan(List<Dog> dogs, int age, Sex gender) {
for (Dog d : dogs) {
if (d.getAge() >= age && d.getGender() == gender) {
d.printDog();
}
}
}
Generalized Filters
We will define the search criteria using an interface and then implement the filter critereon as desired.
Definition
Specify the filter criteria using the FilterDog interface.
interface FilterDog {
boolean filter(Dog d);
}
The following class searches for an old male dog and implements the filter method.
class FilterOldMaleDogService implements FilterDog {
public boolean filter(Dog d) {
return d.gender == Dog.Sex.MALE &&
d.getAge() >= 6;
}
}
The following method prints the dogs based on our filter criteria.
public static void printDogs(
List<Dog> dogs, FilterDog filterCriteria) {
for (Dog d : dogs) {
if (filterCriteria.filter(d)) {
d.printDog();
}
}
}
Usage
printDogs(
dogs, new FilterOldMaleDogService());
Anonymous Filters
printDogs(
dogs,
new FilterDog() {
public boolean filter(Dog p) {
return d.getGender() == Dog.Sex.MALE
&& d.getAge() >= 6;
}
}
);
Lambda Filters
We saw in the previous section, the bulky syntax of anonymous classes
printDogs(
dogs,
(Dog d) -> d.getGender() == Dog.Sex.MALE
&& p.getAge() >= 6
);
Predicates
It's a standard functional interface that defines a method test
which accepts the object and returns a Boolean variable and is similar to Predicate in C#.
We use FilterDog to pass filter behaviour and print filtered dogs.
interface FilterDog {
boolean filter(Dog d);
}
public static void printDogs(
List<Dog> dogs, FilterDog filterCriteria) {
for (Dog d : dogs) {
if (filterCriteria.filter(d)) {
d.printDog();
}
}
}
Instead of using FilterDog
, let's use the Predicate defined below.
interface Predicate<T> {
boolean test(T t);
}
interface Predicate<Dog> {
boolean test(Dog t);
}
public static void printDogs(
List<Dog> dogs, Predicate<Dog> filterCriteria) {
for (Dog d : dogs) {
if (filterCriteria.test(d)) {
d.printDog();
}
}
}
The usage remains the same as previous lambda call.
printDogs(
dogs,
(Dog d) -> d.getGender() == Dog.Sex.MALE
&& p.getAge() >= 6
);
Consumer
It's a standard functional interface that defines a method accept
which accepts the object and return void and is similar to the action behaviour in C#.
We use FilterDog to pass filter behaviour and print filtered dogs, let's allow the passing of print as action behaviour.
public static void printDogs(
List<Dog> dogs, Predicate<Dog> filterCriteria) {
for (Dog d : dogs) {
if (filterCriteria.test(d)) {
d.printDog();
}
}
}
Let's define processDogs
method that works action behaviour using Consumer
.
public static void processDogs(
List<Dog> dogs,
Predicate<Dog> filterCriteria,
Consumer<Dog> process) {
for (Dog d : dogs) {
if (filterCriteria.test(d)) {
process.accept(d);
}
}
}
The usage now accepts the print method.
printDogs(
dogs,
d -> d.getGender() == Dog.Sex.MALE
&& p.getAge() >= 6,
d -> d.printDog()
);
Function
It's a standard functional interface that defines a method apply
which accepts the object and return another object and is similar to Func behaviour in C#.
We use FilterDog to pass filter behaviour and print filtered dogs, let's allow the passing of print as action behaviour.
public static void processDogs(
List<Dog> dogs,
Predicate<Dog> filterCriteria,
Consumer<Dog> process) {
for (Dog d : dogs) {
if (filterCriteria.test(d)) {
process.accept(d);
}
}
}
Let's define processDogsString
method that works Function behaviour using Function
which accepts Dog as input and returns string as output.
public static void processDogsString(
List<Dog> dogs,
Predicate<Dog> filterCriteria,
Function<Dog, String> mapper,
Consumer<String> process) {
for (Dog d : dogs) {
if (filterCriteria.test(d)) {
String data = mapper.apply(d);
process.accept(data);
}
}
}
The usage now accepts the print method.
processDogsString(
dogs,
d -> d.getGender() == Dog.Sex.MALE
&& p.getAge() >= 6,
d -> d.getName(),
name -> System.out.println(name)
);
Generics Conclusion
The following method filters, maps and then act.
public static <X, Y> void processElements(
Iterable<X> source,
Predicate<X> tester,
Function <X, Y> mapper,
Consumer<Y> block) {
for (X d : source) {
if (tester.test(d)) {
Y data = mapper.apply(d);
block.accept(data);
}
}
}
Comparison
Please find the below table comparing various aspects of Lambdas in C# and Java.
C# | Java |
---|---|
creaed using delegate keyword delegate bool FilterDog(Dog dog); |
created using interfaces interface FilterDog { boolean filter(Dog d);} |
initialization takes a reference to a method, let's say we have a FilterMaleDog method of type FilterDog. var myFilter = new FilterDog(FilterMaleDog); |
initialization involves object creation of class, let's say FilterMaleDog, implementing the interface FilterDog. FilterDog myFilter = new FilterMaleDog(); |
natural usage using method for invoke myFilter(d) |
mention both interface and method name myFilter.filter(d) |
capture variables defined in the parent scope | capture only final or effectively final variables |
Note: The interfaces Predicate , Consumer and Function in Java maps functionally to Predicate , Action and Func in C#. |
References
https://docs.microsoft.com/en-us/dotnet/csharp/programming-guide/delegates/
https://docs.oracle.com/javase/tutorial/java/javaOO/lambdaexpressions.html#approach4
http://my-techno-arena.blogspot.com/2014/10/lambda-expressions-comparison-between-c.html