Lambda表达式
Lambda表达式支持将代码块作为方法参数,Lambda表达式允许使用更简洁的代码来创建只有一个抽象方法的接口(函数式接口)的实例。
Lambda表达式代替了匿名内部类的繁琐语法,不需要new Xxx(){ }这种繁琐的代码,不需要指出重写的方法名字,也不需要给出重写的方法的返回值类型,只要给出重写的方法括号以及括号里的形参列表即可。
Lambda表达式构成:形参列表;箭头 -> ;代码块。
看一个匿名内部类的代码
int[] target = {3,-4,6,4};
pa.process(target, new Command()
{
//匿名内部类实例来封装处理行为
public void process(int[] target)
{
int sum = 0;
for(int tmp : target)
{
sum += tmp;
}
System.out.println("数组元素的总和是:" + sum);
}
});
使用Lambda表达式简化上述匿名内部类代码
int[] array = {3,-4,6,4};
pa.process(array,(int[] target)->{
int sum = 0;
for (int tmp: target)
{
sum += tmp;
}
System.out.println("数组元素的总和是:" + sum);
});
介绍几种Lambda表达式的简化方法
形参列表:形参列表允许省略形参类型,如果形参中只有一个参数,可以省略圆括号。
代码块:若代码块只包含一条语句,可省略代码块的花括号,只有一条return语句,可省略return关键字。
lq.eat(()->System.out.println("苹果的味道不错!")); //不带形参的匿名方法,一行代码,省略花括号
lq.drive(weather->
{
System.out.println("今天天气是:" + weather);
System.out.println("直升机飞行平稳");
}); //一个形参,省略括号,省略类型
lq.test((a,b)->a+ b); //省略类型,一行返回值代码,省略return,省略花括号
Lambda表达式与函数式接口
Lambda表达式的目标类型必须是“函数式接口function interface”,函数式接口代表只包含一个抽象方法的接口。
在只需要实现一个抽象方法来采用匿名内部类语法来创建函数式接口的实例时,可使用Lambda表达式。可使用Lambda表达式进行赋值,因可被当成对象。
Lambda表达式的目标类型必须是明确的函数式接口,只能为函数式接口创建对象,只能实现一个方法。
为使Lambda表达式的目标类型是一个明确的函数式接口,可将Lambda表达式赋值给函数式接口类型的变量;可将Lambda表达式作为函数式接口类型的参数传给某个方法;可使用函数式接口对Lambda表达式进行强制类型转换。
java.uril.function包下的典型的函数式接口:
XxxFuncyion(对指定数据进行转换处理,转换方法的处理逻辑由Lambda实现);
XXXConsumer(同上相似,对参数处理,但不会返回处理结果);
XXXSupplier(不输入参数,按某种逻辑算法(由Lambda实现)返回数据);
XxxxPredicate(用于判断参数(由Lambda实现)是否满足特定条件,进行筛选数据)。
方法引用与构造器引用
如果Lambda表达式的代码块只有一条代码,可以在代码块中使用方法引用和构造器引用。
引用类方法
类名::类方法;
函数式接口中被实现方法的全部参数传给该类方法作为参数;
(a,b,。。。)-》类名。类方法(a,b,。。。)。
Lambda表达式的代码块只有一行调用类方法的代码,则可用引用类方法代替。
Converter converter1 = from-> Integer.valueOf(from); //Lambda表达式创建Converter对象
Converter Converter1 = Integer::valueOf; //方法引用代替Lambda表达式
调用Integer类的valueOf()类方法来实现Converter函数式接口中唯一的抽象方法,当调用Converter接口中的唯一的抽象方法时,调用参数将会传给Integer类的valueOf()类方法。
引用特定对象的实例方法
特定对象::实例方法;
函数式接口中被实现方法的全部参数传给该方法作为参数;
(a,b,。。。)-》特定对象。实例方法(a,b,。。。)。
Lambda表达式的代码块只有一行调用“fkit。org”对象的indexOf()实例方法的代码,则可用引用特定对象的实例方法代替。
Converter converter2 = from -> "fkit.org".indexOf(from); //LaCmbda表达式创建Converter对象
Converter converter2 = "fkit.org"::indexOf; //方法引用代替Lambda表达式,全部参数传给该方法作为参数
调用“fkit.org”对象的indexOf()实例方法来实现Converter函数式接口中唯一的抽象方法,当调用Converter接口中的唯一的抽象方法时,调用参数将会传给“fkit.org"对象的indexOf()实例方法。
引用某类对象的实例方法
类名::实例方法;
函数式接口中被实现方法的第一个参数 作为调用者,后面的参数全部传给该方法作为参数;
(a,b,。。。)-》a。实例方法(b,。。。)。
Lambda表达式的代码块只有一行a。substring(b,c),则可用引用某类对象的实例方法代替
MyTest mt = (a,b,c)->a.subString(b,c); //根据String、int、int三个参数生成一个String返回值
MyTest mt = String::subString; //函数式接口中被实现方法的第一个参数作为调用者,后面的参数全部传给该方法作为参数
当调用MyTest接口中的唯一的抽象方法时,第一个调用参数将做为substring()方法的调用者,剩下的调用参数会作为substring()实例方法的调用参数。
引用构造器
类名:new ;
函数式接口中被实现方法的全部参数传给该构造器作为参数;
(a,b,。。。)-》new。类名(a,b,。。。)。
Lambda表达式的代码块只有一行new JFrame(a),调用构造器 ,可以用引用构造器代替。
YourTest yt = (String a )-> new JFrame(a ); //Lambda表达式
YourTest yt = JFrame::new ; //函数式接口中被实现的全部参数传给该构造器作为参
调用YourTest接口中的唯一的抽象方法时,调用参数会传给JFrame构造器。
当调用YourTest接口中的抽象方法时,实际传入一个String类型的参数,这个String类型的参数会被传给JFrame构造器,即确定了是调用JFrame类的带一个String参数的构造器。
总结:Lambda表达式只有一行代码且该函数式接口中只有一个抽象方法时方可用上述方法。
4.Lambda表达式与匿名内部类的联系和区别
联系:Lambda表达式与匿名内部类一样,都可以直接访问“effectively final ” 的局部变量,以及外部类的成员变量;
Lambda表达式创建的对象与匿名内部类生成的对象一样,都可以直接调用从接口中继承的默认方法。
区别:Lambda表达式只能为函数式接口创建实例,而匿名内部类能为任意接口和抽象类以及普通类创建实例;
Lambda表达式代码块中不允许调用接口中定义的默认方法,而匿名内部类可以。
5.使用Lambda表达式调用Arrays的类方法
Arrays类的有些方法需要Comparator、XxxOperator、XXXFunction等函数式接口的实例,可以使用Lambda表达式使代码更加简洁。
Arrays.perallelsort(arr1,(o1,o2)->o1.length() - o2.length()) ; 目标类型为Comparator指定了判断字符串大小的标准。
使用Lambda表达式遍历集合
Iterable接口中新增的forEach(Consumer action )默认方法 所需参数的类型是一个函数式接口,而Iterable接口是Collection接口的父接口,因此Collection集合也可以调用该方法。
当程序调用Iterable的forEach(Consumer action)遍历集合元素时,程序会依次将集合元素传给Consumer的accept(T t)方法(该接口中唯一的抽象方法)。正因为Consumer是函数式接口,因此可以使用Lambda表达式来遍历集合元素。
import java.util.*;
public class CollectionEach { public static void main(String[] args) { // 创建一个集合
Collection books = new HashSet(); books.add("轻量级Java EE企业应用实战"); books.add("疯狂Java讲义"); books.add("疯狂Android讲义"); // 调用forEach()方法遍历集合
books.forEach(obj -> System.out.println("迭代集合元素:" + obj)); //Lambda表达式,省略圆括号,方括号 } }
使用Lambda表达式遍历Iterator
Iterator新增的forEachRemaining(Consumer action )方法 所需的Comsumer参数同样也是函数式接口。当程序调用Iterator的forEachRemaining(Consumer action )遍历集合元素时,程序会依次将集合元素传给Consumer的accept(T t)方法(该接口中唯一的抽象方法)。
import java.util.*;
public class IteratorEach { public static void main(String[] args) {
// 创建一个集合 Collection books = new HashSet(); books.add("轻量级Java EE企业应用实战"); books.add("疯狂Java讲义"); books.add("疯狂Android讲义");// 获取books集合对应的迭代器
Iterator it = books.iterator(); // 使用Lambda表达式(目标类型是Comsumer)来遍历集合元素
it.forEachRemaining(obj -> System.out.println("迭代集合元素:" + obj)); } }