Lambda表达式(一)入门认识篇

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体),对应的是对抽象方法的实现;(函数式接口(只能有一个抽象方法))

  • Lambda表达式的实质是 对接口的实现;
	(parameters) ->{ statements; }
//示例代码
Comparator<Apple> byWeight2 = (Apple o1, Apple o2)-> o1.getWeight().compareTo(o2.getWeight());

解析:

  • 参数列表——这里它采用了 Comparatorcompare 方法的参数,两个 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五大语法格式

语法格式一:接口中的抽象方法 : 无参数,无返回值;

 @Test
public void test1(){
/**
*语法格式一、
* 接口中的抽象方法 : 无参数,无返回值;
*/ /*final */int num = 2; //jdk1.7之前必须定义为final的下面的匿名内部类中才能访问 Runnable r = new Runnable() {
@Override
public 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();
}

语法格式二:接口中的抽象方法 : 一个参数且无返回值; (若只有一个参数,那么小括号可以省略不写)

    @Test
public void test2(){
/**
*语法格式二、
* 接口中的抽象方法 : 一个参数且无返回值; (若只有一个参数,那么小括号可以省略不写)
*/ Consumer<String> con = x -> System.out.println(x);
con.accept("Lambda牛逼!");
}

语法格式三:两个参数,有返回值,并且有多条语句 : 要用大括号括起来,而且要写上return

    @Test
public 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省略

    @Test
public 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表达式,使用块格式,将五行代码转换成单行语句
*/
@Test
public void test6() {
// 1.1原来方式使用匿名内部类
new Thread(new Runnable() {
@Override
public void run() {
System.out.println("Hello world !使用匿名内部类,开线程");
}
}).start(); // 1.2使用 lambda expression
new Thread(() -> System.out.println("使用 lambda expression,开线程")).start(); // 2.1使用匿名内部类
Runnable race1 = new Runnable() {
@Override
public void run() {
System.out.println("使用匿名内部类,不开线程");
}
}; // 2.2使用 lambda expression
Runnable 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 排序 players
Arrays.sort(players, new Comparator<String>() {
@Override
public 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 排序 players
Comparator<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;

@FunctionalInterface
public 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 的方式来实例化对象,然后调用方法返回对象。
* 该接口作为对象的生成器---->创建一个无参构造器,
* 该接口作为对象的生成器---->创建一个有参构造器,
*/
@Test
public 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的新特性


参考/推荐博客:

Lambda表达式详解

Java8新特性入门一(Lambda表达式一)

Java Lambda表达式入门

Lambda表达式(一)入门认识篇的更多相关文章

  1. 函数式编程/lambda表达式入门

    函数式编程/lambda表达式入门 本篇主要讲解 lambda表达式的入门,涉及为什么使用函数式编程,以及jdk8提供的函数式接口 和 接口的默认方法 等等 1.什么是命令式编程 命令式编程就是我们去 ...

  2. c++11新标准for循环和lambda表达式

    :first-child { margin-top: 0px; } .markdown-preview:not([data-use-github-style]) h1, .markdown-previ ...

  3. 一文掌握 Lambda 表达式

    本文将介绍 Java 8 新增的 Lambda 表达式,包括 Lambda 表达式的常见用法以及方法引用的用法,并对 Lambda 表达式的原理进行分析,最后对 Lambda 表达式的优缺点进行一个总 ...

  4. Java 8:掌握 Lambda 表达式

    本文将介绍 Java 8 新增的 Lambda 表达式,包括 Lambda 表达式的常见用法以及方法引用的用法,并对 Lambda 表达式的原理进行分析,最后对 Lambda 表达式的优缺点进行一个总 ...

  5. JAVA8学习——深入浅出Lambda表达式(学习过程)

    JAVA8学习--深入浅出Lambda表达式(学习过程) lambda表达式: 我们为什么要用lambda表达式 在JAVA中,我们无法将函数作为参数传递给一个方法,也无法声明返回一个函数的方法. 在 ...

  6. entity framework 新手入门篇(1.5)-lambda表达式与linq

    在建立好了EF模型之后,先不着急使用它,在使用它之前,你还需要了解两个相关的技术,lambda表达式与linq. 作为微软C#语言中重要的语法糖-lambda表达式与LINQ,本质都是一个方法,以la ...

  7. Lambda 表达式入门,看这篇就够了

    说出来怕你们不相信,刚接到物业通知,疫情防控升级了,车辆只能出不能进,每户家庭每天可指派 1 名成员上街采购生活用品.这不是谣言,截个图自证清白,出自洛阳市湖北路街道处. 看来事态严峻,这样看似好心, ...

  8. ASP.NET MVC学前篇之Lambda表达式、依赖倒置

    ASP.NET MVC学前篇之Lambda表达式.依赖倒置 前言 随着上篇文章的阅读,可能有的朋友会有疑问,比如(A.Method(xxx=>xx>yy);)类似于这样的函数调用语句,里面 ...

  9. Java Lambda表达式入门

    Java Lambda表达式入门 http://blog.csdn.net/renfufei/article/details/24600507 Java 8十个lambda表达式案例 http://w ...

随机推荐

  1. 如何实现 axios 的自定义适配器 adapter

    Axios 是一个非常优秀的基于 promise 的 HTTP 库,可以用在浏览器和 node.js 中.并且提供了很多便捷的功能,例如: 支持 Promise API 拦截请求和响应 转换请求数据和 ...

  2. 万字长文深入理解java中的集合-附PDF下载

    目录 1. 前言 2. List 2.1 fail-safe fail-fast知多少 2.1.1 Fail-fast Iterator 2.1.2 Fail-fast 的原理 2.1.3 Fail- ...

  3. javascript常见面试题之一:数组的冒泡排序;

    var arr=[32,2,7,78,90,10]; //外层循环控制轮数: for (var i = 0; i < arr.length; i++) { //内层循环控制次数: for (va ...

  4. Tomcat8升级后URL中特殊字符报错出现原因

    请求带上花括号等字符,请求无法送达服务端,报错: Failed to load resource: the server responded with a status of 400 () https ...

  5. Eclipse IDE 使用指南:Git失误提交代码,撤销commit操作

    在Eclipse IDE使用Git Commit提交代码时把不需要的文件失误Commit了,比如.settings..classpath..project等文件. 如果是Commit提交代码到本地仓库 ...

  6. 三分钟带你分清Mysql 和Oracle之间的误区

    摘要:Mysql 和Oracle,别再傻傻分不清. mysql 和Oracle 在开发中的使用是随处可见的,那就简单去了解一下这俩款火的不行的数据库. 本质区别: Oracle数据库是一个对象关系数据 ...

  7. 03.axios登录前端

    1.创建一个Login.vue页面   1.1 写页面 views/Login.vue   在 views/components 下创建 Login.vue 页面   <template> ...

  8. linux上性能调优常用命令及简介

    1.综合命令:nmon.top:topas(aix) d :磁盘相关 c:cpu相关 m:内存相关 2.磁盘 2.1 测试顺序写性能dd if=/dev/zero of=/cdr/test.data ...

  9. python创建Django项目

    创建Django项目 关注公众号"轻松学编程"了解更多. 创建一个HelloDjango项目 GitHub地址:https://github.com/liangdongchang/ ...

  10. 作为servlet容器的hi-nginx-java

    hi-nginx-java是一个独立于java官方的servlet规范,它有能力把NGINX直接编成servlet容器服务器.换言之,无需安装tomcat等容器服务器,也无需使用nginx的反向代理功 ...