-2

why the following code can pass compile phase and run correctly?

There are two points that I can't understand: First,mapToLong method accept a functionalInterface like this

@FunctionalInterface
public interface ToLongFunction<T> {

    /**
     * Applies this function to the given argument.
     *
     * @param value the function argument
     * @return the function result
     */
    long applyAsLong(T value);
}

but the method longValue of class Number is public abstract long longValue(); Second, the method longValue is a abstract method, but it can be passed to the mapToLong method as a argument, why is that?

Here is the code:

package com.pay.screen;


import java.util.ArrayList;
import java.util.List;

public class MainTest {

    public static void main(String[] args)  {
        List<Long> list = new ArrayList<>();
        list.add(1L);
        list.add(2L);
        long sum = list.stream().mapToLong(Number::longValue).sum();
        System.out.println(sum);

    }
}
BackSlash
  • 21,927
  • 22
  • 96
  • 136
DanteLord
  • 35
  • 3
  • 5
    Why shouldn't it? – Hulk Jan 17 '19 at 09:52
  • `Number::longValue` can be written as `l -> l.longValue()`. it is not an actual implementation you're passing but rather a "description" of what should be done with every element in the stream. Try passing `Long::longValue` you see that it works as well – Lino Jan 17 '19 at 09:55
  • Maybe also have a look at this other question: https://stackoverflow.com/questions/32283833/please-explain-java-8-method-reference-to-instance-method-using-class-name – Lino Jan 17 '19 at 09:56

2 Answers2

4

From the Javadoc of mapToLong:

LongStream mapToLong(ToLongFunction<? super T> mapper)

Since the stream is a Stream<Long>, mapToLong requires a ToLongFunction<? super Long>.

Number::longValue can be a ToLongFunction<Number>, i.e. something which takes a Number and returns a long. You can pass a Long to a ToLongFunction<Number>, because all Longs are also Numbers. Hence ToLongFunction<Number> is also a ToLongFunction<? super Long>.

Hence it's fine.

Andy Turner
  • 137,514
  • 11
  • 162
  • 243
0

This is perfectly fine and working as specified.

In addition to what Andy already said in this answer:

Looking at the JLS 15.13. Method Reference Expressions we can see that

Number::longValue matches the 3rd form, i.e. ReferenceType :: [TypeArguments] Identifier

Nothing here says that the method cannot be abstract. Now, this method is a ToLongFunction<Number>, which is completely sufficient for mapToLong, as Andy already explained.

But it even works if we assign it to a ToLongFunction<Double>:

ToLongFunction<Double> f = Number::longValue;

See 15.13.1. Compile-Time Declaration of a Method Reference

In the second search, if P1, ..., Pn is not empty and P1 is a subtype of ReferenceType, then the method reference expression is treated as if it were a method invocation expression with argument expressions of types P2, ..., Pn.[...]

P1 is Double, which is a subtype of Number, so this is fine.

There is only one form where referring to an abstract method is not ok, and that is

It is a compile-time error if the method reference expression has the form super :: [TypeArguments] Identifier or TypeName . super :: [TypeArguments] Identifier, and the compile-time declaration is abstract.

that is, you explicity requested to invoke a method from a supertype, but that supertype only has an abstract method that would match.

Hulk
  • 6,399
  • 1
  • 30
  • 52