If I create a Functional interface:
@FunctionalInterface
public class Consumer2<T1, T2> {
void accept(T1 t1, T2 t2);
default Consumer1<T2> curry(T1 t1) {
return (t2) -> accept(t1, t2);
}
}
Now, if I have a class:
public class MyClass {
public void printStrings(String a, String b) {
System.out.println(a + ": " + b);
}
}
MyClass myClass = new MyClass();
Now, if I want to use my functional interface, I can:
Consumer2<String, String> printString = myClass::printStrings;
printString.curry("hello").accept("world");
But I can't do something like:
myClass::printStrings.curry("hello").accept("world");
which makes sense, because Java has no way of knowing that myClass::printStrings
can be applied to the functional interface Consumer2
. To do this, I have created a utility class:
public class F {
public static <T1, T2> Consumer2<T1, T2> c2(Consumer2<T1, T2> fn) {
return fn;
}
}
Then I can:
F.c2(myClass::printStrings).curry("hello").accept("world");
Even, this will work:
((Consumer2<String, String>)myClass::printStrings).curry("hello").accept("world");
As long as there is some way for Java 8 to understand that functional type in this case. So, the question is, what is the best way to do it, while possibly avoiding the boilerplate?
The Function Interface is a part of the java. util. function package which has been introduced since Java 8, to implement functional programming in Java. It represents a function which takes in one argument and produces a result.
A functional interface is an interface that contains only one abstract method. They can have only one functionality to exhibit. From Java 8 onwards, lambda expressions can be used to represent the instance of a functional interface. A functional interface can have any number of default methods.
identity. static <T> Function<T,T> identity() Returns a function that always returns its input argument. Type Parameters: T - the type of the input and output objects to the function Returns: a function that always returns its input argument.
function Description. Functional interfaces provide target types for lambda expressions and method references. Each functional interface has a single abstract method, called the functional method for that functional interface, to which the lambda expression's parameter and return types are matched or adapted.
You are not currying but performing partial function application. These operations are related, but not identical. Currying means to transform your Consumer2<T1, T2>
to a Function<T1,Consumer1<T2>>
. When applying that curried function to a T1
value you get what your method is effectively doing.
It’s easier to use the established name bind
as binding a value to a function’s parameter is something, every developer understands without needing to dive deeply into the world of functional programming.
That said, it’s best to remember that now interface
s can have static
methods, so there is no need for such utility classes. Further, a static
method just returning its argument is of little use on its own, so you may fuse it with the follow-up method that it is supposed to serve. Then, it’s fulfilling the same purpose as the instance method and can be offered as a simple overload:
@FunctionalInterface
public interface Consumer2<T1, T2> {
void accept(T1 t1, T2 t2);
default Consumer1<T2> bind(T1 t1) {
return bind(this, t1);
}
static <T,U> Consumer1<U> bind(Consumer2<? super T, ? super U> c, T t) {
return u -> c.accept(t, u);
}
}
public interface Consumer1<T1> extends Consumer<T1> {}
public class MyClass {
public static void printStrings(String a, String b) {
System.out.println(a + ": " + b);
}
public static void main(String[] args) {
Consumer2.bind(MyClass::printStrings, "hello").accept("world");
}
}
On the other hand, when you use the existing standard interface
s Consumer
and BiConsumer
you have no choice but to offer a utility method in a class different to these interface
s. But the good news is that it’s easy to make the solution consistent then, as you can’t provide anything else but a static
method:
class FunctionUtil {
static <T,U> Consumer<U> bind(BiConsumer<? super T, ? super U> c, T t) {
return u -> c.accept(t, u);
}
}
public class MyClass {
public static void printStrings(String a, String b) {
System.out.println(a + ": " + b);
}
public static void main(String[] args) {
FunctionUtil.bind(MyClass::printStrings, "hello").accept("world");
}
}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With