1.什么是lambda表达式?
首先看一段比较熟悉的代码片断,目的是为按钮注册一个事件监听器,当按钮被点击时输出一个提示信息"button clicked",java
Button button = new Button(); button.addActionListener(new ActionListener() { @Override public void actionPerformed(ActionEvent e) { System.out.println("button clicked"); } });
这段自己没有什么问题,可是代码量相对有点多并且语义表达上也不够清新。那么什么样的表达才是清晰的呢?Jdk8给我带来了一种全新的思路,可让咱们更加优雅的实现上述功能,咱们看下述Lambda代码。express
Button button = new Button(); button.addActionListener(e-> System.out.println("button clicked"));
2.lambda 表达式3种表现形式
无参数表达式: () -> System.out.println(“hello world!”) 单个参数表达式 : (String sayHello) -> System.out.println(sayHello) 多个参数表达式: (int x, int y) -> x + y
须要注意的是以上三种基本形式能够根据“是否有显示声明参数类型”、“是否有返回值”、“是否省略()”等又会衍生出若干变化。数组
3.lambda表达式值引用
lambda表达式操做的是对象的引用值而不是对象自己,怎么理解呢?匿名内部类若是引用所在方法中变量那么该变量必须声明为final类型,也就是对象只能对赋值一次。相似的lambda表达式引用所在方法中的变量也必须是final类型,表现形式上能够显示声明final 也能够不显示声明,可是该变量必须是一个既成事实上的final变量。ide
public static void sayHello(String name) { if (Objects.isNull(name)) { name = "kity"; } ActionListener act = event -> System.out.println("hello " + name); .... }
上述代码变异会提示 Variable used in lambda expression should be final or effectively final。函数
4.lambda表达式方法引用
之前咱们关注更多的是对象引用,jdk8以后新增了方法引用,须要使用关键字 “::”,这是jdk8咱们须要重点关注的。一下咱们详细列举6种方法引用的详细格式。this
- 静态方法引用
格式 类名::静态方法名spa
注意事项:
被引用方法参数列表和函数式接口中方法的参数一致
接口的抽象方法没有返回值,引用的方法能够有返回值也能够没有
接口的抽象方法有返回值,引用方法必须有同类型的返回值code
interface Transfer { int run(String string); } public static void main(String[] args) { printStr("1", Integer::valueOf); } public static void printStr(String str, Transfer t) { int num = t.run(str); System.out.println(num); }
- 对象方法引用
格式 对象::方法名orm
@FunctionalInterface interface Action { String run(Integer num); } public static void main(String[] args) { Action t2 = new Person()::walking; System.out.println(t2.run(5)); } class Person { public String walking(Integer num) { return "go ".concat(String.valueOf(num)).concat(" step ..."); } }
- 构造方法引用
格式 类名::new对象
interface Action { Person init(String string); } class Person { String name; public Person(String name) { this.name = name; } @Override public String toString() { return "Person{" + "name='" + name + '\'' + '}'; } public static void main(String[] args) { Action t2 = Person::new; Person p2 = t2.init("李四"); System.out.println(p2); }
- 数组构造方法引用
格式 数据类型[]::new
interface Action { String[] run(int len); } public static void main(String[] args) { Action action = String[]::new; String[] arr = action.run(5); System.out.println(arr.length); }
- 特定类型的方法引用
格式 类名::非静态方法
ArrayList<String> list = new ArrayList<>(); Collections.addAll(list, "a", "C", "B", "d"); Collections.sort(list, String::compareToIgnoreCase); System.out.println(list);
- 类中方法调用父类或本类方法引用
格式 this::方法名
super::方法名
interface Action { void init(); } class Father { public void buy() { System.out.println("买东西"); } } class Son extends Father { public void buy() { System.out.println("买糖"); } public void run() { Action action1 = this::buy; action1.init(); Action action2 = super::buy; action2.init(); } } public static void main(String[] args) { Son s = new Son(); s.run(); }
5.lambda函数接口
函数式接口是 Java8 引入的一个新特性,是一种特殊的接口:SAM类型的接口(Single Abstract Method),但本质上仍是接口。相比较于其余接口,函数式接口有且只能有一个抽象方法。只要接口中出现多个抽象方法,那么就不能称之为函数式接口,运行的时候就会报错。为此 Java8 提供了一个新的注解@FunctionalInterface,若是接口被这个注解标注,就说明该接口是函数式接口,若是有多于一个的抽象方法,在编译的时候就会报错。可是这个注解不是必需的,只要接口符合函数式接口的定义,那么这个接口就是函数式接。
Java中主要的函数接口
接口 | 参数 | 返回类型 | 描述 |
Predicate<T> | T | boolean | 断言型接口 |
Consumer<T> | T | void | 消费型接口 |
Function<T,R> | T | R | 功能型接口 |
Supplier<T> | None | T | 供给型接口 |
UnaryOperator<T> | T | T | 一元运算 |
BinaryOperator<T> | (T,T) | T | 二元运算 |
6.lambda函数类型推断
Lamda表达式是函数式接口的一个实例,然而咱们并无从Lambda表达式中看到有关函数式接口的任何信息,于是有必要弄清楚Lambda的类型是什么?它实现了哪一个函数式接口?其实Lambda表达式的类型是从上下文推断出来的,这里的上下文包括以下3种: 赋值上下文 方法调用上下文(参数与返回值) 类型转换上下文 经过这3种上下文就能够推断出Lambda表达式的目标类型(与之对应的函数式接口)。