定义

MyBatis Plus 推荐的这篇文章

lambada 表达式实质上是一个匿名方法,但该方法并非独立执行,而是实现由函数式接口定义的唯一抽象方法

使用 lambda 表达式时,会创建实现了函数式接口的一个匿名类实例

可以将 lambda 表达式视为一个对象,可以将其作为参数传递。

函数式接口

函数式接口是仅含一个抽象方法的接口,但可以指定 Object 定义的任何公有方法。

@FunctionalInterface
public interface IFuntionSum<T extends Number> {
    T sum(List<T> numbers);      // 抽象方法
}
@FunctionalInterface
public interface IFunctionMulti<T extends Number> {
    void multi(List<T> numbers); // 抽象方法
    
    boolean equals(Object obj);  // Object中的方法
}

不是函数式接口

@FunctionalInterface
public interface IFunctionMulti<T extends Number> extends IFuntionSum<T> {
    void multi(List<T> numbers);
    
    @Override
    boolean equals(Object obj);
}
// IFunctionMulti 接口继承了 IFuntionSum 接口,此时 IFunctionMulti 包含了2个抽象方法
  • 可以用 @FunctionalInterface 标识函数式接口,非强制要求,但有助于编译器及时检查接口是否满足函数式接口定义
  • 在 Java 8 之前,接口的所有方法都是抽象方法,在 Java 8 中新增了接口的默认方法

Lambda 表达式

类型

单独表达式

list.forEach(item -> System.out.println(item));

代码块

list.forEach(item -> {
  int numA = item.getNumA();
  int numB = item.getNumB();
      System.out.println(numA + numB);
});

每个 lambda 表达式背后必定有一个函数式接口,该表达式实现的是这个函数式接口内部的唯一抽象方法。

原理

例如 ArrayList 中的 foreach方法

@Override
public void forEach(Consumer<? super E> action) {
  // 太长了,不看了~
}

其中 Consumer 是一个函数式接口:

@FunctionalInterface
public interface Consumer<T> {
    void accept(T t);    // lambda 表达式 item -> System.out.println(item) 实现了该方法

    default Consumer<T> andThen(Consumer<? super T> after) {
        Objects.requireNonNull(after);
        return (T t) -> { accept(t); after.accept(t); };
    }
}

在执行 lambda 表达式时,会自动创建一个实现了目标函数式接口的类实例,该类实例是一个匿名内部类。

实现

由于 lambda 表达式在执行时会生成目标函数式接口的类实例,因此我们可以做以下操作:

// 有以下函数式接口
@FunctionalInterface
public interface IFuntionSum<T extends Number> {
    T sum(List<T> numbers);
}

// 将一个lambda表达式赋值给函数式接口引用(类型须兼容)
IFuntionSum<Long> function = list -> {
  Long sum = 0L;
  for (Long item : list) {
      sum += item;
  }
  return sum;
};
function.sum(Arrays.asList(1L, 2L));   // 执行结果为3L

在开发过程中,我们可以将 lambda 表达式等同于一个对象使用,对其声明、引用、传递。

@FunctionalInterface
interface test {
    public void run(String string);
}


public class blog {
    public static void main(String[] args) {
        test t1 = new test() {
            @Override
            public void run(String string) {
                System.out.println("匿名内部类创建实现类对象" + string);
            }
        };
        test t2 = (string) -> {
            System.out.println("lambda创建实现类对象" + string);
        };
    }
}

规约

lambda 表达式的参数可以通过上下文推断,如果需要显示声明一个参数的类型,则必须为所有的参数声明类型。

@FunctionalInterface
public interface IFunctionMod {
  boolean (int n, int d);
}
  
IFunctionMod function = (n, d) -> (n % d) == 0          // 合理,n 和 d 的类型通过上下文推断
IFunctionMod function = (int n, int d) -> (n % d) == 0   // 合理,指定 n 和 d 的类型
IFunctionMod function = (int n, d) -> (n % d) == 0       // 不合理,须显示声明所有参数类型

lambda 表达式中抛出的异常需要与目标函数式接口的抽象方法抛出的异常类型兼容:
以下是合理的:

@FunctionalInterface
public interface IFunctionMod {
  boolean (int n, int d) throw Exception; 
}

IFunctionMod function = (n, d) -> {
    if (d == 0) {
      // IOException是EXception 的子类,通过类型转换,IOException 可转换为 Exception
      throw new IOException("test"); 
    } 
    return n % d == 0;
};

如果反一下,就不行了:

@FunctionalInterface
public interface IFunctionMod {
  boolean (int n, int d) throw IOException; 
}

IFunctionMod function = (n, d) -> {
    if (d == 0) {
      // 父类不能通过自动类型转换转为子类,lambda 表达式抛出的异常类型与抽象方法抛出的异常类型不兼容
      throw new Exception("test");
    } 
    return n % d == 0;
};

lambda 表达式中参数类型需要与目标函数式接口中抽象方法的参数类型兼容。

方法引用

静态方法引用

interface test {
    public void run(String string);
}
 
public class blog {
    public static void main(String[] args) {
 
        printString("静态方法引用",System.out::println);    
//   实质代码: printString("静态方法引用", (string) -> System.out.println(string));   
}
 
    public static void printString(String str, test t) {
        t.run(str);
    }
}

对象方法引用

@FunctionalInterface
interface test {
    public String run(String string);
}
 
class Person {
    public String goWalking(String string) {
        return string.concat(" 引用方法");
    }
}
 
public class blog {
    public static void main(String[] args) {
  //实质代码:      test t1 = (string) -> new Person().goWalking(string);
  //实质代码:     System.out.println(t1.run("对象"));
        test t2 = new Person()::goWalking;
        System.out.println(t2.run("对象"));        //输出:对象 引用方法
    }    
}

构造方法引用

interface test {
    public Person1 run(String string);
}
 
class Person {
    String name;
 
    public Person(String name) {
        this.name = name;
    }
 
    @Override
    public String toString() {
        return "Person{" +
                "name='" + name + '\'' +
                '}';
    }
}
 
public class blog {
    public static void main(String[] args) {
    //实质代码:    test t1 = (string) -> new Person(string);
    //实质代码:    Person p = t1.run("张三");
        test t2 = Person::new;
        Person p2 = t2.run("李四");
        System.out.println(p2);            //输出:Person{name='李四'}
    }
}
最后修改:2022 年 07 月 01 日 11 : 13 AM
赏杯咖啡喝 谢谢您~