以下代码为什么可以通过编译阶段并正确运行?
我不明白两点:
首先,mapToLong
方法接受这样的functionalInterface
@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);
}
但是类longValue
的方法Number
是public abstract long longValue();
其次,方法longValue
是抽象方法,但是可以将其作为参数传递给mapToLong
方法,为什么呢?
代码如下:
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);
}
}
答案 0 :(得分:3)
LongStream mapToLong(ToLongFunction<? super T> mapper)
由于流是Stream<Long>
,所以mapToLong
需要ToLongFunction<? super Long>
。
Number::longValue
可以是ToLongFunction<Number>
,即需要Number
并返回long
的东西。您可以将Long
传递给ToLongFunction<Number>
,因为所有Long
也是Number
。因此,ToLongFunction<Number>
也是ToLongFunction<? super Long>
。
因此很好。
答案 1 :(得分:0)
这很好,可以按照指定的方式工作。
除了安迪(Andy)在this answer中所说的话:
看看JLS 15.13. Method Reference Expressions,我们可以看到
Number::longValue
匹配第三种形式,即ReferenceType :: [TypeArguments] Identifier
这里没有任何内容表明该方法不能为abstract
。现在,此方法是ToLongFunction<Number>
,对于
mapToLong
,正如安迪(Andy)所解释的。
但是如果我们将其分配给ToLongFunction<Double>
,它甚至可以工作:
ToLongFunction<Double> f = Number::longValue;
请参见15.13.1. Compile-Time Declaration of a Method Reference
在第二个搜索中,如果P1,...,Pn不为空,并且P1是ReferenceType的子类型,则将方法引用表达式视为具有类型P2,...的参数表达式的方法调用表达式。 ..,Pn。[...]
P1
是Double
,它是Number
的子类型,所以很好。
只有一种形式无法引用abstract
方法,那就是
如果方法引用表达式的形式为super :: [TypeArguments] Identifier或TypeName,则是编译时错误。 super :: [TypeArguments]标识符,并且编译时声明是抽象的。
也就是说,您明确要求从超类型调用方法,但是该超类型只有一个可以匹配的抽象方法。