Lambda表达式入门
Lambda表达式是Java8的重要更新。Lambda表达式支持将代码块作为方法参数,Lambda表达式允许使用简洁的代码来创建只有一个抽象方法的接口(这种接口被称为函数式接口)的实例。
如下代码使用匿名内部类,动态的传入一段代码作为具体的处理行为,程序创建了一个匿名内部类实例来封装处理行为。
1、Lambda表达式介绍
//代码一,使用匿名内部类
public interface Command{
//接口定义的process方法用于封装“处理行为”
void process(int[] target);
}
class ProcessArry{
public void process(int[] target,Command cmd){
cmd.process(target);
}
}
class testCommand{
public static void main(String args[]){
ProcessArry p=new ProcessArry();
int[] target={1,2,3,4,5,6,7,8,9,0};
p.process(target, new Command() {
@Override
public void process(int[] target) {
int sum=0;
for (int tem: target){
sum+=tem;
}
System.out.println("对数组求和的结果是:"+sum);
}
}
);
}
}
//代码二,使用Lambda表示
public interface Command{
//接口定义的process方法用于封装“处理行为”
void process(int[] target);
}
class ProcessArry{
public void process(int[] target,Command cmd){
cmd.process(target);
}
}
class testCommand{
public static void main(String args[]){
ProcessArry p=new ProcessArry();
int[] target={1,2,3,4,5,6,7,8,9,0};
p.process(target, (int[] arry)->{
int sum=0;
for (int tem: target){
sum+=tem;
}
System.out.println("对数组求和的结果是:"+sum);
}
);
}
}
/*
使用Lambda表达式,来实现匿名内部类的效果时,
不需要new xx(),不需要指出重写方法的名字,也不需要给出重写方法的返回值,
只需要给出重写方法的括号以及括号里的形参表即可
(形参)->{
}
*/
//代码三 Lambda表达式的简写形式
package Lambda;
interface Eatable{
void taste();
}
interface Flyable{
void fly(String weather);
}
interface Addable{
int add(int a,int b);
}
public class LambdaQs {
void eat(Eatable e){
System.out.println(e);
e.taste();
}
void drive(Flyable f){
System.out.println("我正在驾驶"+f);
f.fly("晴天");
}
void test(Addable add){
System.out.println("5与3的和为:"+add.add(5,3));
}
public static void main(String args[]){
LambdaQs l=new LambdaQs();
//Lambda的代码块只有一条语句,可以省略花括号
l.eat(()->System.out.println("苹果的味道不错"));
//Lambda的形参列表只有一个形参,可以省略圆括号
l.drive( weather->{
System.out.println("今天的天气是:"+weather);
System.out.println("直升机飞的很平稳");
}
);
//Lambda代码块中只有一条语句,即使表达式需要返回值,也可以
//省略关键字return
l.test((a,b)->a+b);
}
}
2、Lambda表达式与函数式接口
Lambda表达式的类型,也被称为“目标类型(target type)”,Lambda表达式的目标类型必须是“函数式接口”
函数式接口代表只包含一个抽象方法的接口。函数式接口可以包含多个默认方法、类方法,但只能声明一个抽象方法。
Lambda表达是实现的是匿名方法,因此它只能实现特定函数式接口中的唯一方法。这意味着Lambda表达式有如下两个限制 ·Lambda表达式的目标类型必须是明确的函数式接口
·Lambda表达式只能为函数式接口创建对象。Lambda表达式只能实现一个方法,因此它只能为只有一个抽象方法的接口创建对象。
为了保证Lambda表达式的目标类型是一个明确的函数式接口,可以有如下的三种常见方式、
- 将Lambda表达式赋值给函数式接口类型。
- 将Lambda表达式作为函数式接口类型的参数传给某个方法。
- 使用函数式接口对Lambda表达式进行强制类型转换。(唯一的要求是,Lambda表达式实现的匿名类方法与目标类型(函数式接口)中惟一的抽象方法有相同的形参)
这里写图片描述
3、方法引用和构造器引用
如果Lambda表达式的代码块只有一条代码,程序就可以省略Lambda表达式中代码块的花括号,不仅如此,如果Lambda表达式的代码块只有一条代码,还可以在代码块中使用方法引用和构造器引用。
方法引用和构造器引用可以让Lambda表达式的代码块更加简洁。方法引用和构造器引用都需要使用两个英文冒号。
Lambda表达式支持的如下表所示的几种引用方式
①引用类方法
//定义了一个函数式接口,它包含了一个convert()抽象方法
@FunctionalInterface
interface Converter{
//该方法负责将String参数转换为Integer
Integer convert();
}
//下面代码使用Lambda表达式创建Converter对象
Converter c1=from->Integer.valueOf(from);
//由于代码块就一句,因此程序就省略了该代码块的花括号;
//而且由于表达式所实现的convert()方法需要返回值,
//因此Lambda表达式将会把这条代码作为返回值
接下来程序就可以调用c1对象的convert()方法将字符串转换为整数了,如下
Integer val=c1.convert("99");
System.out.println(val);//输出99
当代码调用c1对象的convert()时,由于c1这个对象是由Lambda表达式创建的,convert()方法执行体就是Lambda表达式代码块部分。
上面Lambda表达式的代码块只有一行调用类方法的代码,因此可以使用如下方法引用进行替换,如下
//方法引用代替类表达式
//函数式接口中被实现方法的全部参数传给该类方法作为参数
Converter c2=Integer::valueOf;
对于上面的类方法引用,也就是调用Integer类的valueOf()类方法来实现Converer函数式接口中唯一的抽象方法,当调用Converter接口中的唯一抽象方法时,调用参数会传给Integer类的valueOf()类方法。
===============================
②引用特定对象的实例方法
先用Lambda表达式创建一个Converter对象
Converter c3=from-> "hello".indexOf(from);
接下来程序调用 c3对象的convert()方法将字符串转换为整数了,
Integer value =c3.convert("itit")
System.out.println(value);//输出4
/*上面代码调用c3对象的convert()方法时,由于c3对象是Lambda
表达式创建的,所以convert()方法执行体就是Lambda表达式的代码块*/
//该Lambda表达式代码块只有一行调用"hello"的indexOf()实例方法的代码
//因此,可以使用如下方法引用替换
Converter c3=from-> "hello".indexOf(from);
//-----------------------------------------
Converter c3="hello".indexOf();
/*上面实例的引用,就是调用hello这个字符串对象的
indexOf()实例方法来实现Converter函数式接口中唯一的抽象方法,
当调用Converter接口中惟一的抽象方法时,
调用参数会传给"hello"对象的indexOf()实例方法。
*/
③、引用某类对象的实例方法
//定义函数式接口
@FunctionalInterface
interface Mytest{
String test(String a,int b,int c);
}
//使用Lambda表达式创建MyTest对象
MyTest my=(a,b,c)->a.subString(b,c);
//程序可以调用mt对象的test()方法
String str =mt.test("hello Cruel World!!!",3,8);
System.out.println(str);
因为上面的Lambda表达式的代码块只有一行a.subString(b,c),因此可以使用如下方法引用进行替换
//方法引用代替Lambda表达式:引用某类对象的实例方法
//函数式接口中被实现方法的第一个参数作为调用者
//后面的参数全部传给该方法作为参数
MyTest mt=String ::substring;
/*对于上面的实例方法引用,也就是调用某个String对象的substring()
实例方法来实现MyTest函数式接口中唯一的抽象方法,
当调用MyTest接口中唯一的抽象方法时,第一个参数作为substring()方法的
调用者,剩下的调用参数会作为substring()实例方法的调用参数、*/
4、引用构造器
@FunctionalInterface
interface YourTest{
JFrame win(String tittle);
}
//下面的代码使用Lambda表达式创建YourTest对象
YourTest yt=(String a)->new JFrame(a);
//使用yt对象调用win()方法
JFrame jf=yt.win("我的窗口");
System.out.println(jf);
使用构造器引用代替Lambda表达式
函数式接口中被实现方法的全部参数传给该构造器作为参数
YourTest yt=JFrame::new;
/*对于构造器引用,也就是调用某个JFrame类的构造器来实现
YourTest函数式接口唯一的抽象方法,当调用YourTest函数式接口中唯一
的抽象方法,调用参数会传给JFrame构造器。
*/
==========================================================
4、Lambda表达式和匿名内部类的联系和区别
联系
Lambda表达式与匿名内部类一样,都可以直接访问“effectively final”的局部变量,以及外部类的成员变量(包括实例变量和类变量)
Lambda表达式创建的对象与匿名内部类生成的对象一样,都可以直接调用从接口中继承的默认方法
区别如下:
匿名内部类可以为任意接口创建实例----不管接口包含多少个抽象方法,只要匿名内部类实现了所有的抽象方法即可;但Lambda表达式只能为函数式接口创建实例。
匿名内部类可以为抽象类甚至普通类创建实例;但Lambda表达式只能为函数式接口创建实例。
匿名内部类实现的抽象方法的方法体允许调用借口中定义的默认方法;
但Lambda表达式的代码块不允许调用接口中定义的默认方法。