在Java 8中,每个函数都是可以传递的对象,包括静态函数、实例函数与构造函数,唯一需要确认的就是,要传递的函数必须与需要的函数签名一致。
首先定义函数接口如下:
@FunctionalInterface
public interface HelloService {
void sayHello(String username);
}
上面的函数接口只需要一个String参数,所以只要满足此要求的函数都可以复用,如下:
public class TestHelloService {
private String username;
/** * 定义构造函数 */
public TestHelloService(String username) {
this.name = username;
System.out.println(username + ":");
}
/** * 定义函数接口的使用方法 */
void say(HelloService hs) {
hs.sayHello(this.name);
}
/** * 静态函数 */
static void print(String username) {
System.out.println("Hello, " + username + "!");
}
void println(String username) {
// 这句话也会执行
this.name = "yiifee";
System.out.println("Hello, " + username + "!");
}
}
下面可以进行测试了。
public static void main(String[] args) {
// 声明测试对象
TestHelloService ts = new TestHelloService("yiifaa");
}
1. 箭头函数测试
// 输出yiifaa
ts.say(username -> System.out.println(username));
2. 静态函数测试
// 因为函数的签名一模一样,所以可以复用
// 输出Hello,yiifaa!
ts.say(TestHelloService::print);
3. 实例函数测试
调用实例函数的时候要非常小心,因为涉及到对象上下文的读写,可能会产生意想不到的副作用,比如本例中的输出虽然为“Hello, yiifaa!”,但实例的name属性已经更改。
// 输出为Hello, yiifaa!
// 但实例的name属性已经更改
ts.say(ts::println);
4. 构造函数测试
构造函数与普通函数一样,也可以直接调用,但在大部分的情况下,一般用于创造新的实例。
// 输出为yiifaa:
ts.say(User::new);
创造实例的接口定义示例如下:
@FunctionalInterface
public interface CreateInterface {
/** * 返回创造的对象 */
TestHelloService create(String username);
}
创造的函数签名如下:
static TestHelloService create(String username, CreateInterface ci) {
return ci.create(username);
}
现在可以传入构造函数了。
TestHelloService ths = create("yiifaa123", TestHelloService::new);
结论
一个小小的lambda表达式激活了函数的潜能,难怪如此一个功能竟然花费了这么多年的开发时间。
最后附送一段Collector的源码,全是精华。
public static <T> Collector<T, ?, List<T>> toList() {
return new CollectorImpl<>((Supplier<List<T>>) ArrayList::new, List::add,
(left, right) -> { left.addAll(right); return left; },
CH_ID);
}