# 简介

Lambda 允许把函数作为一个方法的参数(函数作为参数传递进方法中)。使用 Lambda 表达式可以使代码变的更加简洁紧凑。

以下是 lambda 表达式的重要特征:

  • ** 可选类型声明:** 不需要声明参数类型,编译器可以统一识别参数值。
  • ** 可选的参数圆括号:** 一个参数无需定义圆括号,但多个参数需要定义圆括号。
  • ** 可选的大括号:** 如果主体包含了一个语句,就不需要使用大括号。
  • ** 可选的返回关键字:** 如果主体只有一个表达式返回值则编译器会自动返回值,大括号需要指定表达式返回了一个数值。

具体代码为

// 1. 不需要参数,返回值为 5  
() -> 5  
  
// 2. 接收一个参数 (数字类型), 返回其 2 倍的值  
x -> 2 * x  
  
// 3. 接受 2 个参数 (数字), 并返回他们的差值  
(x, y) -> x – y  
  
// 4. 接收 2 个 int 型整数,返回他们的和  
(int x, int y) -> x + y  
  
// 5. 接受一个 string 对象,并在控制台打印,不返回任何值 (看起来像是返回 void)  
(String s) -> System.out.print(s)

面向对象编程是对数据进行抽象,而函数式编程是对行为进行抽象。现实世界中,数据和行为并存,程序也是如此,因此这两种编程方式我们都得学。

# 主要内容

# 变量作用域

Lambda 使用的变量,必须被 final 修饰或者含有隐性 final 。同样的,变量在 Lambda 表达式中也不能被修改。

public static void main(String[] args) {
    int n = 10;
    // n = 9;
    Runnable r = ()->{ System.out.println(n); };
    // n = 9;
    //n 既不能在表达式中被修改,也不能在 Lambda 表达式之外被修改。
    // 总之,n 是不可修改的
}

# 方法引用

方法引用的可以简化 Lambda 表达式,但是在方法引用中不能有任何参数修改

构造引用

Student s = Student::new

对象 **:😗* 实例方法

// public void forEach(Consumer<? super E> action) 
//forEach 需要重写 Consumer 的 accept 方法
list.forEach(n -> System.out.println(n)); 
list.forEach(System.out::println);  // 使用方法引用
list.forEach((String s) -> System.out.println("*" + s + "*"));

类名 **:😗* 静态方法

Stream<Double> stream = Stream.generate(Math::random);

类名 **:😗* 实例方法

/*  这里如果使用第一句话,编译器会有提示: Can be replaced with Comparator.naturalOrder,这句话告诉我们
  String 已经重写了 compareTo () 方法,在这里写是多此一举,这里为什么这么写,是因为为了体现下面
  这句编译器的提示: Lambda can be replaced with method reference。好了,下面的这句就是改写成方法引用之后: 
*/
//  TreeSet<String> set = new TreeSet<>((s1,s2) -> s1.compareTo(s2));
TreeSet<String> set = new TreeSet<>(String::compareTo);

# 注解 @FunctionInterface

@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
public @interface FunctionalInterface{}

该注解只能修饰接口。被 FunctionalInterface 注解修饰的接口,只能有一个抽象方法,被实现的方法必须被 defalut 修饰。

如果声明的方法和 java.lang.Object 中的某个方法一样,它可以不当做未实现的方法,不违背这个原则::一个被它注解的接口只能有一个抽象方法。

比如 java public interface Comparator<T> { int compare(T o1, T o2); boolean equals(Object obj); }

编译器会自动把满足 function interface 要求的接口自动识别为 function interface ,所以你才不需要对上面示例中的 ITest 接口增加 @FunctionInterface 注解。

# 内置四大函数接口

  • 消费型接口: Consumer< T> void accept(T t)
Consumer<Person> greeter = (p) -> System.out.println("Hello, " + p.firstName);
greeter.accept(new Person("Luke", "Skywalker"));
  • 供给型接口: Supplier < T> T get()
Supplier<Person> personSupplier = Person::new;
personSupplier.get();   // new Person
  • 断定型接口: Predicate<T> boolean test(T t) 。返回值类型是固定的 boolean
redicate<String> predicate = (s) -> s.length() > 0;
predicate.test("foo");              // true
Predicate<String> isEmpty = String::isEmpty;
  • 函数型接口: Function<T,R> R apply(T t) ,有参有返回值的抽象方法。
Function<String, Integer> toInteger = Integer::valueOf;

# 参考

菜鸟教程:https://www.runoob.com/java/java8-lambda-expressions.html

Java 全栈知识体系:https://pdai.tech/md/java/java8/java8-stream.html