Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to directly use a function as a Functional Type in Java 8

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?

like image 216
Rohan Prabhu Avatar asked Sep 16 '15 07:09

Rohan Prabhu


People also ask

Is function a functional interface in Java?

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.

What is function functional interface in Java 8?

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.

What is function identity () in Java 8?

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.

Which functional interface does Java provide to serve as data types for lambda expressions?

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.


1 Answers

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 interfaces 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 interfaces Consumer and BiConsumer you have no choice but to offer a utility method in a class different to these interfaces. 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");
    }
}
like image 191
Holger Avatar answered Oct 07 '22 03:10

Holger