MOOC 个人学习笔记

# 1.Lambda 表达式基础

  • 类似于匿名方法,一个没有名字的方法
  • 参数,箭头,表达式语句
  • 可以忽略写参数类型
  • 坚决不声明返回值类型
  • 没有 public/protected/private/static/final 等修饰符
  • 单句表达式,将直接返回值,不用大括号
  • 带 return 语句, 算多句,必须用大括号
  • 无参数,仅保留括号,箭头,表达式
new Thread(() -> {
    int sum = 0;
    for (int i = 1; i <= 100; i++) {
        sum = sum + i;
    }
    System.out.println("总和:" + sum);
}).start();
  • 一个参数,可省略括号,箭头,表达式
Adder c1 = x -> {
    x++;
};
  • 如果有返回值,返回值类型会在上下文推断出来的,无需声明
  • 只在某几个分支有返回值,这样是不合法的
Adder c2 = x -> {
    if (x > 0)
        return x + 1;// wrong
};

# 2. 函数式接口

  • 是一个接口,符合 Java 接口的定义
  • 只包含一个抽象方法的接口
  • 可以包括其他的 default 方法、static 方法、private 方法
  • 由于只有一个未实现的方法,所以 Lambda 表达式可以自动填上这个尚未实现的方法
  • 采用 Lambda 表达式,可以自动创建出一个 () 嵌套类的对象 (没有实际的嵌套类 class 文件产生),然后使用,比真正嵌套类更加轻量,更加简洁高效

# 系统自带的函数式接口

  • 涵盖大部分常用的功能,可以重复使用
  • 位于 java.util.function 包中

# Predicate<T>: 接收一个参数,返回一个布尔值

Predicate<String> oddLength = s -> 
    s.length()%2 == 0 ? false:true;
for(String p : planets) {
    if(oddLength.test(p)) {
        System.out.println(p);
    }
}

# Consumer<T>:接收一个参数,做操作,无返回

Consumer<String> printer = s -> 
    System.out.println("Planet :" + s);
for(String p : planets) {
    printer.accept(p);
}

# Supplier<T>:无输入参数,返回一个数据

Supplier<String> planetFactory = () -> 
    planets[(int) floor(random() * 8)];
for (int i = 0; i < 5; i++) {
    System.out.println(planetFactory.get());
}

# Function<T>:接收一个参数,返回一个参数

Function<String, String> upper = s -> {
    // 可以做更复杂的操作
    return s.toUpperCase();
};
for (String p : planets) {
    System.out.println(upper.apply(p));
}

# 3. 方法引用

  • Lambda 表达式支持传递现有的类库函数
  • Class::staticMethod,如 Math::abs 方法
  • Class::instanceMethod,如 String::compareToIgnoreCase 方法
  • object::instanceMethod,如 System.out::println 方法
    • 支持 this::instanceMethod 调用
    • 支持 super::instanceMethod 调用
  • Class::new,调用某类构造函数,支持单个对象构建
  • Class []::new,调用某类构造函数,支持数组对象构建

# Class::staticMethod

  • 等价于提供方法参数的 Lambda 表达式
  • Math::abs 等价于 x -> Math.abs (x)\
public class ClassStaticMethodTest {
    public static double worker(NumFunction nf, double num)
    {
        return nf.calculate(num);
    }
    public static void main(String[] args) {
        double a = -5.3;
        double b = worker(Math::abs, a);
        System.out.println(b);
        
        double c = worker(Math::floor, a);
        System.out.println(c);
    }    
}
interface NumFunction {
    double calculate(double num);
}

# Class::instanceMethod

  • 第一个参数将变成方法的执行体
  • String::compareToIgnoreCase 等价于 (x,y)->x.compareToIgnoreCase (y)
String[] planets = new String[] { 
        "Mercury", "Venus", "Earth", "Mars", 
        "Jupiter", "Saturn", "Uranus",
        "Neptune" };    
Arrays.sort(planets, String::compareToIgnoreCase);
System.out.println(Arrays.toString(planets));

# object::instanceMethod

  • 等价于提供方法参数的 Lambda 表达式
  • System.out::println 等价于 x->System.out.println (x)
public class ObjectInstanceMethodTest {
    public static void worker(PrintFunction pf, String s) {
        pf.exec(s);
    }
    
    public static void main(String[] args) {
        String a = "abc";
        worker(System.out::println, a);
    }
}
interface PrintFunction {
    public void exec(String s);
}
  • 支持 this::instanceMethod
public class ThisInstanceMethodTest {
    public static void main(String[] args) {
        ThisInstanceMethodTest obj = new ThisInstanceMethodTest();
        obj.test();
    }    
    
    public void test()    {
        String[] planets = new String[] { 
                "Mercury", "Venus", "Earth", "Mars", 
                "Jupiter", "Saturn", "Uranus", "Neptune" };    
                
        Arrays.sort(planets, this::lengthCompare);
        System.out.println(Arrays.toString(planets));    
    }
    
    public int lengthCompare(String first, String second){
        return first.length() - second.length();
    }
}
  • 支持 super::instanceMethod
public class SuperInstanceMethodTest extends Father{
    public static void main(String[] args) {
        SuperInstanceMethodTest obj = new SuperInstanceMethodTest();
        obj.test();
    }    
    
    public void test()    {
        String[] planets = new String[] { 
                "Mercury", "Venus", "Earth", "Mars", 
                "Jupiter", "Saturn", "Uranus", "Neptune" };    
                
        Arrays.sort(planets, super::lengthCompare);
        System.out.println(Arrays.toString(planets));    
    }
}
class Father {
    public int lengthCompare(String first, String second){
        return first.length() - second.length();
    }
}

# Class::new

Supplier<Person> s = Person::new;
Person p = s.get();

# Class[]::new

IntFunction<int[]> intArray = int[]::new;        
int[] nums = intArray.apply(10); 
Function<Integer, Person[]> personArray = Person[]::new;
Person[] persons = personArray.apply(5);

# 4.Lambda 表达式应用

  • Lambda 表达式没有存储目标类型 (target type) 的信息
  • 重载调用,依据重载的规则和类型参数推理

# 变量遮蔽

  • Lambda 表达式和匿名内部类 / 局部内部类一样,可以捕获变量 (capture variables),即访问外部嵌套块的变量
    • 但是变量要求是 final 或者是 effectively final 的
  • Lambda 表达式没有变量遮蔽问题,因为它的内容和嵌套块有着相同的作用域
    • 在 Lambda 表达式中,不可以声明与 (外部嵌套块) 局部变量同名的参数或者局部变量
  • 表达式中的 this,就是创建这个表达式的方法的 this 参数

# 优先级

  • Lambda 表达式优先级比嵌套类要高
    • 短小精干,本身可以自描述的
    • 无法创建命名实例,无法获取自身的引用 (this)
  • 方法引用比自定义 Lambda 表达式的优先级高
    • 系统自带的方法引用更简洁高效
    • 对于复杂的 Lambda 表达式,采用方法引用比内嵌 Lambda 表达式更清晰,更容易维护
  • 坚持使用标准的函数式接口
    • 更容易学习,提高互操作性