少女祈祷中...

1.Lambda表达式基础

  • 类似于匿名方法,一个没有名字的方法

  • 参数,箭头,表达式语句

  • 可以忽略写参数类型

  • 坚决不声明返回值类型

  • 没有public/protected/private/static/final等修饰符

  • 单句表达式,将直接返回值,不用大括号

  • 带return语句, 算多句,必须用大括号

  • 无参数,仅保留括号,箭头,表达式

1
2
3
4
5
6
7
8
9
10
11
new Thread(
()->
{
int sum=0;
for(int i=1;i<=100;i++)
{
sum = sum + i;
}
System.out.println("总和:" + sum);
}
).start();
  • 一个参数,可省略括号,箭头,表达式
1
2
3
4
5
Adder  c1 = 
x ->
{
x++;
};
  • 如果有返回值,返回值类型会在上下文推断出来的,无需声明
  • 只在某几个分支有返回值,这样是不合法的
1
2
3
4
5
6
Adder  c2 =
x ->
{
if(x>0)
return x+1;//wrong
};

2.函数式接口

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

系统自带的函数式接口

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

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

1
2
3
4
5
6
7
8
Predicate<String> oddLength = s -> 
s.length()%2 == 0 ? false:true;

for(String p : planets) {
if(oddLength.test(p)) {
System.out.println(p);
}
}

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

1
2
3
4
5
6
Consumer<String> printer = s -> 
System.out.println("Planet :" + s);

for(String p : planets) {
printer.accept(p);
}

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

1
2
3
4
5
6
Supplier<String> planetFactory = () -> 
planets[(int) floor(random() * 8)];

for (int i = 0; i < 5; i++) {
System.out.println(planetFactory.get());
}

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

1
2
3
4
5
6
7
8
9
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)\
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
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)
1
2
3
4
5
6
7
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)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
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
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
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
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
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

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

Class[]::new

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