在使用Lambda表达式的时候,我们实际上传递进去的代码就是一种解决方案:拿什么参数做什么操作。那么考虑

一种情况:如果我们在Lambda中所指定的操作方案,已经有地方存在相同方案

2.1 冗余的Lambda场景

  1. @FunctionalInterface
  2. interface Printable {
  3. void print(String s);
  4. }
  5. public class Repent_Lambda {
  6. private static void printString(Printable data) {
  7. data.print("Hello, World!");
  8. }
  9. public static void main(String[] args) {
  10. printString((str)->System.out.println(str));
  11. printString(System.out::println);// 使用方法引用直接调用
  12. }
  13. }

其中 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,那么根据“可推导就是可省略”的原则,无需指定参数类型,也无需指定的重载形式——它们都将被自动推导。而如果使用方法引用,也是同样可以根据上下文进行推导。

  1. @FunctionalInterface
  2. interface Printable {
  3. //void print(String s);
  4. void print(int s);
  5. }
  6. public class Repent_Lambda {
  7. private static void printString(Printable data) {
  8. data.print(2);
  9. }
  10. public static void main(String[] args) {
  11. printString((int str)->System.out.println(str)); // 参数类型 可以推断
  12. printString(System.out::println); // 方法引用 连 方法重载也推导了
  13. }
  14. }

总结:

  • Lambda 可以推导参数类型 直接处理业务逻辑代码
  • :: 方法引用 可以推导函数重载

2.3 通过对象名引用成员方法

如果一个类中已经存在了一个成员方法:可以吧这个方法实现通过引用直接传递过去

解释:

  1. public class Obj_Lambda {
  2. private static void printString(Printable lambda) {
  3. lambda.print("Hello");
  4. }
  5. public static void main(String[] args) {
  6. MethodRefObject obj = new MethodRefObject();
  7. printString((str)->System.out.println(str));
  8. printString(obj::printUpperCase);// 具体代码
  9. /**
  10. printString 需要一个Printable函数接口,这里传统方法就是将自己实现的Lambda传递过去,
  11. 如果引用 MethodRefObject 类的对象实例,则可以通过对象名引用成员方法 将实现传递过去,
  12. */
  13. }
  14. }
  15. class MethodRefObject {
  16. public void printUpperCase(String str) {
  17. System.out.println(str.toUpperCase());
  18. }
  19. }

2.4 通过类名称引用静态方法

调用: java.lang.Math 类中已经存在了静态方法 abs

  1. public class Static_Lambda {
  2. private static void method(int num, Calcable lambda) {
  3. System.out.println(lambda.calc(num));
  4. }
  5. public static void main(String[] args) {
  6. method(-10, num->Math.abs(num));
  7. method(-10, Math::abs); // 静态调用方式
  8. }
  9. }
  10. @FunctionalInterface
  11. interface Calcable {
  12. int calc(int num);
  13. }

下面两种方法等效

  • Lambda表达式:() -> Math.abs(n)
  • 方法引用:Math : : abs

2.5 通过super引用成员方法

  1. @FunctionalInterface
  2. interface Greetable {
  3. void useToolsEat();
  4. }
  5. class Animal {
  6. void eat() {
  7. System.out.println("Animal eat----");
  8. }
  9. }
  10. class FemalAnimal extends Animal {
  11. protected int age;
  12. public FemalAnimal(int age) {
  13. this.age = age;
  14. }
  15. void eat() {
  16. System.out.println(" FemalAnimal eat----");
  17. }
  18. }
  19. class Cat extends FemalAnimal {
  20. private String name;
  21. public Cat(int age, String name) {
  22. super(age);
  23. this.name = name;
  24. }
  25. @Override
  26. void eat() {
  27. System.out.println(super.age + " " + this.name + " " + "吃饭");
  28. }
  29. public static void method(Greetable g) {
  30. g.useToolsEat();
  31. }
  32. void show(){
  33. method(super::eat); // 调用直接父类的方法
  34. method(()->new FemalAnimal(0).eat()); //
  35. }
  36. }

下面两种方法等效

  • Lambda表达式:() -> super.buyHouse()
  • 方法引用:super : : buyHouse

2.6 通过this引用成员方法

  1. public class This_Lambda {
  2. @FunctionalInterface
  3. interface Richable{
  4. void buy();
  5. }
  6. private void buyHouse() {
  7. System.out.println("买套房子");
  8. }
  9. private static void marry(Richable lambda) {
  10. lambda.buy();
  11. }
  12. public void beHappy() {
  13. marry(()->buyHouse());
  14. marry(this::buyHouse); // 直接引用本类存在的方法
  15. }
  16. }

下面两种方法等效

  • Lambda表达式:() -> this.buyHouse()
  • 方法引用:this : : buyHouse

2.7 类的构造器引用

  1. public class Construtor_Lambda {
  2. @FunctionalInterface
  3. interface PersonBuilder{
  4. P buildPerson(String name);
  5. }
  6. public static void printName(String name, PersonBuilder builder) {
  7. System.out.println(builder.buildPerson(name).getName());
  8. }
  9. public static void main(String[] args) {
  10. printName("Tom", (name)->new P(name)); // 传递的参数name 将会被自动推导
  11. printName("Tom", P::new);
  12. }
  13. }

下面两种方法等效

  • Lambda表达式:name -> new Person(name)
  • 方法引用:Person : : new

2.8 数组的构造器引用

数组 List Set Map 等其他类似的数据结构都可以进行推导

  1. public class Array_Lambda {
  2. @FunctionalInterface
  3. interface GenerateIntArr{
  4. int[] BuilderArr(int length);
  5. }
  6. @FunctionalInterface
  7. interface GenerateIntList{
  8. ArrayList<Integer> BuilderArr(int length);
  9. }
  10. private static void method(int length,GenerateIntArr gArr) {
  11. System.out.println(Arrays.toString(gArr.BuilderArr(length)));
  12. }
  13. private static void method1(int length,GenerateIntList gArr) {
  14. System.out.println(gArr.BuilderArr(length));
  15. }
  16. public static void main(String[] args) {
  17. method(10, (num)->new int[num]);
  18. method(10,int[]::new);
  19. method1(10, ArrayList<Integer>::new);
  20. }
  21. }

下面两种方法等效

  • Lambda表达式:length -> new int[length]
  • 方法引用:int[] : : new