3

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?

2
  • docs.oracle.com/javase/8/docs/api/java/util/function/… Commented Apr 25, 2023 at 7:20
  • 1
    Function.apply() is non-static 1-parameter method, so it needs an instance to run; but Function::apply is not providing such instance, so needs an additional argument when called, meaning it is equivalent to a 2-parameter function (first argument will be this) || BiFunction<T, U, R> function = Function::apply ~= (T t, U u) -> t.apply(u) returning type R ( if I am correct, in this specific case: (Consumer<String> t, String u) -> t.apply(s) ) Commented Apr 25, 2023 at 7:30

2 Answers 2

5

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 write String::length, eventhough this method has a different signature. The requested signature is:

Integer apply(String x) { ... }

but we supplied a method that only has int length(), so no String argument at all. However, that method is non-static and operates on instances of String. 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: String
  • R: Consumer<T>, so Consumer<UnaryOperator<String>>

Quite complex, but okay.

Actual signature

Now, the requested signature is:

Consumer<UnaryOperator<String>> apply(UnaryOperator<String> t, String u)

When you give your method reference Function::apply, whose signature is:

R apply(T t)

so, in this case:

Consumer<UnaryOperator<String>> apply(String u)

Then, the first argument from the required signature (UnaryOperator<String> t) is again deducted from the fact that Function::apply is a non-static method that operates on the Function instance, which happens to be compatible with UnaryOperator<String>, since UnaryOperator extends Function.

Implementation

So Function::apply essentially is the same as implementing your BiFunction as:

Consumer<UnaryOperator<String>> apply(UnaryOperator<String> t, String u) {
  return t.apply(u);
}

Taking the function from the first argument and applying it to the second, returning the result.

Sign up to request clarification or add additional context in comments.

1 Comment

BTW the length method could have been declared public int length(String this) (only works inside the String class) - this acts as the first parameter of a non-static method (( reason for this: allow annotations on this ))
1

Function::apply is a method reference that refers to the apply method of the Function interface in the line Seq s1 = seq.zip(split, Function::apply). It serves as the second argument to the zip method, which requires a BiFunction functional interface and anticipates one that accepts two arguments of types T and U and returns a result of type R.

In this instance, T is an UnaryOperator<String> (from Seq<UnaryOperator<String>> seq), U is a String (from List<String> split), and R is a String when the zip method is executed. Because String::apply has the same signature as the apply method of the Function interface, which accepts a single argument of type String and returns a String, it is used as a reference to the Function::apply method.

The seq sequence of functions will be applied to each element of the split list by the zip method as iterates over the split list's elements. The apply method of the Function interface will be used to apply each UnaryOperator<String> in seq to the corresponding String element in the split.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.