Welcome to the unit on Currying in Java! As part of our Advanced Functional Programming Techniques course, this lesson will introduce you to the concept of currying and demonstrate its practical applications in Java. We'll explore how currying can be leveraged to create more modular, reusable, and maintainable code.
In this lesson, you'll discover how to:
Currying is a powerful technique that allows you to transform a function with multiple arguments into a series of functions that each take a single argument. This not only simplifies the function calls but also enables partial application, where you can preset some arguments and delay others for later. By breaking down functions in this way, currying helps create more modular and reusable code. Let’s explore this concept through two practical examples: one demonstrating numerical operations and another focusing on object creation.
Let's start with a basic arithmetic example to illustrate how currying works. In this case, we'll be using currying to break down a simple addition operation:
Java1Function<Integer, Function<Integer, Integer>> curriedAddition = a -> b -> a + b; 2 3Function<Integer, Integer> addTen = curriedAddition.apply(10); 4 5int sumResult = addTen.apply(5); 6 7System.out.println("Sum Result: " + sumResult); // Outputs: Sum Result: 15
In the code above, we first define a curried function curriedAddition
. This function is structured to take two integers, but instead of taking both at once, it takes the first integer (a
) and returns a new function that then takes the second integer (b
). When b
is provided, the two integers are added together.
Now, let’s break this down further:
Creating the Curried Function:
Java1Function<Integer, Function<Integer, Integer>> curriedAddition = a -> b -> a + b;
This line defines the curried function. Here, a -> b -> a + b
means that curriedAddition
takes an integer a
and returns a function that takes another integer b
, which adds b
to a
.
Partial Application:
Java1Function<Integer, Integer> addTen = curriedAddition.apply(10);
By applying the first argument (10
), we create a new function called addTen
. This new function is a partially applied version of the original curriedAddition
function—it’s now a specialized function that adds 10 to any number it’s given.
Final Application and Result:
Java1int sumResult = addTen.apply(5);
When we apply 5
to addTen
, it completes the operation, adding 5
to 10
and resulting in 15
. Finally, we print the result:
Java1System.out.println("Sum Result: " + sumResult); // Outputs: Sum Result: 15
Currying isn’t limited to simple arithmetic—it’s also a powerful tool for object creation.
First, let's define the Person
class that we'll be using in our example:
Java1class Person { 2 private String firstName; 3 private String lastName; 4 private int age; 5 6 public Person(String firstName, String lastName, int age) { 7 this.firstName = firstName; 8 this.lastName = lastName; 9 this.age = age; 10 } 11 12 @Override 13 public String toString() { 14 return "Person: " + firstName + " " + lastName + ", age " + age; 15 } 16}
Now, let’s see how currying can simplify the process of creating a Person
object step by step:
Java1Function<String, Function<String, Function<Integer, Person>>> curriedPersonCreation = firstName -> lastName -> age -> new Person(firstName, lastName, age); 2 3Function<String, Function<Integer, Person>> withFirstName = curriedPersonCreation.apply("John"); 4 5Function<Integer, Person> withLastName = withFirstName.apply("Doe"); 6 7Person person = withLastName.apply(30); 8 9System.out.println("Created Person: " + person); // Outputs: Created Person: John Doe, age 30
Here’s a step-by-step breakdown of what’s happening:
Defining the Curried Function for Object Creation:
Java1Function<String, Function<String, Function<Integer, Person>>> curriedPersonCreation = firstName -> lastName -> age -> new Person(firstName, lastName, age);
This curried function curriedPersonCreation
is designed to create a Person
object. It takes three arguments—firstName
, lastName
, and age
—one at a time. Initially, it takes a firstName
and returns a function that expects the lastName
, which in turn returns another function that takes the age
and finally creates a Person
object.
Applying the First Argument (Partial Application):
Java1Function<String, Function<Integer, Person>> withFirstName = curriedPersonCreation.apply("John");
Here, we apply the firstName
("John") to the curried function. This returns a new function withFirstName
that now only needs the lastName
and age
to complete the object creation.
Applying the Second Argument:
Java1Function<Integer, Person> withLastName = withFirstName.apply("Doe");
Next, we apply the lastName
("Doe") to the withFirstName
function, which returns yet another function withLastName
that only needs the age
to complete the object.
Finalizing the Object Creation:
Java1Person person = withLastName.apply(30);
Finally, we apply the age
(30
) to withLastName
, which creates a Person
object with the full name "John Doe" and age 30. The result is printed:
Java1System.out.println("Created Person: " + person); // Outputs: Created Person: John Doe, age 30
Currying offers several advantages that can significantly improve your coding practices in Java:
Ready to practice? Let’s move on to the exercises where you’ll get hands-on experience with currying in Java!