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.
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).
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.
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.
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