0

In collectors.java class I found this method. I cannot explain myself why you can use array indexing here. I mean this line (a, t) -> { a[0] = op.apply(a[0], mapper.apply(t)); },

public static <T, U> Collector<T, ?, U> reducing(U identity,
                                Function<? super T, ? extends U> mapper,
                                BinaryOperator<U> op) {
        return new CollectorImpl<>(
                boxSupplier(identity),
                (a, t) -> { a[0] = op.apply(a[0], mapper.apply(t)); },
                (a, b) -> { a[0] = op.apply(a[0], b[0]); return a; },
                a -> a[0], CH_NOID);
    }
9
  • 1
    What do you mean by "why"? Because a is an array? Commented Jan 8, 2023 at 12:50
  • but where in this class is it mentioned that a is an array . The second paramter of CollectorImpl is BiConsumer<A, T> accumulator, In order to write a[0] somewhere needs to say that a is an array Commented Jan 8, 2023 at 12:52
  • 1
    Well for that you would need to find the constructor of CollectorImpl that this is calling. It likely takes some functional interfaces, and those functional interfaces have a single abstract method that takes some array as its first parameter. Commented Jan 8, 2023 at 12:54
  • 2
    Look closely at the signature of boxSupplier :) Commented Jan 8, 2023 at 13:02
  • 1
    The process is called type inference. Commented Jan 8, 2023 at 13:16

1 Answer 1

4

Why is it possible to use an array subscript here? Because it's an array, why wouldn't you be able to use it just like any other array?

The Collectors#reducing method returns a new Collector instance:

    return new CollectorImpl<>(
            boxSupplier(identity),
            (a, t) -> { a[0] = op.apply(a[0], mapper.apply(t)); },
            (a, b) -> { a[0] = op.apply(a[0], b[0]); return a; },
            a -> a[0],
            CH_NOID);

The signature of this CollectorImpl constructor is:

    CollectorImpl(Supplier<A> supplier,
                  BiConsumer<A, T> accumulator,
                  BinaryOperator<A> combiner,
                  Function<A,R> finisher,
                  Set<Characteristics> characteristics) {

The signature of boxSupplier is:

private static <T> Supplier<T[]> boxSupplier(T identity) {
    return () -> (T[]) new Object[] { identity };
}

which returns a Supplier which supplies an array (effectively wrapping the specified identity object lazily in an array with a single element). Consequently, type param A of CollectorImpl has to be an array of the identity's type, meaning that the second and third parameter are a BiConsumer of an array and a BinaryOperator on an array. The formal parameter a in the lambda's parameter list therefore is of an array type. Since it's an array, it can be used like any other array.

This is called type inference:

Type inference is a Java compiler's ability to look at each method invocation and corresponding declaration to determine the type argument (or arguments) that make the invocation applicable. The inference algorithm determines the types of the arguments and, if available, the type that the result is being assigned, or returned. Finally, the inference algorithm tries to find the most specific type that works with all of the arguments.

You could specify the lambda parameter types explicitly to make it clearer:

public static <T, U>
Collector<T, ?, U> reducing(U identity,
                            Function<? super T, ? extends U> mapper,
                            BinaryOperator<U> op) {
    return new CollectorImpl<>(
            boxSupplier(identity),
            (U[] a, T t) -> {a[0] = op.apply(a[0], mapper.apply(t));},
            (U[] a, U[] b) -> {a[0] = op.apply(a[0], b[0]);return a;},
            (U[] a) -> a[0],
            CH_NOID);
}

Or you could replace each lambda expression with an anonymous class:

    return new CollectorImpl<>(
            new Supplier<U[]>() {
                @Override
                public U[] get() {
                    return (U[]) new Object[]{identity};
                }
            },
            new BiConsumer<U[], T>() {
                @Override
                public void accept(final U[] a, final T t) {
                    a[0] = op.apply(a[0], mapper.apply(t));
                }
            },
            new BinaryOperator<U[]>() {
                @Override
                public U[] apply(final U[] a, final U[] b) {
                    a[0] = op.apply(a[0], b[0]);
                    return a;
                }
            },
            new Function<U[], U>() {
                @Override
                public U apply(final U[] a) {
                    return a[0];
                }
            },
            CH_NOID);
Sign up to request clarification or add additional context in comments.

Comments

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.