方法引用(::)有四种写法,大多数人只记得"能代替lambda"这个用法,这篇真实把四种形式各自跑一遍,再用反射看它们和lambda在运行时的类名,验证它们到底是不是"同一套东西"。
上一篇:双亲委派真实验证
静态方法引用、特定对象的实例方法引用、任意对象的实例方法引用、构造器引用——依次真实跑一遍。
MethodReferenceDemo.java Function<Integer, Integer> f1 = MethodReferenceDemo::staticDouble; // 静态方法引用 Function<Integer, Integer> f2 = instance::instanceTriple; // 特定对象的实例方法引用 Function<String, Integer> f3 = String::length; // 任意对象的实例方法引用 Function<String, Person> f4 = Person::new; // 构造器引用
静态方法引用 f1.apply(5) = 10
特定对象方法引用 f2.apply(5) = 15
任意对象方法引用 f3.apply("hello") = 5
构造器引用 f4.apply("Alice") = Person(Alice)
真实结果验证了四种形式各自的语义:静态方法引用 直接对应一个静态方法调用;特定对象的实例方法引用 真实绑定到了一个具体实例(instance),调用时不需要再传入调用者;任意对象的实例方法引用(String::length)真实把函数式接口的第一个参数当作方法的调用者,f3.apply("hello") 真实等价于 "hello".length();构造器引用 真实等价于一个"专门用来 new 对象"的函数。
用反射查看方法引用和手写lambda生成的对象,运行时类名到底长什么样。
MethodReferenceDemo.java Function<Integer, Integer> f1 = MethodReferenceDemo::staticDouble; Function<Integer, Integer> lambda1 = x -> staticDouble(x); System.out.println(f1.getClass().getName()); System.out.println(lambda1.getClass().getName());
方法引用与等价lambda结果是否一致: true f1 的运行时类型: MethodReferenceDemo$$Lambda$1/0x0000007001000a10 lambda1 的运行时类型: MethodReferenceDemo$$Lambda$5/0x0000007001004000 两者运行时类名是否都含Lambda字样: true
补充验证两个常见的实际使用场景:排序用的Comparator,以及数组构造器引用。
用方法引用Comparator排序后: [Person(Alice), Person(Bob), Person(Charlie)] 数组构造器引用 int[]::new 创建长度: 5
真实结果验证了 Comparator.comparing(Person::getName) 这种"任意对象方法引用"作为排序键提取器的经典用法,以及 int[]::new 这种数组构造器引用(常用于 Stream.toArray(size -> new T[size]) 场景的简写形式)——两者都真实给出了正确结果。
每一行都是真实运行这段代码得到的结果。