Lambda简介
Lambda表达式是JDK8的一个新特性,可以取代大部分的匿名内部类,写出更优雅的Java代码,尤其在集合的遍历和其他集合操作中,可以极大地优化代码结构。- JDK 也提供了大量的
内置函数式接口供我们使用,使得Lambda表达式的运用更加方便、高效。 Lambda表达式是Java SE 8中一个重要的新特性。Lambda表达式允许你通过表达式来代替功能接口。Lambda表达式就和方法一样,它提供了一个正常的参数列表和一个使用这些参数的主体(body,可以是一个表达式或一个代码块)。Lambda表达式还增强了集合库。Java SE 8添加了2个对集合数据进行批量操作的包:java.util.function包以及java.util.stream包。 流(stream)就如同迭代器(iterator),但附加了许多额外的功能。 总的来说Lambda表达式和stream是自Java语言添加泛型(Generics)和注解(annotation)以来最大的变化。
Lambda定义
把Lambda表达式理解为简洁地表示可传递的匿名函数的一种方式:
它没有名称,但它有参数列表、函数主体、返回类型,可能还有一个可以抛出的异常列表。
- 匿名——我们说匿名,是因为它不像普通的方法那样有一个明确的名称:写得少而想得多
- 函数——我们说它是函数,是因为Lambda函数不像方法那样属于某个特定的类。但和方法一样,Lambda有参数列表、函数主体、返回类型,还可能有可以抛出的异常列表。
- 传递——Lambda表达式可以作为参数传递给方法或存储在变量中。
- 简洁——无需像匿名类那样写很多模板代码。
对接口的要求
虽然使用Lambda 表达式可以对某些接口进行简单的实现,但并不是所有的接口都可以使用Lambda 表达式来实现。Lambda规定接口中只能有一个需要被实现的方法,不是规定接口中只能有一个方法
jdk 8 中有另一个新特性:default, 被 default 修饰的方法会有默认实现,不是必须被实现的方法,所以不影响 Lambda 表达式的使用。
函数式接口
什么是函数式接口
(1)只包含一个抽象方法的接口,称为函数式接口。
(2)你可以通过 Lambda 表达式来创建该接口的对象。(若Lambda 表达式抛出一个受检异常,那么该异常需要在目标接口的抽象方 法上进行声明)。
(3)我们可以在任意函数式接口上使用 @FunctionalInterface 注解, 这样做可以检查它是否是一个函数式接口,同时 javadoc也会包 含一条声明,说明这个接口是一个函数式接口。
@FunctionalInterface
修饰函数式接口的,要求接口中的抽象方法只有一个。 这个注解往往会和 lambda 表达式一起出现。
Lambda表达式语法
基本语法
- Java8中引入了一个新的操作符,”->”,该操作符称为箭头操作符或者Lambda操作符,箭头操作符将Lambda表达式拆分成两部分;
左侧: Lambda表达式的参数列表,对应的是接口中抽象方法的参数列表;
右侧: Lambda表达式中所需要执行的功能(Lambda体),对应的是对抽象方法的实现;(函数式接口(只能有一个抽象方法))
- Java8中引入了一个新的操作符,”->”,该操作符称为箭头操作符或者Lambda操作符,箭头操作符将Lambda表达式拆分成两部分;
- Lambda表达式的实质是 对接口的实现;
(parameters) ->{ statements; }//示例代码Comparator<Apple> byWeight2 = (Apple o1, Apple o2)-> o1.getWeight().compareTo(o2.getWeight());
解析:
- 参数列表——这里它采用了
Comparator中compare方法的参数,两个Apple - 箭头——箭头 -> 把参数列表与
Lambda主体分隔开 Lambda主体——比较两个Apple的重量。表达式就是Lambda的返回值了
Lambda语法示例
下面是Java Lambda表达式的简单例子:
// 1. 不需要参数,返回值为 5() -> 5// 2. 接收一个参数(数字类型),返回其2倍的值x -> 2 * x// 3. 接受2个参数(数字),并返回他们的差值(x, y) -> x – y// 4. 接收2个int型整数,返回他们的和(int x, int y) -> x + y// 5. 接受一个 string 对象,并在控制台打印,不返回任何值(看起来像是返回void)(String s) -> System.out.print(s)
Lambda五大语法格式
语法格式一:接口中的抽象方法 : 无参数,无返回值;
@Testpublic void test1(){/***语法格式一、* 接口中的抽象方法 : 无参数,无返回值;*//*final */int num = 2; //jdk1.7之前必须定义为final的下面的匿名内部类中才能访问Runnable r = new Runnable() {@Overridepublic void run() {System.out.println("Hello world!" + num); //本质还是不能对num操作(只是jdk自己为我们设置成了final的)}};r.run();System.out.println("----------使用Lambda输出-----------");Runnable r1 = () -> System.out.println("Hello world!" + num);//省去乐:大括号,分号r1.run();}
语法格式二:接口中的抽象方法 : 一个参数且无返回值; (若只有一个参数,那么小括号可以省略不写)
@Testpublic void test2(){/***语法格式二、* 接口中的抽象方法 : 一个参数且无返回值; (若只有一个参数,那么小括号可以省略不写)*/Consumer<String> con = x -> System.out.println(x);con.accept("Lambda牛逼!");}
语法格式三:两个参数,有返回值,并且有多条语句 : 要用大括号括起来,而且要写上return
@Testpublic void test3(){/***语法格式三、* 两个参数,有返回值,并且有多条语句 : 要用大括号括起来,而且要写上return*/Comparator<Integer> com = (x,y) -> {System.out.println("函数式接口,");return Integer.compare(y,x); //降序};Integer[] nums = {4,2,8,1,5};Arrays.sort(nums,com);System.out.println(Arrays.toString(nums));}
语法格式四:两个参数,有返回值,但是只有一条语句: 大括号省略,return省略
@Testpublic void test4(){/***语法格式四、* 两个参数,有返回值,但是只有一条语句: 大括号省略,return省略*/Comparator<Integer> com = (x,y) -> Integer.compare(x,y);//升序Integer[] nums = {4,2,8,1,5};Arrays.sort(nums,com);System.out.println(Arrays.toString(nums));}
语法格式五:表达式的参数列表的数据类型 可以省略不写,因为JVM编译器通过上下文推断出数据类型,即”类型推断”,
(Integer x,Integer y ) -> Integer.compare(x,y)//可以简写成(x,y) -> Integer.compare(x,y);
Lambda 表达式常用示例
Lambda创建线程
- 创建 线程对象,然后通过匿名内部类重写
run()方法,提到匿名内部类我们可以通过使用lambda表达式来简化线程的创建过程。
通过Thread创建线程示例代码:
Thread t = new Thread(() -> {for (int i = 0; i < 10; i++) {System.out.println(2 + ":" + i);}});t.start();
通过Runnable创建线程示例代码:
/** Lambda实现Runnable接口* Runnable 的 lambda表达式,使用块格式,将五行代码转换成单行语句*/@Testpublic void test6() {// 1.1原来方式使用匿名内部类new Thread(new Runnable() {@Overridepublic void run() {System.out.println("Hello world !使用匿名内部类,开线程");}}).start();// 1.2使用 lambda expressionnew Thread(() -> System.out.println("使用 lambda expression,开线程")).start();// 2.1使用匿名内部类Runnable race1 = new Runnable() {@Overridepublic void run() {System.out.println("使用匿名内部类,不开线程");}};// 2.2使用 lambda expressionRunnable race2 = () -> System.out.println("使用 lambda expression,不开线程");// 直接调用 run 方法(没开新线程哦!)race1.run();race2.run();}
Lambda遍历集合
通过调用集合的 public void forEach(Consumer<? super E> action) 方法,通过lambda 表达式的方式遍历集合中的元素。以下是Consumer 接口的方法以及遍历集合的操作。Consumer 接口是 jdk 为我们提供的一个函数式接口。
//jdk提供的函数式接口@FunctionalInterface//这个注解是用判断是否是函数式接口public interface Consumer<T> {void accept(T t);//....}String[] atp = {"Rafael Nadal", "Novak Djokovic","Stanislas Wawrinka","David Ferrer","Roger Federer","Andy Murray","Tomas Berdych","Juan Martin Del Potro"};List<String> players = Arrays.asList(atp);// 以前的循环方式for (String player : players) {System.out.print(player + "; ");}// 使用 lambda 表达式以及函数操作(functional operation)players.forEach((player) -> System.out.print(player + "; "));// 在 Java 8 中使用双冒号操作符(double colon operator)players.forEach(System.out::println);}
删除集合中的某个元素
通过removeIf()方法来删除集合中的某个元素,**Predicate** 也是 jdk 为我们提供的一个函数式接口,可以简化程序的编写。
List<Person> javaProgrammers = new ArrayList<Person>() {{add(new Person("Elsdon", "Jaycob", "Java programmer", "male", 43, 2000));add(new Person("Tamsen", "Brittany", "Java programmer", "female", 23, 1500));add(new Person("Floyd", "Donny", "Java programmer", "male", 33, 1800));add(new Person("Sindy", "Jonie", "Java programmer", "female", 32, 1600));add(new Person("Vere", "Hervey", "Java programmer", "male", 22, 1200));add(new Person("Maude", "Jaimie", "Java programmer", "female", 27, 1900));add(new Person("Shawn", "Randall", "Java programmer", "male", 30, 2300));add(new Person("Jayden", "Corrina", "Java programmer", "female", 35, 1700));add(new Person("Palmer", "Dene", "Java programmer", "male", 33, 2000));add(new Person("Addison", "Pam", "Java programmer", "female", 34, 1300));}};//removeIf()删除集合中符合条件的值javaProgrammers.removeIf(ele -> ele.getSalary() == 2000);//通过 foreach 遍历,查看是否已经删除javaProgrammers.forEach(System.out::println);
集合内元素的排序
在以前我们若要为集合内的元素排序,就必须调用 sort 方法,传入比较器匿名内部类重写 compare 方法,我们现在可以使用 lambda 表达式来简化代码。
/**Lambdas排序集合* 在Java中,Comparator 类被用来排序集合。*/String[] players = {"Rafael Nadal", "Novak Djokovic","Stanislas Wawrinka", "David Ferrer","Roger Federer", "Andy Murray","Tomas Berdych", "Juan Martin Del Potro","Richard Gasquet", "John Isner"};// 1.1 使用匿名内部类根据 name 排序 playersArrays.sort(players, new Comparator<String>() {@Overridepublic int compare(String s1, String s2) {return (s1.compareTo(s2));}});System.out.println("使用静态内部类排序结果:"+Arrays.toString(players));System.out.println("-----------------------分割线-------------------------");String[] players2 = {"Rafael Nadal", "Novak Djokovic","Stanislas Wawrinka", "David Ferrer","Roger Federer", "Andy Murray","Tomas Berdych", "Juan Martin Del Potro","Richard Gasquet", "John Isner"};// 1.2 使用 lambda expression 排序 playersComparator<String> sortByName = (String s1, String s2) -> (s1.compareTo(s2));Arrays.sort(players2, sortByName);System.out.println("使用lambda排序结果(方式一):"+Arrays.toString(players2));// 1.3 也可以采用如下形式:Arrays.sort(players2, (String s1, String s2) -> (s1.compareTo(s2)));System.out.println("使用lambda排序结果(方式二):"+Arrays.toString(players2));
Lambda 表达式引用方法
有时候我们不是必须要自己重写某个匿名内部类的方法,我们可以可以利用 lambda表达式的接口快速指向一个已经被实现的方法。
语法:
方法归属者::方法名 静态方法的归属者为类名,普通方法归属者为对象
示例代码:
package com.zy.pagehelper.Interface;@FunctionalInterfacepublic interface ReturnOneParam {/***函数式接口* 一个参数有返回值*/int method(int a);}public class LambdaTest {public static void main(String[] args) {//这里返回函数式接口ReturnOneParam一个参数ReturnOneParam lambda1 = a -> doubleNum(a);System.out.println(lambda1.method(3));//lambda2 引用了已经实现的 doubleNum 方法ReturnOneParam lambda2 = LambdaTest::doubleNum;System.out.println(lambda2.method(3));LambdaTest exe = new LambdaTest();//lambda4 引用了已经实现的 addTwo 方法ReturnOneParam lambda4 = exe::addTwo;System.out.println(lambda4.method(2));}/*** 要求* 1.参数数量和类型要与接口中定义的一致* 2.返回值类型要与接口中定义的一致*/public static int doubleNum(int a) {return a * 2;}public int addTwo(int a) {return a + 2;}}
构造方法的引用
一般我们需要声明接口,该接口作为对象的生成器,通过 类名::new 的方式来实例化对象,然后调用方法返回对象。
示例代码:
public interface PersonCreatorBlankConstruct {/**接口作为对象的生成器*无参构造器*/Person getPerson();}public interface PersonCreatorParamContruct {/**有参构造器*/Person getPerson(String firstName, String lastName, String job,String gender, int age, int salary);}/***构造方法的引用* 一般我们需要声明接口,该接口作为对象的生成器,通过 类名::new 的方式来实例化对象,然后调用方法返回对象。* 该接口作为对象的生成器---->创建一个无参构造器,* 该接口作为对象的生成器---->创建一个有参构造器,*/@Testpublic void test9() {/***1.lambda表达式创建对象,返回无参函数接口,生参无参对象*/PersonCreatorBlankConstruct creator = () -> new Person();Person person = creator.getPerson();PersonCreatorBlankConstruct creator2 = Person::new;Person person1 = creator2.getPerson();PersonCreatorParamContruct creator3 = Person::new;Person person2 = creator3.getPerson("名称", "修改名称","职位","男",23,2000);}
完毕!,搞定搞定lambda表达式的基本知识点,接下来我们才可以更深入的认识JDK8的新特性
