实例方法引用和Lambda参数
实例方法引用和Lambda参数
我对方法引用的语法有些困惑,其中有两个参数a和b,并且引用的是a上的一个方法,该方法在b上调用。
例如,我知道
Arrays.sort(personArray, comparators::compareByName);
等同于
Arrays.sort(personArray, (o1, o2) -> comparators.compareByName(o1, o2));
因为在这种情况下,lambda参数与方法调用参数(o1, o2)匹配。
然而,对于这个lambda表达式
stream.sorted((o1, o2) -> o1.compareToIgnoreCase(o2));
我的IDE告诉我它等同于:
stream.sorted(String::compareToIgnoreCase);
我找不到一个替换这种语法a.method(b)的规则。
例如,如果lambda有三个或更多的参数,那么是合法的吗?第一个参数是否成为方法的目标,剩下的参数是否成为方法的参数?
问题的出现原因是对于lambda表达式中的方法引用,如果被引用的方法是实例方法,那么在运行时,如何确定方法的目标引用以及方法的参数。解决方法是在Java语言规范的第15.13.3节中进行了详细说明。
根据Java语言规范的15.13.3节的规定,如果方法引用的形式是"ReferenceType :: [TypeArguments] Identifier",那么在运行时,方法引用的效果类似于编译时声明的方法调用表达式。方法调用表达式的运行时评估遵循规范中的相应规则。
在方法调用表达式的运行时评估中,需要根据编译时声明的方法引用表达式来确定调用模式。如果编译时声明是一个实例方法,那么调用模式的目标引用就是调用方法的第一个形式参数;否则,没有目标引用。
另外,如果编译时声明是一个实例方法,那么方法调用表达式的参数(如果有的话)就是调用方法的第二个及以后的形式参数;否则,方法调用表达式的参数就是调用方法的形式参数。
因此,对于lambda表达式中的方法引用,如果被引用的方法是实例方法,那么第一个参数就是方法的目标引用,而剩下的参数就是方法的参数。
例如,如果lambda表达式中有三个或更多参数,那么第一个参数将成为方法的目标引用,而剩下的参数将成为方法的参数。
这样的规定使得lambda表达式中的方法引用更加灵活,能够处理各种不同参数个数的情况。
Instance Method Reference and Lambda Parameters
在Java 8中,我们可以使用方法引用来简化Lambda表达式的写法。然而,有时候在使用方法引用时,我们可能会遇到一些问题。
一个常见的问题是,当我们使用方法引用引用实例方法时,Lambda表达式的参数数量可能会有所不同。这是因为方法引用会被转换成一个Lambda表达式,而Lambda表达式需要根据上下文来确定参数的数量。
假设我们需要一个对字符串进行比较的Comparator实例的引用。我们可以这样写:
.sorted(String::compareTo)
这里,String::compareTo与下面的Lambda表达式是等价的:
(String a, String b) -> a.compareTo(b);
这是因为方法引用会被转换成一个Lambda表达式,而Lambda表达式需要两个参数。第一个参数是流中的任意对象,第二个参数是Comparator的compare方法所需要的参数。
另一个例子是使用方法引用获取员工的薪水:
.map(Employee::getSalary)
在这个例子中,map方法需要一个Function作为参数。Function接口定义了一个apply方法,该方法有一个参数。在这个例子中,Lambda表达式只需要一个参数,即Employee实例。
总之,在编译时的上下文中,方法引用会被转换成一个Lambda表达式,该Lambda表达式的参数数量取决于目标方法所需的参数数量。第一个参数是实例对象本身,后面的参数是目标方法所需的参数,参数的顺序与目标方法的参数顺序相同。