简介

  • 函数式编程是把函数作为基本运算单元,函数可以作为变量,可以接收函数,还可以返回函数
    • 比如一个功能(方法)很多地方用,在要用的类都创建不现实,而单独为其创建一个类也不现实。根据面向对象思想,如果很多种不同功能的方法都单独创建一个类很臃肿。这时内部类和函数式编程就非常简洁高效
    • lambda就是快速实现一个接口的方法,然后把该方法以对象形式传入其他方法

  • 我们把只定义了单方法的接口称之为FunctionalInterface,用注解@FunctionalInterface标记。(即只有一个方法的接口。如果接口有很多方法,但是只有一个抽象方法,也算单方法接口)
    • 在java8后,注意接口中的**default**方法和**static**不算抽象方法
  • 该注解表明接口为函数式接口函数式接口才可以使用Lambda

Lambda表达式

Lambda表达式是JDK8的重要特性之一。使用简洁清晰的表达式来表达一个接口,同时也简化了对集合,数组数据的一系列操作
Lambda表达式语法如下:
([数据类型 参数名,数据类型 参数名,...]) -> {表达式主体}

  • ()内为接口抽象方法需要的参数;数据类型可以省略 ; 只有一个参数时,括号也可以省略
  • ->表示Lambda表达式箭牌,用于指定参数数据指向
  • 表达式主体为接口中抽象方法的具体实现,如果只有一条语句,可以省略**{}**
  • 只有一条return时,可以省略return

前面内部类中讲过匿名内部类可以简化接口,而Lambda表达式比匿名内部类还要简化。
**Lambda表达式可以省略**``_**参数**_``**和**``_**返回值**_``**的类型还有**``_**类名**_``**,编译器会自动推断**
缺点:接口有且只有一个抽象方法时才可以使用Lambda表达式代替匿名内部类

  1. interface Animal {
  2. void shout();
  3. }
  4. //--------------------------------------------------------
  5. public class T {
  6. public static void main(String[] args) {
  7. String name = "小花";
  8. //匿名内部类写法:
  9. animalShout(new Animal() { //匿名内部类
  10. public void shout() {
  11. System.out.println(name);
  12. }
  13. }
  14. );
  15. //lambda写法:
  16. animalShout(() -> System.out.println(name)); //Lambda表达式
  17. }
  18. public static void animalShout(Animal an) { an.shout(); }
  19. }
  1. String[] array = ...
  2. Arrays.sort(array, new Comparator<String>() {
  3. public int compare(String s1, String s2) {
  4. return s1.compareTo(s2);
  5. }
  6. });
  7. //ladmda写法:
  8. Arrays.sort(array, (s1, s2) -> {
  9. return s1.compareTo(s2);
  10. });
  11. //或者再简化:
  12. Arrays.sort(array, (s1, s2) -> s1.compareTo(s2) );

lambda中的变量规则

  • 如果lambda中使用lambda表达式外面的方法中的局部变量,则该变量必须是不可变的。即静态变量和类变量不受影响
  • lambda中的变量名不能与所在方法里的局部变量重名,域变量和静态变量不受限制
  • lambda中可使用 this、super 关键字,等同于在所在方法里方法中使用

    • 注意不能使用super访问父接口中的default方法。这是接口的限制跟lambda没关系 ```java public class AClass { private Integer num1 = 1; private static Integer num2 = 10;

    public void testA() {

    int a = 1;
    int b = 2;
    int c = 3;
    a++;
    new Thread(() -> {
        System.out.println("a=" + a); // 在 Lambda 表达式使用前有改动,编译报错
        b++; // 在 Lambda 表达式中更改,报错
        System.out.println("c=" + c); // 在 Lambda 表达式使用之后有改动,编译报错
    
        System.out.println("num1=" + this.num1++); // 对象变量,或叫类/域变量,编译通过
        AClass.num2 = AClass.num2 + 1;
        System.out.println("num2=" + AClass.num2); // 静态变量,编译通过
    }).start();
    c++;
    

    } } java public class AClass extends ParentClass { @Override public void printHello() {

    System.out.println("subClass: hello budy!");
    

    }

    @Override public void printName(String name) {

    System.out.println("subClass: name=" + name);
    

    }

    public void testA() {

    this.printHello();  // 输出:subClass: hello budy!
    super.printName("susu"); // 输出:ParentClass: name=susu
    
    new Thread(() -> {
        this.printHello();  // 输出:subClass: hello budy!
        super.printName("susu"); // 输出:ParentClass: name=susu
    }).start();
    

    } }

class ParentClass { public void printHello() { System.out.println(“ParentClass: hello budy!”); }

public void printName(String name) { System.out.println(“ParentClass: name=” + name); } }

<a name="imrCG"></a>
# 方法引用

- **方法引用即是普通的lambda表达式基础上更加精简的功能,方法引用的标志就是使用了**`**::**`
   - **方法引用还可以将已存在的方法作为参数进行传递,不必像普通的lambda一样每次写个lambda方法**
   - **即方法已存在,才能使用方法引用。方法不存在只能用lambda创建一个**
- 方法引用**,如果某个方法签名和函数式接口的抽象方法恰好一致**,就可以直接传入方法引用。
   - 因为Comparator<String>接口定义的方法是int compare(String, String),和静态方法int cmp(String, String)相比,除了方法名外,方法参数一致,返回类型相同,因此,我们说两者的方法签名一致,可以直接把方法名作为Lambda表达式传入
   - 实例方法都有一个隐藏参数`this`,下面的`compareTo(s2)`就相当于`compareTo(this,s2)`所以方法签名与`Comparator的compareTo`相同

---

- 静态方法引用`类名::静态方法名`等同于提供了方法参数的lambda
- 实例方法引用`对象::实例方法名` 等同于提供了方法参数的lambda
   - 等同于提供了方法参数的lambda比如:`System.out::println相当于System.out.println(x)`

`Math::pow相当于(x, y) -> Math.pow(x, y)`

- 构造方法引用`类名::new`
-  实例方法引用`类名::实例`
   - 这种情况第一个参数会成为执行方法的对象,比如`(x, y) -> x.compareToIgnoreCase(y)`
- 还可以使用this,super进行方法引用,同普通方法里使用一样的效果,如`this::equals相当于x -> this.equals(x)``super::实例方法`
```java
list.forEach(System.out::println);
//---------相当于
Consumer<Integer> consumer = System.out::print; // accept(T t)
list.forEach(consumer);
//引用静态方法
public class Main {
    public static void main(String[] args) {
        String[] array = new String[] { "Apple", "Orange", "Banana", "Lemon" };
        Arrays.sort(array, Main::cmp);
        System.out.println(String.join(", ", array));
    }

    static int cmp(String s1, String s2) {
        return s1.compareTo(s2);
    }
}
//引用实例方法
public class Main {
    public static void main(String[] args) {
        String[] array = new String[] { "Apple", "Orange", "Banana", "Lemon" };
        Arrays.sort(array, String::compareTo);
        System.out.println(String.join(", ", array));
    }
}

//引用静态方法,快速将List<String> 注入到List<Person>中
public class Main {
    public static void main(String[] args) {
        List<String> names = List.of("Bob", "Alice", "Tim");
        List<Person> persons = names.stream().map(Person::new).collect(Collectors.toList());
        System.out.println(persons);
    }
}
class Person {
    String name;
    public Person(String name) {
        this.name = name;
    }
    public String toString() {
        return "Person:" + this.name;
    }
}


@FunctionalInterface
public interface Function<T, R> {
    R apply(T t);
}
把泛型对应上就是方法签名Person apply(String),即传入参数String,返回类型Person。而Person类的构造方法恰好满足这个条件,

四大函数式接口

  • 下面这几个接口就是为了帮助非函数式接口的方法作为参数传递的,这些接口的核心函数式方法相当于被替换为了我们传递的任意方法

    Consumer 消费型接口

  • 有参无返回,只吃不拉。接口核心函数是void accept(T t) ```java @Test public void test1 () {
    consumo(500, (x) -> System.out.println(x)); }

public void consumo (double money, Consumer c) { c.accept(money);
}

<a name="XJlXc"></a>
## Supplier<T> 供给型接口

- `T get();`无参有返回,无中生有
```java
@Test
public void test2 () { 
    Random ran = new Random();
    List<Integer> list = supplier(10, () -> ran.nextInt(10));

    for (Integer i : list) { 
        System.out.println(i);
    } 
} 

/**
 * 随机产生 sum 个数量得集合 
 * @param sum 集合内元素个数
 * @param sup 
 */
public List<Integer> supplier(int sum, Supplier<Integer> sup){ 
    List<Integer> list = new ArrayList<Integer>(); 
    for (int i = 0; i < sum; i++) {  
        list.add(sup.get()); 
    }
    return list; 
}

Function 函数型接口

  • R apply(T t) 有参有返回,对来料进行加工然后返回

    @Test
    public void test3 () { 
      String s = strOperar(" asdf ", x -> x.substring(0, 2));
      System.out.println(s);
      String s1 = strOperar(" asdf ", x -> x.trim());
      System.out.println(s1); 
    } 
    public String strOperar(String str, Function<String, String> fun) { 
      return fun.apply(str);
    }
    

    Predicate 断言型接口

  • 有参有返回的一种特殊情况,对来料进行判断。boolean test(T t) ```java @Test public void test4 () { List l = new ArrayList<>(); l.add(102); l.add(172); l.add(13); l.add(82); l.add(109); List list = filterInt(l, x -> (x > 100)); for (Integer integer : list) {

      System.out.println(integer);
    

    } }

/**

  • 过滤集合
  • @param list
  • @param pre
  • @return */ public List filterInt(List list, Predicate pre){ List l = new ArrayList<>(); for (Integer integer : list) {
     if (pre.test(integer)) {
         l.add(integer); 
     }
    
    } return l; } ```