07_Java8新增的Lambda表达式
【Lambda表达式概述】
Lambda表达式支持将代码块作为方法参数,Lambda表达式允许将使用简洁的代码来创建只有一个抽象方法的接口的实例。(这种接口称为函数式接口)
【入门实例】
package com.Higgin.Lambda; import org.junit.Test; interface Command{ //接口里定义的process封装行为 public void process(int[] target); } class ProcessArray{ public void process(int[] target , Command cmd){ cmd.process(target); } } public class LambdaDemo { /** * Lambda的方式 代替匿名内部类 */ public static void main(String[] args) { ProcessArray pa=new ProcessArray(); int[] array={1,2,3,4,5}; pa.process(array, (int[] target)->{ int sum=0; for(int tmp:target){ sum=sum+tmp; } System.out.println("数组的和为"+sum); }); } /** * 匿名内部类的方式 */ @Test public void test(){ ProcessArray pa=new ProcessArray(); int[] array={1,3,5,7,9}; pa.process(array, new Command() { @Override public void process(int[] target) { int sum=0; for(int tmp:target){ sum=sum+tmp; } System.out.println("数组的和为"+sum); } }); } }
【分析】
int sum=0; for(int tmp:target){ sum=sum+tmp; } System.out.println("数组的和为"+sum);
lambda表达式代码 与 创建匿名内部类 时需要实现的process(int[] target)方法完全一致。
Lambda表达式不需要去实现 " new XXX(){} " 这部分繁琐的代码。
lambda表达式不需要指出重写的方法名字。
lambda表达式不需要重写方法的返回值类型—— 只需要给出重写的方法括号以及括号里的形参列表 即可。
【Lambda表达式的组成】
Lambda的主要作用是代替匿名内部类的繁琐语法。其组成如下:
1.形参列表。形参列表允许省略形参类型。如果形参列表中只有一个参数,甚至连形参列表的圆括号也可以省略。
2.箭头(->)。
3.代码块。
如果代码块只有一条语句,可以省略代码的花括号,此时也得该语句后面的分号。
如果代码块只有一条return语句,甚至可以忽略return关键字。
【简单的几个例子】
package com.Higgn.Lambda; interface Eateable{ void eat(); } interface Flyable{ void fly(String weather); } interface Addable{ int add(int a,int b); } class Demo{ //调用Eateable对象的方法 public void getEat(Eateable e){ e.eat(); } //调用Flyable的fly方法 public void getFly(Flyable f){ f.fly("晴天"); } //调用Addable的add方法 public void getAdd(Addable a){ System.out.println("和为:"+a.add(10,8)); } } public class LambdaDemo { public static void main(String[] args) { Demo demo=new Demo(); System.out.println("--------------------分割线1---------------------------"); /** * 无参数、代码块有两条及以上语句 */ demo.getEat(()->{ System.out.println("吃了一个大苹果"); System.out.println("又吃了一个大橘子"); }); System.out.println("--------------------分割线2---------------------------"); /** * 无参数、代码块只有一条语句 * 此时可以忽略花括号、分号 */ demo.getEat(()->System.out.println("只吃一个大西瓜!")); System.out.println("--------------------分割线3---------------------------"); /** * 形参列表有一个参数、代码块只有一条语句 */ demo.getFly((weather)->{ System.out.println("今天天气是:"+weather); }); System.out.println("--------------------分割线4---------------------------"); /** * 形参列表只有一个参数、代码块只有一条语句 * 可以省略参数的圆括号,可以省略代码块的花括号 */ demo.getFly(weather->System.out.println("明天天气是:"+weather)); System.out.println("--------------------分割线5---------------------------"); /** * 形参列表有两个参数,有返回值 */ demo.getAdd((a,b)->{ return a+b; }); System.out.println("--------------------分割线6---------------------------"); /** * 形参列表有两个参数,有返回值 * 代码块中只有一条语句,即使该表达式需要返回值,也可以省略return语句 */ demo.getAdd((a,b)->a+b); } }
【运行结果】
【Lambda表达式与函数式接口】
函数式接口:代表只包含一个抽象方法的接口。函数式接口可以包含多个默认方法、类方法,但是只能声明一个抽象方法。
Lambda表达式的目标类型(target type)必须是函数式接口(functional interface)。
【@FunctionalInterface】
Java8专门为函数式接口提供了@FunctionalInterface注解,该注解对程序功能没有任何作用。它用于告诉编译器执行更严格检查,检查该接口必须是函数式接口,否则报错。
【Runnable是java本身提供的一个函数式接口】
【Runnable使用Lambda表达式实现】
/** * Runnable接口中只包含一个无参数的方法 * Lambda表达式代表的匿名方法实现了Runnable接口中唯一的、无参数的方法 * 因此下面的Lambda表达式创建了一个Runnable对象 */ public static void main(String[] args) { //Runnable是一个函数式接口 Runnable r=()->{ for(int i=0;i<100;i++){ System.out.println("i======"+i); } }; Thread t=new Thread(r); t.start(); }
【Lambda表达式的两个限制】
1.Lambda表达式的目标类型必须是明确的函数式接口。
2.Lambda表达式只能为函数式接口创建对象,Lambda表达式只能实现一个方法,因此只能为只有一个抽象方法的接口(函数式接口)创建对象。
【保证Lambda表达式的目标类型是一个明确的函数式接口的三种方式】
1.将Lambda表达式赋值给函数式接口类型的变量。
2.将Lambda表达式作为函数式接口类型的参数传给某个方法。
3.使用函数式接口对Lambda表达式进行强制类型转换。
【保证函数式接口的方法】
【Java8的典型4类函数接口】
Java8在java.util.function包下预定义了大量的函数式接口,典型的4类接口如下:
1.XxxFunction接口:通常包含一个apply()抽象方法。该方法用于对参数进行处理、转换(apply()方法的处理逻辑由Lambda表达式实现),然后返回一个新的值。该函数式接口常用于对指定数据的转换。
2.XxxConsumer接口:通常包含一个accept()抽象方法,与上面的apply()方法类似,主要负责对数据的处理,区别是accept()方法不会返回处理结果。
3.XxxPredicate接口:通常包含一个test()抽象方法,该方法常用于对参数进行某种判断(test()方法的判断逻辑由Lambda表达式实现),然后返回一个boolean值。该函数式接口通常用于判断参数是否满足特定条件,常用于数据的筛选。
4.XxxSupplier接口:通常包含一个getAsXxx()抽象方法,该方法不需要输入参数,该方法会按照某种逻辑算法(getAsXxx()方法的逻辑算法由Lambda表达式来实现)返回一个数据。
【方法引用与构造器引用】
如果Lambda表达式的代码块只有一条代码,程序可以省略Lambda表达式中的代码块的花括号。
除此之外,如果Lambda表达式的代码块只有一条代码,还可以在代码块中使用方法引用和构造器引用。
【Lambda表达式支持的方法引用和构造器引用】
方法引用和构造器引用都需要使用两个英文冒号“::”
【种类一:引用类方法】
/**
* 类方法引用,也就是调用Integer类的valueOf()方法来实现函数式接口Converter中唯一的抽象方法convert(String str),
* 当调用convert(String str)方法时,调用参数将会传递给Integer类的ValueOf()方法。
*/
@FunctionalInterface interface Converter{ //convert方法:String类型转换为Integer类型 Integer convert(String str); } public class ConverterDemo { public static void main(String[] args) { /** * Lambda表达式的方式实现 */ Converter con1=str->Integer.valueOf(str); // (a,b...)->类名.类方法(a,b...) Integer val1=con1.convert("666"); System.out.println(val1); /** * 引用类方法 * 使用 方法引用 代替Lambda表达式 * 函数式接口中被实现的方法的全部参数传给该类方法作为参数 */ Converter con2=Integer::valueOf; //类名::类方法 Integer val2=con2.convert("888"); System.out.println(val2); } }
【种类二:引用特定对象的实例方法】
/** * 引用特定对象的实例方法 * 下面案例也就是调用"ABCDEFGHIGK"对象的实例方法indexOf()来实现Converter函数式接口中唯一的抽象方法convert(String str), * 当调用convert(String str)方法时,调用的参数"D"会传递给"ABCDEFGHIGK"对象的实例方法indexOf()。 */ @FunctionalInterface interface Converter{ //convert方法:String类型转换为Integer类型 Integer convert(String str); } public class ConverterDemo { public static void main(String[] args) { /** * Lambda表达式创建converter对象 */ Converter con1=str->"ABCDEFGHIGK".indexOf(str); //(a,b...)->特定对象.实例方法(a,b...) Integer val1=con1.convert("H"); System.out.println(val1); /** * 引用特定对象的实例方法,代替Lambda表达式
* 函数式接口中被实现方法的全部参数传给该类方法作为参数 */ Converter con2="ABCDEFGHIGK"::indexOf; //特定对象::实例方法 Integer val2=con1.convert("D"); System.out.println(val2); } }
【种类三:引用某类对象的实例方法】
/** * 引用某类对象的实例方法 */ @FunctionalInterface interface ISubString{ public String sub(String str,int begin,int end); } public class SubStringDemo { public static void main(String[] args) { /** * Lambda表达式方式 */ ISubString is1=(str,begin,end)->str.substring(begin,end); //(a,b...)->a.实例方法(b...) String str1=is1.sub("HigginCui", 6, 9); System.out.println(str1); /** * 引用某类对象的实例方法, 代替Lambda表达式 * 函数式接口中被实现方法的第一个参数"HigginCui"作为调用者,后面的参数全部传递给该方法作为参数 */ ISubString is2=String::substring; //类名:实例方法 String str2=is2.sub("HigginCui", 0, 6); System.out.println(str2); } }
【种类4:引用构造器】
@FunctionalInterface interface MyTest{ JFrame win(String title); } public class TestDemo { public static void main(String[] args) { /** * Lambda的方式 */ MyTest mt1=(String a)->new JFrame(a); //(a,b...)->new 类名(a,b...) JFrame jf1=mt1.win("我的窗口1"); System.out.println(jf1); /** * 引用构造器 */ MyTest mt2=JFrame::new; //类名::new JFrame jf2=mt2.win("我的窗口2"); System.out.println(jf2); } }