Lambda表达式与函数式接口
Lambda 表达式是在Java 8中引入的,并且成为了Java 8亮点。它使得功能性编程变得非常便利,极大地简化了开发工作。
让我们从最简单的例子开始,来学习如何对一个string列表进行排序。我们首先使用Java 8之前的方法来实现:
List<String> names = Arrays.asList("peter", "anna", "mike", "xenia"); Collections.sort(names, new Comparator<String>() { @Override public int compare(String a, String b) { return b.compareTo(a); } });
静态工具方法Collections.sort接受一个list,和一个Comparator接口作为输入参数,Comparator的实现类可以对输入的list中的元素进行比较。通常情况下,你可以直接用创建匿名Comparator对象,并把它作为参数传递给sort方法。
除了创建匿名对象以外,Java 8 还提供了一种更简洁的方式,Lambda表达式。
Collections.sort(names, (String a, String b) -> { return b.compareTo(a); });
你可以看到,这段代码就比之前的更加简短和易读。但是,它还可以更加简短:Collections.sort(names, (String a, String b) -> b.compareTo(a));
只要一行代码,包含了方法体。你甚至可以连大括号对{}和return关键字都省略不要。不过这还不是最短的写法:Collections.sort(names, (a, b) -> b.compareTo(a));
Java编译器能够自动识别参数的类型,所以你就可以省略掉类型不写。可以看出:相对于之前使用匿名内部类的方式,Java8的lambda表达式更精简。
Lambda表达式用处
1、凡是有匿名内部类的地方,都可以用Lambda表达式简化。
2、Java8 Stream集合之流式操作,方法参数均为Lambda表达式。
Lambda语法解析
我们在此抽象一下lambda表达式的一般语法:
(Type1 param1, Type2 param2, ..., TypeN paramN) -> { statment1; statment2; //............. return statmentM; }
Lambda表达式的定义
Lambda表达式是:一段带有输入参数的可执行语句块,它不用被绑定到一个标识符上,即不需要赋值给一个变量,并且它将来可能被调用。
上面的例子(String a, String b) -> {return b.compareTo(a)}
其实就是一个lambda表达式,并且还可以简写,省略参数类型和return,因此就成为最后的精简版:(a, b) -> b.compareTo(a)
一个Lambda表达式具有下面这样的语法特征。它由三个部分组成:
- 第一部分为一个括号(),里面用逗号分隔的参数列表,参数即函数式接口里面方法的参数;
- 第二部分为一个箭头符号:->;
- 第三部分为一个大括号{},里面是多条语句构成的方法体,可以是表达式和代码块。
简写版本说明
- 参数类型省略,编译器都可以从上下文环境中推断出lambda表达式的参数类型。
- 当lambda表达式的参数个数只有一个,可以省略小括号。
- 当lambda表达式只包含一条语句时,可以省略大括号、return和语句结尾的分号。
- 如果没有参数则只需(),例如 Thread 中的 run 方法就没有参数传入,当它使用 Lambda 表达式后:
Thread t = new Thread(() -> { System.out.println("Hello from a thread in run");
下面列举了Lambda表达式的几个最重要的特征:
● 可选的类型声明:你不用去声明参数的类型。编译器可以从参数的值来推断它是什么类型。
● 可选的参数周围的括号:你可以不用在括号内声明单个参数。但是对于很多参数的情况,括号是必需的。
● 可选的大括号:如果表达式体里面只有一个语句,那么你不必用大括号括起来。
● 可选的返回关键字:如果表达式体只有单个表达式用于值的返回,那么编译器会自动完成这一步。若要指示表达式来返回某个值,则需要使用大括号。
函数式接口
Lambda表达式如何匹配Java的类型系统?语言的设计者们思考了很多如何让现有的功能和lambda表达式友好兼容。于是就有了函数式接口这个概念。函数式接口是一种只有一个方法的接口,函数式接口可以隐式地转换成 Lambda 表达式。
每一个lambda都能够通过一个特定的接口,与一个给定的类型进行匹配。一个所谓的函数式接口必须要有且仅有一个抽象方法声明。每个与之对应的lambda表达式必须要与抽象方法的声明相匹配。
函数式接口的重要属性是:我们能够使用 Lambda 实例化它们,Lambda 表达式让你能够将函数作为方法参数,或者将代码作为数据对待。Lambda 表达式的引入给开发者带来了不少优点:在 Java 8 之前,匿名内部类,监听器和事件处理器的使用都显得很冗长,代码可读性很差,Lambda 表达式的应用则使代码变得更加紧凑,可读性增强。
要使用 Lambda 表达式,需要定义一个函数式接口,这样往往会让程序充斥着过量的仅为 Lambda 表达式服务的函数式接口。为了减少这样过量的函数式接口,Java 8 在 java.util.function 中增加了不少新的函数式通用接口,即内置函数式接口。
内置函数式接口
Predicates预言式接口
Predicate<T> :将 T 作为输入,返回一个布尔值作为输出,该接口包含多种默认方法来将 Predicate 组合成其他复杂的逻辑(与、或、非)。
Predicate是一个布尔类型的函数接口,该函数只有一个输入参数。Predicate接口包含了多种默认方法,用于处理复杂的逻辑动词(and, or,negate)
Predicate<String> predicate = (s) -> s.length() > 0; predicate.test("foo"); // true predicate.negate().test("foo"); // false Predicate<Boolean> nonNull = Objects::nonNull; Predicate<Boolean> isNull = Objects::isNull; Predicate<String> isEmpty = String::isEmpty; Predicate<String> isNotEmpty = isEmpty.negate();
Functions功能式接口
Function<T, R>:将 T 作为输入,返回 R 作为输出,他还包含了和其他函数组合的默认方法。
Function接口接收一个参数,并返回单一的结果。默认方法可以将多个函数串在一起(compse, andThen)。
Function<String, Integer> toInteger = Integer::valueOf; Function<String, String> backToString = toInteger.andThen(String::valueOf); backToString.apply("123"); // "123"
Consumers
Consumer<T> :将 T 作为输入,不返回任何内容,表示在单个参数上的操作。
Consumer代表了在一个输入参数上需要进行的操作。
Consumer<Person> greeter = (p) -> System.out.println("Hello, " + p.firstName); greeter.accept(new Person("Luke", "Skywalker"));
Suppliers
Supplier接口产生一个给定类型的结果。与Function不同的是,Supplier没有输入参数。
Supplier<Person> personSupplier = Person::new; personSupplier.get(); // new Person
Comparators
Comparator接口在早期的Java版本中非常著名。Java 8 为这个接口添加了不同的默认方法。
Comparator<Person> comparator = (p1, p2) -> p1.firstName.compareTo(p2.firstName); Person p1 = new Person("John", "Doe"); Person p2 = new Person("Alice", "Wonderland"); comparator.compare(p1, p2); // > 0 comparator.reversed().compare(p1, p2); // < 0
Lambda表达式访问其外部变量
以前java的匿名内部类在访问外部变量的时候,外部变量必须用final修饰。在java8对这个限制做了优化,可以不用显示使用final修饰,但是编译器隐式当成final来处理。
可以得到以下结论:
● 可访问 static 修饰的成员变量,如果是 final static 修饰,不可再次赋值,只有 static 修饰可再次赋值;
● 可访问表达式外层的 final 局部变量(不用声明为 final,隐性具有 final 语义),不可再次赋值。
Java 8接口的增强
Java 8 对接口做了进一步的增强。在接口中可以添加使用 default 关键字修饰的非抽象方法。还可以在接口中定义静态方法。如今,接口看上去与抽象类的功能越来越类似了。
默认方法
Java 8 还允许我们给接口添加一个非抽象的方法实现,只需要使用 default 关键字即可,这个特征又叫做扩展方法。在实现该接口时,该默认扩展方法在子类上可以直接使用,它的使用方式类似于抽象类中非抽象成员方法。但扩展方法不能够重载 Object 中的方法。例如:toString、equals、 hashCode 不能在接口中被重载。
静态方法
在接口中,还允许定义静态的方法。接口中的静态方法可以直接用接口来调用。
低调大师中文资讯倾力打造互联网数据资讯、行业资源、电子商务、移动互联网、网络营销平台。
持续更新报道IT业界、互联网、市场资讯、驱动更新,是最及时权威的产业资讯及硬件资讯报道平台。
转载内容版权归作者及来源网站所有,本站原创内容转载请注明来源。
- 上一篇
如何用 R 绘制动态统计图?
如果一幅图胜过千言万语,那么一幅会动的图呢? 需求 绘制统计图形,是为了给谁看? 显然不是给电脑看。 因为它看不懂,也没必要看。给它数据就好了。它理解起来,更准确。 绘制统计图形,是给人看的。 可以给别人看。例如合作者、读者、审稿人,或者演讲时的观众。 但更多的情况,图也是给自己看的。 为什么要画图? 因为密密麻麻的数字或符号,远不如一幅图像,看得清楚和舒服。 人类中的大多数,目前还没有进化出对海量原始数据,条件反射一般的理解能力。 漫长的演化史上,人类的感官只要能有效发现食物(包含猎物),快速捕获危险信号(例如捕食者逼近),和同类高效交流(使用声音、表情或肢体语言)就大概率可以在残酷的自然淘汰赛里幸存下来。 不得不从财务报表这样的密集数据里,发现机会和风险,是最近几百年才有的事儿。 巴菲特和芒格这样的投资大家,也许有这种超能力。 但这种能力,显然不是所有人的标配。 对普通人来说,理解大量的数据,统计图形很必要。因此人们常说,“一幅图胜过千言万语”。 在《如何用Python从海量文本抽取主题?》一文里,我给你展示过如何绘制主题挖掘图形。 而《如何用Python和R对故事情节做情绪分析?...
- 下一篇
抽象工厂模式
《大话设计模式》阅读笔记和总结。原书是C#编写的,本人用Java实现了一遍,包括每种设计模式的UML图实现和示例代码实现。 目录:设计模式 Github地址:DesignPattern 说明 定义:抽象工厂模式(Abstract Factory),提供一个创建一系列相关或相互依赖对象的接口,而无需指定它们具体的类。 UML图: 抽象工厂模式UML图.png 示例 例子:依然使用简单工厂模式的示例,用程序实现输入两个数和运算符号,得到结果。 代码实现: 利用反射改造简单工厂模式,去掉分支判断的逻辑 public class OperationFactory { private static Map<String, Class<?>> allOperationMaps = new HashMap<String, Class<?>>(); public static void fillMap() { allOperationMaps.put("+", OperationAdd.class); allOperationMaps.put("-", ...
相关文章
文章评论
共有0条评论来说两句吧...
文章二维码
点击排行
推荐阅读
最新文章
- Hadoop3单机部署,实现最简伪集群
- Springboot2将连接池hikari替换为druid,体验最强大的数据库连接池
- Windows10,CentOS7,CentOS8安装MongoDB4.0.16
- CentOS8安装Docker,最新的服务器搭配容器使用
- Linux系统CentOS6、CentOS7手动修改IP地址
- CentOS7安装Docker,走上虚拟化容器引擎之路
- CentOS8编译安装MySQL8.0.19
- CentOS8,CentOS7,CentOS6编译安装Redis5.0.7
- Jdk安装(Linux,MacOS,Windows),包含三大操作系统的最全安装
- SpringBoot2更换Tomcat为Jetty,小型站点的福音