赞
踩
什么是Lambda表达式?
Lambda是Java8的重要更新,Lambda表达式支持将代码块作为方法参数,Lambda表达式允许使用更简洁的代码来创建只有一个抽象方法的接口的实例。
案例:对数组进行操作
定义操作数组的命令的接口
package org.westos.demo8;
public interface Command {
//接口里定义的process方法用于封装"处理行为"
void process(int[] target);
}
定义数组操作,传入实现接口的行为和要操作的数组
package org.westos.demo8;
public class ProcessArray {
public void process(int[] target,Command cmd) {
cmd.process(target);
}
}
先使用匿名内部类
package org.westos.demo8; public class CommandTest { public static void main(String[] args) { ProcessArray pa=new ProcessArray(); int[] target={3,-4,6,4}; //处理数组,具体处理方式取决于匿名内部类 pa.process(target, new Command() { @Override public void process(int[] target) { int sum=0; for (int tmp:target){ sum+=tmp; } System.out.println("数组的元素和"+sum); } }); } }
使用Lambda表达式
package org.westos.demo8; public class CommandTest2 { public static void main(String[] args) { ProcessArray pa=new ProcessArray(); 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与创建匿名内部类时需要实现的process(int[] target)方法完全相同,只是不需要new Xxx{}这种烦琐的代码,不需要指出重写方法的方法名字,也不需要给出重写方法的返回值类型——只要给出重写的方法括号以及括号里的形参列表即可。
Lambda表达式其实就相当于一个匿名方法。主要作用就是代替匿名内部类的烦琐语法。
由三部分组成:
简写写法:
interface Eatable { void taste(); } interface Addable { int add(int a,int b); } interface Flyable { void fly(String weather); } package org.westos.LambdaDemo; public class LambdaQs { //调用该方法需要Eatable对象 public void eat(Eatable e){ System.out.println(e); e.taste(); } public void drive(Flyable f){ System.out.println("我正在驾驶:"+f); f.fly("【碧空如洗的晴日】"); } public void test(Addable add){ System.out.println("5和3的和为:"+add.add(5,3)); } public static void main(String[] args) { LambdaQs lq=new LambdaQs(); //使用匿名内部类 lq.eat(new Eatable() { @Override public void taste() { System.out.println("苹果味道不错"); } }); //不省略花括号的Lambda表达式 lq.eat(()->{ System.out.println("苹果味道不错"); }); //使用Lambda表达式,表达式的代码块只有一句,可以省略花括号 lq.eat(()-> System.out.println("苹果味道不错")); //Lambda表达式的形参只有一个形参,可以省略圆括号 lq.drive(weather -> { System.out.println("今天的天气是"+weather); System.out.println("直升机飞行平稳"); }); //代码块中只有一句,可以省略花括号,如果这时候有return返回也可以省略 lq.test((a,b)->a+b); } }
从上面的代码中,如果一个方法需要一个类作为参数时,传入Lambda表达式将会被当成任意类型对象。
Lambda表达式的类型被称为“目标类型”,Lambda表达式的目标类型必须是函数式接口。
函数式接口代表只包含一个抽象方法接口。函数式类型接口可以包含多个默认方法、类方法,但只能声明一个抽象方法。
//Runnable是Java本身提供的一个函数式接口
//其中只包含一个无参的抽象方法,因此下面的Lambda创建了一个Runnable对象
Runnable r=()->{
for (int i=0;i<100;i++){
System.out.println();
}
};
Object ob=()->{
for (int i=0;i<100;i++){
System.out.println();
}
};
在使用Lambda表达式会报下面的错误。因为Object不是函数式接口,所以不能用Lambda表达式来创建对象。
Error:(40, 19) java: 不兼容的类型: java.lang.Object 不是函数接口
为了保证Lambda表达式的目标类型是一个明确的函数式接口,可以有以下三种常见的方式:
比如将上面代码改为:
Object ob=(Runnable)()->{
for (int i=0;i<100;i++){
System.out.println();
}
};
@FunctionalInterface注释,通常放在接口定义前面,检查该接口必须是函数式接口,否则编译器会报错。
同样的Lambda表达式的目标类型完全可能是变化的,唯一要求是,Lambda表达式实现的匿名方法与目标类型中唯一的抽象方法有相同的形参列表。
@FunctionalInterface
interface FKtest {
void run();
}
因为FKtest现在也是函数式接口类型,所以前面的类型转换也可以用FKtest来转换。
Java8中常见的函数式接口
如果Lambda表达式的代码块只有一条代码,还可以在代码块中使用方法引用和构造器引用。
Lambda表达式支持的方法引用和构造器引用
种类 | 示例 | 说明 | 对Lambda表达式 |
---|---|---|---|
引用类方法 | 类名::类方法 | 函数式接口中被实现方法的全部参数传给给类方法作为参数 | (a,b…)->类名.类方法(a,b…) |
引用特定对象的实例方法 | 特定对象::实例方法 | 函数式接口中被实现方法的全部参数传给该方法作为参数 | (a,b…)->特定对象.实例方法(a,b…) |
引用某类对象的实例方法 | 类名::实例方法 | 函数式接口中被实现方发的第一个参数作为调度者,后面参数全部传给该方法作为参数 | |
引用构造器 | 类名::new | 函数式接口中被实现方法的全部参数传给该构造器作为参数 | (a,b…)->new 类名(a,b,…) |
引用类方法
interface Converter {
//返回一个字符类型
Integer converter(String from);
}
//将字符类型转换成整数型
//用Lambda表达式创建一个Converter对象
Converter converter1=from -> Integer.valueOf(from);
//由于converter1对象是Lambda表达式创建的,所以convert()方法执行体就是Lambda表达式的代码块部分。
Integer va1=converter1.converter("99");
System.out.println(va1);
由于上面的Lambda表达式代码块只有一行代码,所以可以用如下方法替换:
Converter converter2=Integer::valueOf;
当调用Converter接口中的唯一的抽象方法时,调用参数将会传给Interger类的valueOf()方法。
引用特定对象的实例方法
先使用Lambda方法来创建一个Converter对象:
Converter converter3=from -> "fkit.org".indexOf(from);
Integer value=converter3.converter("it");
System.out.println(value);
//“fkit.org”是一个String对象,调用这个对象的indexOf实例方法
//因为代码块只有一行,所以可以用下面的代码替换
Converter converter4="fkit.org"::indexOf;
Integer value1=converter3.converter("it");
System.out.println(value1);
对于上面的实例方法引用,也就是调用"fkit.org"对象的indexOf()实例方法来实现Converter函数式接口中唯一的抽象方法,当调用Converter接口中唯一的抽象爱那个方法时,调用参数将会传给"fkit.org"对象的indexOf()实例方法。
引用某类对象的实例方法
定义如下函数式接口:
@FunctionalInterface
interface MyTest {
String test(String a,int b,int c);
}
MyTest mt=(a,b,c)->a.substring(b,c);
//可以用以下代码替换
MyTest mt1=String::substring;
String str=mt.test("I love Java",3,5);
String str1=mt.test("Java I love you",3,5);
System.out.println(str);
System.out.println(str1);
对于上面的实例方法,也就是调用某个String对象的substring()实例方法来实现MyTest函数式接口中唯一的抽象方法,当调用Mytest接口中唯一的抽象方法时,第一个调用参数将作为substring()方法的调度者,剩下的调用参数将会作为substring()方法的调用参数。
引用构造器
定义如下函数式接口:
//下面代码使用Lambda表达式创建YourTest对象
//引用了JFrame的构造器
YourTest yt=(String a)->new JFrame(a);
//可以用下面的代码替换
YourTest yt1=JFrame::new;
//这里的win方法相当于执行了new JFrame(a)语句,并作为这个方法的返回值
JFrame jf=yt.win("我的窗口");
JFrame jf1=yt1.win("我的窗口");
System.out.println(jf);
System.out.println(jf1);
调用某个JFrame类的构造器来实现YourTest函数式接口中唯一的抽象方法,当调用这个抽象方法时,String类型的调用参数将会传给JFrame构造器。这就确定以时调用JFrame类的,一个带参数的构造器。
总结:
个人理解,使用Lamabda表达式时,也就是创建函数式接口对象时(其实可以理解为时匿名的子类对象,因为后面发生了重写),用你定义的引用类方法、引用对象方法或者引用构造器方法来重写了这个类中的抽象方法。
相同点
package org.westos.LambdaDemo;
@FunctionalInterface
interface Displayable {
void display();
default int add(int a,int b){
return a+b;
}
}
package org.westos.LambdaDemo; public class DisplayTest { private int age=12; private static String name="教育中心"; public void test(){ String book="Java从入门到放弃"; //代码块相当于重写了接口中的抽象方法display() //所以左右边Lambda表达式其实是子类的对象,而且继承了父类的默认方法 Displayable dis=()->{ System.out.println("局部变量"+book); System.out.println("外部类变量"+name); System.out.println("外部实例变量"+age); }; dis.display(); System.out.println(dis.add(3,5)); } public static void main(String[] args) { DisplayTest dt=new DisplayTest(); dt.test(); } }
匿名内部类和Lambda的区别
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。