I am confused about the following line:
Seq<String> s1 = seq.zip(split, Function::apply);
In this snippet:
static String underscoreToCamel(String str) {
UnaryOperator<String> capitalize = s -> s.substring(0, 1).toUpperCase() + s.substring(1).toLowerCase();
Seq<UnaryOperator<String>> seq = c -> {
c.accept(String::toLowerCase);
while (true) {
c.accept(capitalize);
}
};
List<String> split = Arrays.asList(str.split("_"));
Seq<String> s1 = seq.zip(split, Function::apply);
String a = s1.join("");
return a;
}
public interface Seq<T> {
void consume(Consumer<T> consumer);
static <T> Seq<T> unit(T t) {
return consumer -> consumer.accept(t);
}
default <E> Seq<E> map(Function<T, E> function) {
return consumer -> consume(t -> consumer.accept(function.apply(t)));
}
default <E> Seq<E> flatMap(Function<T, Seq<E>> function) {
return consumer -> consume(t -> function.apply(t).consume(consumer));
}
default String join(String sep) {
StringJoiner joiner = new StringJoiner(sep);
consume(t -> joiner.add(t.toString()));
return joiner.toString();
}
static <T> T stop() {
throw StopException.INSTANCE;
}
default void consumeTillStop(Consumer<T> consumer) {
try {
consume(consumer);
} catch (StopException ignore) {}
}
default <U, R> Seq<R> zip(Iterable<U> iterable, BiFunction<T, U, R> function) {
return c -> {
Iterator<U> iterator = iterable.iterator();
consumeTillStop(t -> {
if (iterator.hasNext()) {
c.accept(function.apply(t, iterator.next()));
} else {
stop();
}
});
};
}
}
I do understand that Function::apply is a method reference and that the method wants a BiFunction<T, U, R>. But I do not get how this is compatible.
What exactly does it resolve to? Why can I supply Function::apply in this case?
Non-static method reference
This is indeed an interesting one, as it uses one of the special rules that method references allow for.
Quick example, suppose you demand a
Function<String, Integer>, then one could writeString::length, eventhough this method has a different signature. The requested signature is:but we supplied a method that only has
int length(), so noStringargument at all. However, that method is non-static and operates on instances ofString. So Java can assume you meant to call that method on that instance, and by that deduce the first parameter to the method. I.e.s -> s.length().Generic details
The same is happening in your setup. First of all, you have to understand what the generics for your requested
BiFunction<T, U, R>resolve to. They are:T:UnaryOperator<String>U:StringR:Consumer<T>, soConsumer<UnaryOperator<String>>Quite complex, but okay.
Actual signature
Now, the requested signature is:
When you give your method reference
Function::apply, whose signature is:so, in this case:
Then, the first argument from the required signature (
UnaryOperator<String> t) is again deducted from the fact thatFunction::applyis a non-static method that operates on theFunctioninstance, which happens to be compatible withUnaryOperator<String>, sinceUnaryOperator extends Function.Implementation
So
Function::applyessentially is the same as implementing yourBiFunctionas:Taking the function from the first argument and applying it to the second, returning the result.