在使用Lambda表达式的时候,我们实际上传递进去的代码就是一种解决方案:拿什么参数做什么操作。那么考虑
一种情况:如果我们在Lambda中所指定的操作方案,已经有地方存在相同方案
2.1 冗余的Lambda场景
@FunctionalInterfaceinterface Printable {void print(String s);}public class Repent_Lambda {private static void printString(Printable data) {data.print("Hello, World!");}public static void main(String[] args) {printString((str)->System.out.println(str));printString(System.out::println);// 使用方法引用直接调用}}
其中 printString 方法只管调用 Printable 接口的 print 方法,而并不管 print 方法的具体实现逻辑会将字符串
打印到什么地方去。而 main 方法通过Lambda表达式指定了函数式接口 Printable 的具体操作方案为:拿到String(类型可推导,所以可省略)数据后,在控制台中输出它
2.2 方法引用符
双冒号 :: 为引用运算符,而它所在的表达式被称为方法引用。如果Lambda要表达的函数方案已经存在于某个方法的实现中,那么则可以通过双冒号来引用该方法作为Lambda的替代者。
语义分析
例如上例中, System.out 对象中有一个重载的 println(String) 方法恰好就是我们所需要的。那么对于printString 方法的函数式接口参数,对比下面两种写法,完全等效:
- Lambda表达式写法:
s -> System.out.println(s); - 方法引用写法:
System.out::println
第一种语义是指:拿到参数之后经Lambda之手,继而传递给 System.out.println 方法去处理。
第二种等效写法的语义是指:直接让 System.out 中的 println 方法来取代Lambda, 根据已有参数进行推导 损略了传递参数 以及多余的代码()->。两种写法的执行效果完全一样,而第二种方法引用的写法复用了已有方案,更加简洁。
注:Lambda 中 传递的参数 一定是方法引用中 的那个方法可以接收的类型,否则会抛出异常
推导与省略
如果使用Lambda,那么根据“可推导就是可省略”的原则,无需指定参数类型,也无需指定的重载形式——它们都将被自动推导。而如果使用方法引用,也是同样可以根据上下文进行推导。
@FunctionalInterfaceinterface Printable {//void print(String s);void print(int s);}public class Repent_Lambda {private static void printString(Printable data) {data.print(2);}public static void main(String[] args) {printString((int str)->System.out.println(str)); // 参数类型 可以推断printString(System.out::println); // 方法引用 连 方法重载也推导了}}
总结:
- Lambda 可以推导参数类型 直接处理业务逻辑代码
::方法引用 可以推导函数重载
2.3 通过对象名引用成员方法
如果一个类中已经存在了一个成员方法:可以吧这个方法实现通过引用直接传递过去
解释:
public class Obj_Lambda {private static void printString(Printable lambda) {lambda.print("Hello");}public static void main(String[] args) {MethodRefObject obj = new MethodRefObject();printString((str)->System.out.println(str));printString(obj::printUpperCase);// 具体代码/**printString 需要一个Printable函数接口,这里传统方法就是将自己实现的Lambda传递过去,如果引用 MethodRefObject 类的对象实例,则可以通过对象名引用成员方法 将实现传递过去,*/}}class MethodRefObject {public void printUpperCase(String str) {System.out.println(str.toUpperCase());}}
2.4 通过类名称引用静态方法
调用: java.lang.Math 类中已经存在了静态方法 abs
public class Static_Lambda {private static void method(int num, Calcable lambda) {System.out.println(lambda.calc(num));}public static void main(String[] args) {method(-10, num->Math.abs(num));method(-10, Math::abs); // 静态调用方式}}@FunctionalInterfaceinterface Calcable {int calc(int num);}
下面两种方法等效
- Lambda表达式:
() -> Math.abs(n) - 方法引用:
Math : : abs
2.5 通过super引用成员方法
@FunctionalInterfaceinterface Greetable {void useToolsEat();}class Animal {void eat() {System.out.println("Animal eat----");}}class FemalAnimal extends Animal {protected int age;public FemalAnimal(int age) {this.age = age;}void eat() {System.out.println(" FemalAnimal eat----");}}class Cat extends FemalAnimal {private String name;public Cat(int age, String name) {super(age);this.name = name;}@Overridevoid eat() {System.out.println(super.age + " " + this.name + " " + "吃饭");}public static void method(Greetable g) {g.useToolsEat();}void show(){method(super::eat); // 调用直接父类的方法method(()->new FemalAnimal(0).eat()); //}}
下面两种方法等效
- Lambda表达式:
() -> super.buyHouse() - 方法引用:
super : : buyHouse
2.6 通过this引用成员方法
public class This_Lambda {@FunctionalInterfaceinterface Richable{void buy();}private void buyHouse() {System.out.println("买套房子");}private static void marry(Richable lambda) {lambda.buy();}public void beHappy() {marry(()->buyHouse());marry(this::buyHouse); // 直接引用本类存在的方法}}
下面两种方法等效
- Lambda表达式:
() -> this.buyHouse() - 方法引用:
this : : buyHouse
2.7 类的构造器引用
public class Construtor_Lambda {@FunctionalInterfaceinterface PersonBuilder{P buildPerson(String name);}public static void printName(String name, PersonBuilder builder) {System.out.println(builder.buildPerson(name).getName());}public static void main(String[] args) {printName("Tom", (name)->new P(name)); // 传递的参数name 将会被自动推导printName("Tom", P::new);}}
下面两种方法等效
- Lambda表达式:
name -> new Person(name) - 方法引用:
Person : : new
2.8 数组的构造器引用
数组 List Set Map 等其他类似的数据结构都可以进行推导
public class Array_Lambda {@FunctionalInterfaceinterface GenerateIntArr{int[] BuilderArr(int length);}@FunctionalInterfaceinterface GenerateIntList{ArrayList<Integer> BuilderArr(int length);}private static void method(int length,GenerateIntArr gArr) {System.out.println(Arrays.toString(gArr.BuilderArr(length)));}private static void method1(int length,GenerateIntList gArr) {System.out.println(gArr.BuilderArr(length));}public static void main(String[] args) {method(10, (num)->new int[num]);method(10,int[]::new);method1(10, ArrayList<Integer>::new);}}
下面两种方法等效
- Lambda表达式:
length -> new int[length] - 方法引用:
int[] : : new
