Lambda表达式支持将代码块作为方法参数,Lambda表达式允许使用更简单的代码来创建只有一个抽象方法的接口(这种接口被称为函数式接口)的实例。
Command.java
package code;
public interface Command
{
// 接口里定义的process()方法用于封装“处理行为”
void process(int[] target);
}
ProcessArray.java
package code;
public class ProcessArray
{
public void process(int[] target , Command cmd)
{
cmd.process(target);
}
}
CommandTest.java
package code;
public class CommandTest{
public static void main(String [] args){
ProcessArray pa = new ProcessArray();
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);
}
});
}
}
数组元素的总和是9
CommandTest2.java
package code;
public class CommandTest2{
public static void main(String[] args){
ProcessArray pa = new ProcessArray();
int [] array = {3,-4,6,7};
pa.process(array, (int[] target)->{
int sum = 0;
for(int tmp : target){
sum += tmp;
}
System.out.println("数组元素的总和是" + sum);
});
}
}
Lambda表达式可以简化匿名内部类对象,不需要new Xxx(){}这种繁琐的代码,不需要指出重写的方法名字,也不需要给出重写的方法的返回值类型———只要给出重写的方法括号以及括号里的形参列表即可。
Lambda表达式的代码块会代替实现抽象方法的方法体,Lambda表达式就相当一个匿名方法。
Lambda表达式由三个部分组成:
package code;
interface Eatable{
void taste();
}
interface Flyable{
void fly(String weather);
}
interface Addable{
int add(int a ,int b);
}
public class LambdaQs{
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();
//Lambda表达式的代码块只有一条语句,可以省略花括号
lq.eat(()->System.out.println("苹果的味道不错"));
//Lambda表达式的形参列表只有一个形参,可以省略圆括号
lq.drive(weather->{
System.out.println("今天天气是"+ weather);
System.out.println("直升机飞行平稳");
});
//Lambda表达式的代码块只有一条语句,可以省略花括号
//代码块中只有一条语句,即使该表达式需要返回值,也可以省略return关键字
lq.test((a,b)->a+b);
}
}
code.LambdaQs
Lambda$1/1175962212@24d46ca6
苹果的味道不错
我正在驾驶code.LambdaQs$$Lambda$2/925858445@2f92e0f4
今天天气是碧空如洗的晴日
直升机飞行平稳
5+3的和为8
Lambda表达式的类型,也被称为“目标类型(target type)”,Lambda表达式的目标类型必须是“函数式接口(functional interface)”。函数式接口代表只包含一个抽象方法的接口,函数式接口可以包含多个默认方法,类方法,但只能声明一个抽象方法。
如果采用 匿名内部类语法来创建函数式接口的实例,则只需要实现一个抽象方法,在这种情况下即可采用Lambda表达式来创建对象。
java8专门为函数式接口提供了@FunctionalInterface注解,该注解通常放在接口定义前面,该注解对程序功能没有任何作用,它用于告诉编译器执行更严格的检查—检查该接口必须是函数式接口,否则编译器就会报错。
由于Lambda表达式的结果就是别当成对象,因此程序中完全可以使用Lambda表达式进行赋值。
Runnable r =()->{
for(int i = 0;i<100;i++)
System.out.println();
};
Lambda表达式有两个限制。
Obj obj = ()->{
for(int i=0;i<100;i++)
System.out.println();
};
上面程序直接赋值给Object变量,编译上面程序会报错。
LambdaTest.java:33: 错误: 不兼容的类型: Object 不是函数接口
所以Lambda表达式的目标类型必须是明确的函数式接口,而Lambda表达式的类型为Object,Object并不是函数式接口,因此上面代码报错。
可以用上面三种常见的方式来保证代码正确:
Object obj = (Runnable)->{
for(int i = 0;i<100;i++)
System.out.println();
}
同样的Lambda表达式的目标类型完全可能是变化的—–唯一的要求是要与目标类型中唯一的抽象方法有相同的形参列表。
@FunctionalInterface
interface FKTest{
void run();
}
Object obj2 = (FKTest)()->{
for(int i= 0;i<100;i++)
System.out.println();
};
Java8在java.util.function包下预定义了大量函数式接口。
方法引用和构造器引用可以让Lambda表达式的代码更加简洁。
种类 | 示例 | 对应的Lambda表达式 |
---|---|---|
引用类方法 | 类名::类方法 | (a,b…)->类名.类方法(a,b…) |
引用特定对象的实例方法 | 特定对象::实例方法 | (a,b…)->特定对象.实例方法(a,b…) |
引用某类对象的实例方法 | 类名::实例方法 | (a,b…)->a.实例方法(b…) |
引用构造器 | 类名::new | (a,b…)->类名(a,b…) |
详细:
@functionalInterface
interface Converter{
Integer convert(String from);
}
public class MethodRefer{
public static void main(String []args){
//Converter con1 = from->Integer.valueOf(from);
Converter con1 = Integer::valueOf;
Integer val = con1.convert("22");
System.out.println(val);
}
}
对于上面的类方法引用,也就是调用Integer类的valueOf()类方法来实现Converter函数式接口中唯一的抽象方法,当调用Converter接口中的唯一的抽象方法时,调用参数将会传给Integer类的valueOf()类方法。
2. 引用特定对象的实例方法
先用Lambda表达式来创建一个Converter对象
Converter converter2 = from->"fkit.org".indexOf(from);
Integer value = converter2.convert("it");
System.out.println(value);
调用converter1对象的convert()方法将字符串转换为整数,
Converter converter2 = "fkit.org"::indexOf;
Integer value = converter2.convert("it");
对于上面的实例方法,也就是调用“fkit.org”对象的indexOf()实例方法,来实现,Converter函数式接口中唯一的抽象方法,调用参数将会传给”fkit.org”对象的indexOf()实例方法。
3. 引用某类对象的实例方法
@FunctionalInterface
interface MyTest{
String test(String a,int b,int c);
}
该函数式接口中包含一个test()抽象方法,该方法负责根据Stirng,int,int三个参数生成一个String返回值。
//MyTest mt = (a,b,c)->a.substring(b,c);
MyTest mt = String::substring;
String str = mt.test("Java I Love You",2,9);
System.out.println(str);
创建Lambda表达式,
接着可以调用mt对象的test()—–由于mt对象是Lambda表达式创建的,test()方法执行体就是Lambda表达式的代码块部分,因此上面程序输出
va I Lo
对于上面的实例方法引用,也就是调用某个String对象的substring()实例方法来实现MyTest函数式接口中唯一的抽象方法,当调用MyTest接口中的唯一的抽象方法时,第一个调用参数将作为substring()方法的调用者,剩下的调用参数会作为substring()实例方法的调用参数。
4. 引用构造器
import javax.swing.*;
@FunctionalInterface
interface YourTest{
//JFrame需要导入javax.swing.*;
JFrame win(String title);
}
该函数接口包含一个win()抽象方法, 该方法负责根据String参数生成一个JFrame返回值。
//YourTest yt = (String a) -> new JFrame(a);
YourTest yt = JFrame::new;
JFrame jf = yt.win("我的窗口");
//System.out.println(jf);
jf.setVisible(true);
使用Lambda表达式创建一个YourTest对象,
接着是调用yt对象的win()方法—-由于yt对象是Lambda表达式创建的,因此win()方法执行体就是Lambda表达式的代码块部分,即执行体就是执行new JFrame(a);语句,并将这条语句的值作为方法的返回值。
调用YourTest对象的win()抽象方法时,实际只传入了一个String类型的参数,这个String类型的参数会被传给JFrame构造器—这就确定了是调用JFrame类的、带一个String参数的构造器。
Lambda表达式是匿名内部类的一种简化,因此它可以部分取代匿名内部类的作用,Lambda和匿名内部类有以下的相同点
package code;
@FunctionalInterface
interface Displayable{
//定义一个抽象方法和默认方法
void display();
default int add(int a,int b){
return a+b;
}
}
public class LambdaAndInner{
private int age = 12;
private static String name = "疯狂软件中心";
public void test(){
String book = "疯狂Java讲义";
Displayable dis = ()->{
System.out.println("book局部变量为" + book);
System.out.println("外部类的age实例变量为" + age);
System.out.println("外部类的name类变量为" + name);
};
dis.display();
System.out.println(dis.add(3,5));
}
public static void main(String [] args){
LambdaAndInner lambda = new LambdaAndInner();
lambda.test();
}
}
book局部变量为疯狂Java讲义
外部类的age实例变量为12
外部类的name类变量为疯狂软件中心
8
当程序使用Lambda表达式创建了Displayable的对象之后,该对象不仅可调用接口中唯一的抽象方法,也可调用接口中的默认方法。
主要存在如下区别:
//尝试调用接口中的默认方法,编译器会报错
System.out.println(add(3,5));
虽然Lambda比大师的目标类型:Displayable中包含了add()方法,但是Lambda表达式的代码块不允许调用这个方法, 而改用匿名内部类的时候就可以调用这个add()方法。
Arrays类的有些方法需要Comparator、XxxOperato、XxxFunction等接口的实例,这些接口都是函数式接口,因此可以使用Lambda表达式来调用Arrays的方法。
package code;
import java.util.Arrays;
public class LambdaArrays{
public static void main(String [] args){
String [] arr1 = new String[]{
"java","fkava","fkit","ios","android"};
Arrays.parallelSort(arr1,(o1,o2)->o1.length()-o2.length());
System.out.println(Arrays.toString(arr1));
int[] arr2 = new int[]{3,-4,25,16,30,18};
Arrays.parallelPrefix(arr2,(left,right)->left*right);
System.out.println(Arrays.toString(arr2));
long[] arr3 = new long[5];
Arrays.parallelSetAll(arr3,operand->operand*5);
System.out.println(Arrays.toString(arr3));
}
}
[ios, java, fkit, fkava, android]
[3, -12, -300, -4800, -144000, -2592000]
[0, 5, 10, 15, 20]
原文地址:http://blog.csdn.net/u014270902/article/details/43957179