Java8方法引用特性?

import java.util.Arrays; import java.util.List; import java.util.function.Supplier; /** * Created by carlos-lee on 17-1-11. */ public class Car { public static Car create(final Supplier<Car> supplier){ return supplier.get(); } public static void collide(final Car car){ System.out.println("Collided "+car.toString()); } public void follow(final Car another){ System.out.println("Following …
关注者
12
被浏览
1056

3 个回答

说个简单的理解方法吧。

请先看看我这篇答案 zhihu.com/question/5373 中的“亮点一”。

在Clojure里,调用Java实例上的方法的形式是

(.follow car other) 对应 java的 car.follow(other)
(.repair car) 对应java的 car.repair()

也就是说, Java里 对象.方法() 这种调用方式其实可以看成一个与具体对象无关的函数,和一个在函数体中作为this的隐含参数。Java等语言只不过是为了突出主体对象,把这个参数写在了方法前面。

好了,现在看你的代码,follow和repair都是对象方法,因此它们都至少需要一个参数作为this。collide是静态类方法,不需要额外的this参数。而forEach总是会硬塞进来一个(也只有一个)参数。

那么对于Car::collide,它自己需要一个参数,forEach塞进来一个,刚好对上。

对于Car::repair,它自己需要一个隐含的this参数,forEach塞进来一个,刚好对上。

对于follow, 你用car::follow形式传函数,也就是这种形式自带了作为this的car参数。还缺一个方法上定义的参数another,而forEach塞进来一个,又刚好对上了。

用repair那里如果用Car::follow,如果用Clojure的调用形式,需要凑成(.follow this another)才能正常调用。而现在只有forEach塞进来一个参数,还缺一个。

而follow那里如果用car::repair ,只需要凑成 (.repair this)就能调用,而你已经提供了car这个实例,forEach又塞进来一个,究竟用那个?(当然,语言规范规定了这种形式一定是用car当this。但多塞进来一个forEach的参数说明逻辑有问题,所以编译器报错。)
谢邀。这个问题以前思考过,把笔记搬过来基本就是回答了。

其实你示例代码可以更简单一点。
为了对比更明显,我稍微改了下变量名。
final Car c = Car.create(Car::new);
final List<Car> cs = Arrays.asList(c);
cs.forEach(c::follow);
cs.forEach(Car::repair);

首先,你要明白,forEach()方法接受的是一个Consumer<? super Car>类型的变量。
然后,对于一个实例方法,可以有c::methodCar::method两种引用形式,并且,这两种引用形式的作用是不一样的。

请看下表:
类型上静态方法引用			ClassName::methodName
类型上实例方法引用			ClassName::methodName
实例上实例方法引用			instanceReference::methodName
超类上实例方法引用			super::methodName
类构造器引用			ClassName::new
数组构造器引用			TypeName[]::new

那么第二种和第三种有什么区别呢?请看下面的例子。

Function<Person, String> fs = Person::getName;
String name1 = fs.apply(new Person("Tony"));	// "Tony"

Supplier<String> ss = new Person("Jenny")::getName;
String name2 = ss.get();			// "Jenny"
这样就很清楚了,通过类名获取实例方法引用时并转换成函数式对象后,调用方法时需要提供一个调用方法的实例——该职责本来由隐式参数提供。

现在可以回到题主的问题了,为什么下面这句能行?
cs.forEach(c::follow);
因为follow()方法接受一个Car类型参数,不返回值,与Consumer<Car>相匹配。

为什么下面这句能行?
cs.forEach(Car::repair);
因为repair()方法不接受参数,也不返回值。但是通过Car::repair方式获取的引用,还需要提供一个Car类型参数作为“实例”,所以也与Consumer<Car>相匹配。

为什么这里不能引用follow()方法?
cs.forEach(Car::follow);
因为follow()接受一个Car类型参数,不返回值,Car::follow还需要提供一个Car类型参数,因此匹配的是——貌似并没有匹配到java.util.function中的哪个函数式接口。

这里为什么不能引用repair()方法?
cs.forEach(c::repair);
因为repair()方法不接受参数,也不返回值,匹配的是,额,Runnable。。。