lambda 表达式,写法格式是比较固定的。即使我们以最简洁的写法,也是在结构范围内的。但是有一种写法,直接突破了常规的格式,那就是方法引用。相信使用 intellij idea
时,代码提示自动帮你用过这种写法:::
,提示是 Replace lambda with method reference
。
总览 方法引用:简单的说,就是 lambda 表达式的内容,刚好是其他类的某个方法。此时,我们就可以直接引用那个类的那个方法。俗称,处男,哦不,方法引用。
《让子弹飞》一定要申遗
方法引用 引用语法 常规写法 静态方法引用 类名::静态方法 (参数) -> 类名.静态方法(参数) 构造方法引用 类名::构造方法 (参数) -> new 类名(参数) 实例方法引用 实例化对象::普通方法 (参数) -> 实例化对象.普通方法(参数) 对象方法引用 类名::普通方法 (实例化对象,参数…) -> 类名.普通方法(参数…)
首先,我们定义一个 MethodRef
类,包含有静态方法、普通方法、构造器方法。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 public class MethodRef { private String methodName; public static boolean isNull (String str) { return str == null ; } public MethodRef (String methodName) { this .methodName = methodName; } public String getMethodName () { return methodName; } public boolean nameEquals (String name) { return Objects.equals(methodName, name); } @Override public String toString () { return "I am " + methodName; } }
静态方法引用 格式 :类名::静态方法
1 2 3 4 5 6 7 8 9 10 11 12 13 14 public void staticRef () { Function<String, Boolean> isNull = s -> MethodRef.isNull(s); System.out.println(isNull.apply("abc" )); isNull = MethodRef::isNull; System.out.println(isNull.apply(null )); Function<Boolean, String> boolean2String = b -> String.valueOf(b); System.out.println(boolean2String.apply(true )); boolean2String = String::valueOf; System.out.println(boolean2String.apply(false )); }
s -> MethodRef.isNull(s)
实际上就是调用 MethodRef
的静态方法,且参数一致,可以使用静态方法引用的格式来写。
构造方法引用 格式:类名::new
1 2 3 4 5 6 7 public void constructorRef () { Function<String, MethodRef> f = s -> new MethodRef(s); System.out.println(f.apply("commRef" ).toString()); f = MethodRef::new ; System.out.println(f.apply("constructorRef" ).toString()); }
函数式接口的的抽象方法,是通过 MethodRef
的构造方法返回一个实例化对象,且参数一致,可以使用构造方法引用的格式来写。
实例方法引用 格式:实例化对象::普通方法
1 2 3 4 5 6 7 8 9 10 public void instanceRef () { Arrays.asList("I" , "am" , "instanceRef" ).forEach(System.out::println); MethodRef ref = new MethodRef("instanceRef" ); Supplier<String> supplier = () -> ref.getMethodName(); System.out.println(supplier.get()); supplier = ref::getMethodName; System.out.println(supplier.get()); }
常用的 System.out::println
就是这种方式。普通方法是要有实例化对象才可以调用的,当函数式接口的参数也与实例方法一致时,可以使用实例方法引用的格式来写。
对象方法引用 格式:类名::普通方法
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 public void objRef () { MethodRef objRef = new MethodRef("objRef" ); Function<MethodRef, String> f = methodRef -> methodRef.getMethodName(); System.out.println(f.apply(objRef)); f = MethodRef::getMethodName; System.out.println(f.apply(objRef)); BiFunction<MethodRef, String, Boolean> bf = (methodRef, s) -> methodRef.nameEquals(s); System.out.println(bf.apply(objRef, "randomRef" )); bf = MethodRef::nameEquals; System.out.println(bf.apply(objRef, "randomRef" )); List<String> list = Arrays.asList("objRef2" , "I" , "am" ); list.sort((o1, o2) -> o1.compareTo(o2)); System.out.println(list); list.sort(String::compareTo); System.out.println(list); }
我们知道,通过类名是无法直接调用普通方法的,如果调用必须要先有实例化对象。而对象方法引用的关键点就在于,函数式接口的第一个参数,一定要是实例化对象,可以没有第二第三个参数。当函数式接口的后几个参数与实例化对象的普通方式的参数一致时,就可以使用对象方法引用的格式来写。
总结 方法引用相较 lambda 表达式而言,更简洁易懂一些。比如,MethodRef::new
,一看就是用来 new 对象的。
静态方法引用和构造方法引用会易懂一些,直接通过类名引用就可以了。
易让人混淆的是实例方法引用和对象方法引用。记住一点,对于普通方法的引用,如果函数式接口的第一个参数是实例化对象时,才考虑对象方法引用。如果函数式接口首参数不是实例化对象,就不可以使用对象方法引用。
注:本文配套代码可在 github
查看:stream-and-lambda