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 表达式更清晰,更容易维护
- 坚持使用标准的函数式接口
- 更容易学习,提高互操作性