上一节介绍了Lambda表达式的基础用法,Java为我们提供了很多函数式接口。详见我转载的一篇博客:深入学习Java8中的函数式接口
接着,是配合Lambda表达式使用的新语法:方法引用和构造器引用。
引用类方法 | 类名::类方法 | 函数式接口中被实现的方法的全部参数传给该类方法作为参数 | (a,b...)->类名.类方法(a,b...) |
引用特定对象的实例方法 | 特定对象::实例方法 | 函数式接口中被实现方法的全部参数传给该方法作为参数 | (a,b...)->特定对象.实例方法(a,b...) |
引用某类对象的实例方法 | 类名::实例方法 | 函数式接口中被实现的方法的第一个参数作为调用者,后面的参数全部传给该方法作为参数 | (a,b...)->a.实例方法(b...) |
引用构造器 | 类名::new | 函数式接口中被实现方法的全部参数传给该构造器作为参数 | (a,b...)->new 类名(a,b...) |
若 Lambda 体中的功能,已经有方法提供了实现,可以使用方法引用,(可以将方法引用理解为 Lambda 表达式的另外一种表现形式),方法引用分为三种:
对象 ::实例方法
类名 ::类方法
类名 ::实例方法
方法引用的前提:所引用的方法的参数列表与返回值类型,需要与函数式接口中抽象方法的参数列表和返回值类型保持一致!
先看对象 ::实例方法:
//首先,这是Consumer接口,accept方法接收一个T,无返回值
@FunctionalInterface
public interface Consumer {
void accept(T var1);
}
@Test
public void test1(){
//1 为了更好理解,将ps声明出来
PrintStream ps = System.out;
//2 传入的参数用来打印到控制台
Consumer con = (str) -> ps.println(str);
//3 调用方法
con.accept("Hello World!");
//4 上面是一个简单的例子,下面我们换个写法
System.out.println("--------------------------------");
//5 ps为PrintStream对象,println为实例方法
Consumer con2 = ps::println;
//6 调用方法
con2.accept("Hello Java8!");
//7 所以也可换成这种写法
//Consumer con3 = System.out::println;
}
运行结果:
再看类名 ::类方法:
//这是BiFunction接口,apply方法,接收两个参数,返回一个参数
@FunctionalInterface
public interface BiFunction {
R apply(T var1, U var2);
}
@Test
public void test2(){
//1 使用Math类的静态方法max(double,double)做最大值选择
BiFunction fun = (x, y) -> Math.max(x, y);
//2 调用方法,返回double
Double apply1 = fun.apply(1.5, 22.2);
System.out.println(apply1);
System.out.println("--------------------------------------------------");
//3 Max.max(x,y)写成 Math::max
BiFunction fun2 = Math::max;
Double apply = fun2.apply(1.2, 1.5);
System.out.println(apply);
}
运行结果:
最后是类名 ::实例方法:
如果是调用实例方法,这种写法不能显式的看到调用者,那应该怎么写呢?
//BiPredicate接口,接收两个参数,返回boolean
@FunctionalInterface
public interface BiPredicate {
boolean test(T var1, U var2);
}
@Test
public void test3(){
//1 x调用equals方法,y作为参数
BiPredicate bp = (x, y) -> x.equals(y);
System.out.println(bp.test("abcde", "abcde"));
System.out.println("-----------------------------------------");
//2 可以改写为 String::equals
BiPredicate bp2 = String::equals;
System.out.println(bp2.test("abc", "abc"));
}
运行结果:
这种String : : equals写法不是很好理解,equals方法的调用者在哪呢?
这就是关键,BiPredicate接口的test方法有两个参数,第一个参数就是调用者,第二个参数就是被调用方法的参数
如果,BiPredicate的test方法只有一个参数,那会是怎么样?
我很快写了个例子:
//这是我自己的一个函数式接口,只有一个参数
@FunctionalInterface
public interface TestMethodRefInterface {
boolean test(T a);
}
//我想实现判断开头是不是以http://开头字符串的方法(此代码是不能通过的)
TestMethodRefInterface test = String::startsWith;
System.out.println(test.test("http://"));
这时候就明了了,在泛型对应的情况下,如果被调用方法参数个数和函数式接口方法参数一致,会被认为是类名 ::静态方法,因为没有指定调用者嘛,所以startWith不是静态方法就不会通过。
构造器引用可以理解为另一种形式的类名 ::类方法名。
//Supplier接口的get方法,没有参数,返回T
@FunctionalInterface
public interface Supplier {
T get();
}
@Test
public void test4(){
//1 新建一个对象
Supplier sup = () -> new Employee();
System.out.println(sup.get());
System.out.println("------------------------------------");
Supplier sup2 = Employee::new;
System.out.println(sup2.get());
}
运行结果:
Lambda表达式简化了代码,下节介绍Lambda表达式与匿名内部类的区别!