2017/6/30

转载写明出处:http://www.cnblogs.com/daren-lin/p/anonymous-classes-and-lambda-expressions-in-java.html

本来在查看官方文档中的collection介绍,介绍到如何遍历(traverse)一个容器时提到一个方法是聚合操作(Aggregate Operations),感觉这个写法比较简洁,而且从来没接触过,于是进一步了解。而为了了解Aggregate Operations,必须复习总结一下本文提到的两点:匿名类(Anonymous Classes) 以及 lambda表达式(Lambda Expressions)。官方文档是英文的,为了方便一些关键字直接使用英文原文。

匿名类 (Anonymous class)

首先要知道 匿名类anonymous class是表达式(expression),局部类local class是申明(declaration)

什么时候用:如果一个类只用一次,而且不想取名字的时候

Anonymous class很像的local class,除了没有名字,他们都能够同时定义与实例化。通常如果只需要使用一个类一次,就可以使用anonymous class。

关于anonymous class的定义,和local class也有些不同,local class的定义是一个类的定义,而anonymous class的定义是表达式,所以可以直接在表达式里定义这个类。下面的例子很好的说明了。

 HelloWorld frenchGreeting = new HelloWorld() {
String name = "tout le monde";
public void greet() {
greetSomeone("tout le monde");
}
public void greetSomeone(String someone) {
name = someone;
System.out.println("Salut " + name);
}
};

anonymous class的表达式由一下几点组成

  1. new操作符
  2. 这个类extend的class或者implement的interface的名字,这个例子里,类Implement了一个叫 HelloWorld的interface
  3. 一对括号,里面包含了构造函数的参数,由于这里是实现了一个接口,所以没有构造函数,也就没有参数
  4. 一个body

需要注意的一点,由于anonymous class是表达式,所以最后要加上分号 ;

Lambda表达式(Lambda Expressions)

lambda表达式也是表达式!

什么时候用:如果一个类只有一个方法,而且不想取任何名字的时候

可以看到使用anonymous class比起重新定义一个class来说,方便了很多。然后如果一个class里只有一个方法的话,这样子的定义看起来还是有那么一点繁琐,lambda表达式便可以让这种情况更加的简洁。

下面是一个完整的例子来说明anonymous class 和 lambda表达式的用处(省略了文档中的部分approach)

假设我们有本花名册(roaster)里面存了各种人(Person),有个函数需要筛选年龄大于某个阈值的人,然后输出他们的信息。可以写在如下的函数里:

 // approach 1
public static void printPersonsOlderThan(List<Person> roster, int age) {
for (Person p : roster) {
if (p.getAge() >= age) {
p.printPerson();
}
}
}

但是如果突然产品经理一拍脑袋说,光大于一个年龄不行啊,还得限制他得小于某个年龄,这样就得修改之前写的那个函数了,修改已写过的代码是最蛋疼的事情,因为至少有三件麻烦的事情,1. 以前写的代码不能继续用了 2. 以前写的测试白写了 3. 又要给新写的函数写新的测试了,即使代码和以前的代码基本一样。

于是可以尝试着把判断年龄这块分离出来,因为可能以后产品经理还会一拍脑袋说光年龄不够,再加点别的东西,改改吧很快的对吧!为了防止这种情况带来的修改,我们可以把判断这部用interface代替

 // approach 3
public static void printPersons(
List<Person> roster, CheckPerson tester) {
for (Person p : roster) {
if (tester.test(p)) {
p.printPerson();
}
}
} interface CheckPerson {
boolean test(Person p);
}

然后用一个类来实现这个接口,而在具体调用这个函数的时候,把类当做参数就可以了,如下

 class CheckPersonEligibleForSelectiveService implements CheckPerson {
public boolean test(Person p) {
return p.gender == Person.Sex.MALE &&
p.getAge() >= 18 &&
p.getAge() <= 25;
}
} printPersons(
roster, new CheckPersonEligibleForSelectiveService());

这个时候我们发现 CheckPersonEligibleForSelectiveService 这个类从申明出来到使用只用过一次好嘛,还特地申明一下并且费脑子给他想了个名字,完全可以用刚刚学到的anonymous class来写嘛,稍作修改如下

 // approach 4
printPersons(
roster,
new CheckPerson() {
public boolean test(Person p) {
return p.getGender() == Person.Sex.MALE
&& p.getAge() >= 18
&& p.getAge() <= 25;
}
}
);

而下面终于引出了主角,lambda表达式,由于这个anonymous class只有一个方法,所以不如直接用lambda表达式

 // approach 5
printPersons(
roster,
(Person p) -> p.getGender() == Person.Sex.MALE
&& p.getAge() >= 18
&& p.getAge() <= 25
);

现在给出一个定义:

functional interface: 只有一个abstract方法的interface (但是可以有多个default或者static方法)

java.util.function中定义了很多standard functional interface,供大家使用。

比如现在我们实现的这个interface是一个函数,返回boolean的值,这在标准库中可以用Predicate<T>来实现,而不需要定义CheckPerson这个interface了,代码则可以删去CheckPerson的代码,进一步缩减成:

 public static void printPersonsWithPredicate(
List<Person> roster, Predicate<Person> tester) {
for (Person p : roster) {
if (tester.test(p)) {
p.printPerson();
}
}
} printPersonsWithPredicate(
roster,
p -> p.getGender() == Person.Sex.MALE
&& p.getAge() >= 18
&& p.getAge() <= 25
);

Predicate的定义如下,是在标准库中定义的:

 interface Predicate<T> {
boolean test(T t);
}

好,现在我们尝试把更多的部分用lambda来代替,循环中printPerson() 这块实际上是一个返回void的函数接口,对应于标准库中的 Consumer<>,代码进一步可以写成:

 public static void processPersons(
List<Person> roster,
Predicate<Person> tester,
Consumer<Person> block) {
for (Person p : roster) {
if (tester.test(p)) {
block.accept(p);
}
}
} processPersons(
roster,
p -> p.getGender() == Person.Sex.MALE
&& p.getAge() >= 18
&& p.getAge() <= 25,
p -> p.printPerson()
);

Lambda简直简洁!

Lambda表达式的语法

说了这么多,首先明确lambda是一个表达式,和 a+b 是一个东西,lambda表达式有自己的语法,他由以下几部分组成:

  1. 参数由逗号分隔,整体在一个括号中,比如 (a,b,c) 或 (Person p)

    • 类型可省略
    • 如果只有一个参数,那么连括号都能省略
  2. 箭头  ->
  3. 函数块
    1. 如果没有加上 { } ,则执行时候会自动计算并返回结果
    2. 如果加上了 { } ,就和正常函数一样

可以看到lambda表达式语法还是很直接的。

注意到lambda表达式长的很像函数定义,所以也能把lambda表达式理解为匿名函数

Lambda表达式返回类型

那么lambda表达式返回的是什么类型呢?java编译器会通过上下文来判断出应该返回什么类型。所以lambda表达式必须使用在java编译器能够判断返回类型的情况下。

  • Variable declarations

  • Assignments

  • Return statements

  • Array initializers

  • Method or constructor arguments

  • Lambda expression bodies

  • Conditional expressions,:

  • Cast expressions

参考:

https://docs.oracle.com/javase/tutorial/java/javaOO/anonymousclasses.html

https://docs.oracle.com/javase/tutorial/java/javaOO/lambdaexpressions.html

java语言中的匿名类与lambda表达式介绍与总结 (Anonymous Classes and Lambda Expressions)的更多相关文章

  1. Linq学习系列-----1.1 Lambda表达式介绍

     1.1  Lambda表达式介绍 下图就是一个典型的Lambda表达式. instance:输入参数 =>:Lambda操作符 instance.MemoryCount>=20*1024 ...

  2. Java语言中的面向对象特性总结

    Java语言中的面向对象特性 (总结得不错) [课前思考]  1. 什么是对象?什么是类?什么是包?什么是接口?什么是内部类?  2. 面向对象编程的特性有哪三个?它们各自又有哪些特性?  3. 你知 ...

  3. Java语言中的面向对象特性:封装、继承、多态,面向对象的基本思想(总结得不错)

    Java语言中的面向对象特性(总结得不错) [课前思考] 1. 什么是对象?什么是类?什么是包?什么是接口?什么是内部类? 2. 面向对象编程的特性有哪三个?它们各自又有哪些特性? 3. 你知道jav ...

  4. JAVA语言中的修饰符

    JAVA语言中的修饰符 -----------------------------------------------01--------------------------------------- ...

  5. Java语言中的面向对象特性

    面向对象的基本特征 1.封装性 封装性就是把对象的属性和服务结合成一个独立的相同单位,并尽可能隐蔽对象的内部细节,包含两个含义: ◇ 把对象的全部属性和全部服务结合在一起,形成一个不可分割的独立单位( ...

  6. 列举java语言中反射的常用方法

    package review;/*12:43 2019/7/21*/ import model.AnotherClass; import model.OneClassMore; import mode ...

  7. Java语言中的正则表达式

    正则表达式是什么? 正则表达式是一种强大而灵活的文本处理工具.初学正则表达式时,其语法是一个难点,但它确实是一种简洁.动态的语言.正则表达式提供了一种完全通用的方式,能够解决各种字符串处理相关的问题: ...

  8. Java语言中使用OpenMP

    从去年年中,开始学习Java,主要是维护公司用Java编写的服务器软件.目前,该服务器软件遇到一个问题,在下载大文件时,如果同时下载的用户很多, 服务器软件工作会出现异常,有的用户无法下载.服务器硬件 ...

  9. Java语言中的这些知识点有没有用过,工作中有没有入过这些坑?

    在Java语言中,有一些相对生僻的知识,平时用的机会可能不是很多,但如果不了解不掌握这些知识点的话,也可能会掉入陷阱之中,今天我们就来初步梳理一下: 1. goto是java语言中的关键字. &quo ...

随机推荐

  1. 13、Java菜单条、菜单、菜单项

    13.Java菜单条.菜单.菜单项 一般用Java做界面时,都得牵涉到菜单条.菜单.菜单项的设计.菜单项放在菜单里,菜单放在菜单条里,且其字体均可设置. 13.1.菜单条(Menubar) Frame ...

  2. 005---query接口初步

    Query session.createQuery(String hql)方法; * hibernate的session.createQuery()方法是使用HQL(hibernate的查询语句)语句 ...

  3. 几个常用的linux快捷键和shell知识

    1)   !$    !$是一个特殊的环境变量,它代表了上一个命令的最后一个字符串.如:你可能会这样:     $mkdir mydir     $mv mydir yourdir     $cd y ...

  4. 【Python3之常用模块】

    一.time 1.三种表达方式 在Python中,通常有这几种方式来表示时间: 时间戳(timestamp):通常来说,时间戳表示的是从1970年1月1日00:00:00开始按秒计算的偏移量.命令如下 ...

  5. 提升单元测试体验的利器--Mockito使用总结

    为神马要使用Mockito? 在编写单元测试的时候,为了尽可能的保证隔离性,我们时常需要对某些不容易构造或者不容易获取或者对外部环境有依赖的对象,用一个虚拟的对象来创建以便于测试.假设你正在开发的的代 ...

  6. weather API 天气api接口 收集整理

    腾讯 http://sou.qq.com/online/get_weather.php?callback=Weather&city=南京 中国天气-weather.com.cn http:// ...

  7. JavaScript中对事件简单的理解(1)

    事件(event) 1.什么是JavaScript事件? 事件是文档或浏览器中发生的特定交互瞬间. 2.事件流 事件流描述的是从页面中接受事件的顺序,包含IE提出的事件冒泡流与Netscape提出的事 ...

  8. 测序分析软件-trimmomatic的记录

    1.下载相关软件,网址:http://www.usadellab.org/cms/index.php?page=trimmomatic,它有源代码和二进制两种文件(建议都下载,然后合并成一个文件,因为 ...

  9. github pages部署静态网页

    如果你的项目只是一个静态网站,就没有必要再去整什么服务器,github pages 提供了搭建静态网站的功能: 为什么使用Github Pages 1. 搭建简单而且免费: 2. 支持静态脚本: 3. ...

  10. [0] MVC&MVP&MVVM差异点

    MVC: 用户的请求首先会到达Controller,由Controller从Model获取数据,选择合适的View,把处理结果呈现到View上: MVP: 用户的请求首先会到达View,View传递请 ...