[JAVA] Mutual conversion between Function and Consumer

Introduction

If you want to execute only side effects because you can discard the return value for the method that takes Function as an argument, you cannot directly pass a Consumer type variable. You need to convert it to a Function type in some way.

Note that this article uses Function and Consumer as examples for simplicity, but all conversions between a functional interface that returns a value and a functional interface that does not return are the same. If you want to use another functional interface, please read as appropriate.

Conversion example

You can convert it as follows.

// Function<T, R>f to Consumer<T>Conversion to
Consumer<T> c = f::apply;

// Consumer<T>c to Function<T, Void>Conversion to
Function<T, Void> f = arg -> {
    c.accept(arg);
    return null;
};

If you pass a method reference as a Consumer type, no conversion is required even if it is a function type that returns a value. The above conversion also uses that specification (see below).

Utility method

If you don't want to convert a Consumer to a Function on the fly, you can create a utility class and provide a static method for the conversion.

public class Functions {
    public static <T> Function<T, Void> of(Consumer<T> consumer) {
        return arg -> {
            consumer.accept(arg);
            return null;
        };
    }
}

//Example of use
Stream.of("1","2","3").map(Functions.of(System.out::println)).forEach(v -> {});

If the target is your own functional interface, it may be easier to define it as a default method.

@FunctionalInterface
public static interface MyConsumer<T> {
    public abstract void doSomething(T t);
    public default Function<T, Void> asFunction() {
        return arg -> {
            doSomething(arg);
            return null;
        };
        //If you also use the above utility class
        // return Functions.of(this::doSomething);
    }
}

//Example of use
MyConsumer<String> m = System.out::println;
Stream.of("a","b","c").map(m.asFunction()).forEach(v -> {});

By the way, this example is just for demonstration, so usually use forEach (System.out :: println). Just in case.

Digression 1 (Specific examples and miscellaneous thoughts about design)

I often write utility methods like this to prevent resource closing omissions.

//Files to close without permission#lines()
public static <R> R safeLines(Path path, Function<Stream<String>, R> lineProcessor) throws IOException {
    try(Stream<String> stream = Files.lines(path)) {
        return lineProcessor.apply(stream);
    }
}

//Example of use
Path path = Paths.get("example.txt");
Optional<String> head = safeLines(path, Stream::findFirst);
System.out.printf("First line: %s%n", head.orElse(""));

If you prepare such a method and use this method whenever you handle text files in the project, you do not have to worry about accidentally missing a close. However, the argument of this method is Function, so you cannot pass Consumer.

//For example, even if there is a method like this
public class StreamUtils {
    public static void printAll(Stream<?> stream) {
        stream.forEach(System.out::println);
    }
}

//This will result in a compilation error
safeLines(path, StreamUtils::printAll);

//Need to do this
safeLines(path, stream -> {StreamUtils.printAll(stream); return null;});

If the original is lambda, I still give up, but it is a pain to write a lambda with a block and return null; when it seems that the method reference is enough.

If you can change the template side, you can prepare a similar method [^ 1] that takes Consumer as an argument, but of course there are cases where you can not change it, and prepare two methods for each template [ It would be cheaper to have one conversion method than ^ 2].

[^ 1]: Normally, the method with the same name is overloaded, but when called with lambda, the method call is often ambiguous (the argument type needs to be specified), so in this case the method name is It is safer to divide.

[^ 2]: I think it would be nice to abstract it one step further, but it doesn't have to be that much. became.

By the way, if you can change the API of the method you pass in the method reference, you can change the return type to Void instead of void, but I'm not sure if this is the way to go.

//do this
public class StreamUtils {
    public static Void printAll(Stream<?> stream) {
        stream.forEach(System.out::println);
        return null;
    }
}

//Pass through
safeLines(path, StreamUtils::printAll);

Actually, this is more versatile, but it's still not a general way of writing, and it's a subtle point that you have to specify return null; after all.

Digression 2 (Case that does not require conversion)

As mentioned at the beginning, conversion may not be necessary in the first place depending on the passing method.

//When passing as a variable, the type is specified, so conversion is always required.
Function<String, String> f = String::trim;

//Of course compilation error: Type mismatch
Consumer<String> c = f;

//Cast is also a run-time exception: ClassCastException
Consumer<String> c = (Consumer<String>) f;

//Pass through
Consumer<String> c = f::apply;

//If you pass it directly by method reference, you can leave it as it is
Consumer<String> c = String::trim;

For details, refer to the language specification 15.13.2. Type of a Method Reference. please refer to. Roughly speaking, if the receiving result type is void, the method referencing result type can be anything. Otherwise, both the receiver and the method reference must return a non-void and compatible type.

By the way, lambda is described in 15.12.2.1. Identify Potentially Applicable Methods. Yes, to put it simply

--If the lambda body is an expression, it can be treated as a sentence. --If the lambda body is a block, the value is not returned

In either case, you can pass it without conversion (or modification).

//Compile error: Void methods cannot return a value
Consumer<String> c = s -> s;

//Pass through
Consumer<String> c = s -> s.trim();

//Compile error: Void methods cannot return a value
Consumer<String> c = s -> {return s.trim();};

//Pass through
Consumer<String> c = s -> {s.trim(); return;};

Well, Lambda isn't a reusable one, so you won't get this error unless you copy and paste it.

Recommended Posts

Mutual conversion between Function and Consumer
[Swift] Mutual conversion between Int and Data
Mutual conversion between Java objects and JSON using Moshi
Conversion between Kotlin nullable and Java Optional
Hex and UIColor mutual conversion in Swift
Summary of mutual conversion between Groovy's Default Groovy Methods and Java's Stream API
Switch between JDK 7 and JDK 8
Difference between vh and%
Difference between i ++ and ++ i
Difference between product and variant
Difference between redirect_to and render
[Java] Difference between == and equals
Rails: Difference between resources and resources
Difference between puts and print
Difference between redirect_to and render
Difference between CUI and GUI
Difference between variables and instance variables
Relationship between Controller and View
Difference between mockito-core and mockito-all
Difference between class and instance
Difference between bundle and bundle install
Connection between ViewModel and XML
Relationship between package and class
Difference between ArrayList and LinkedList
Difference between render and redirect_to
Difference between List and ArrayList
Differences between IndexOutOfBoundsException and ArrayIndexOutOfBoundsException
Difference between .bashrc and .bash_profile
Difference between StringBuilder and StringBuffer
Difference between render and redirect_to
Difference between render and redirect_to