Lambda表达式可以理解为简洁地表示可传递的匿名函数的一种方式:它没有名称,但它有参数列表/函数主体/返回类型,可能还有一个可以抛出的异常列表。

Lambda表达式由参数/箭头和主体组成:

(Apple a1, Apple a2)  -> a1.getWeight().compareTo(a2.getWeight());

之前的代码形式:

Comparator<Apple> byWeight = new Comparator<Apple>() {
  public int compare(Apple a1, Apple a2) {

    return a1.getWeight().compareTo(a2.getWeight());

  }

}

Java 8 中有效的Lambda表达式:

1,  (String s) -> s.length()

  具有一个String类型的参数,并返回一个int。Lambda表达式没有return语句,因为已经隐含了return。

2,  (Apple a) -> a.getWeight() > 150

  参数为Apple类型,返回一个boolean类型。

3,  (int x, int y) -> {

    System.out.println("Result");

    System.out.println(x+y);

  }

  该Lambda表达式具有两个int类型的参数而没有返回值。Lambda表达式可以包含多行语句。

  () -> {return "Mario";}

  该Lambda表达式也是有效的。

4, () - > 42

  该Lambda表达式没有参数,返回一个int

5, (Apple a1, Apple a2) -> a1.getWeight().compareTo(a2.getWeight())

  该表达式具有两个Apple类型的参数,返回一个int

无效Lambda表达式:

(Integer i) -> return "Alan" + i;

(String s) -> {"IronMan";}

第一个表达式中,return是一个控制流语句。要使得这个语句有效,需要加上花括号。

第二个表达式中,"IronMan" 是一个表达式,不是语句。要使得此Lambda有效,可以去除花括号和分号。

Lambda基本语法:

(parameters) -> expression

or

(parameters) -> { statements;}

可以使用Lambda表达式的地方 —— 函数式接口

  只有在接受函数式接口的地方才可以使用Lambda表达式。

  函数式接口就是只定义一个抽象方法的接口。如Comparator和Runnable接口。

  Lambda表达式可以直接以内联的形式为函数式接口的抽象方法提供实现,并把整个表达式作为函数式接口的实例。

  函数式接口的抽象方法的签名就是Lambda表达式的签名。这种抽象方法叫做函数描述符。

  新的Java API中,函数式接口带有@FunctionalInterface的标注。如果用@FunctionalInterface定义了一个接口,但它却不是函数式接口的话,便一起将返回一个提示原因的错误,例如“Multiple non-overriding abstract methods found in interface Foo”,表明存在多个抽象方法。

使用Lambda表达式的步骤:

1,行为参数化:

  提取Lambda表达式,设计好参数,函数主体和返回值。

2,使用函数式接口来传递行为:

  创建一个能匹配Lambda表达式的函数式接口I,并把这个接口作为参数传递给需要使用Lambda表达式的函数M。

3,执行函数式接口中的行为

  在函数M中调用接口I中的抽象函数

4,传递Lambda表达式

常用函数式接口:

Java 8 以前已有的函数式接口:

  Comparable

  Runnable

  Callable

Java 8 在java.util.function包中引入的新的函数式接口:

  Predicate  

@FunctionalInterface
public interface Predicate<T>{boolean test(T t);
}
public static <T> List<T> filter(List<T> list, Predicate<T> p) {List<T> results = new ArrayList<>();for(T s: list){if(p.test(s)){results.add(s);}}return results;
}
Predicate<String> nonEmptyStringPredicate = (String s) -> !s.isEmpty();
List<String> nonEmpty = filter(listOfStrings, nonEmptyStringPredicate);

  Consumer  

  

@FunctionalInterface
public interface Consumer<T>{void accept(T t);
}
public static <T> void forEach(List<T> list, Consumer<T> c){    for(T i: list){c.accept(i);}
}
forEach(Arrays.asList(1,2,3,4,5),(Integer i) -> System.out.println(i));

  Function

@FunctionalInterface
public interface Function<T, R>{R apply(T t);
}
public static <T, R> List<R> map(List<T> list,Function<T, R> f) {List<R> result = new ArrayList<>();for(T s: list){result.add(f.apply(s));}return result;
}List<Integer> l = map(Arrays.asList("lambdas","in","action"),(String s) -> s.length());

Lambda及函数式接口例子

使用案例  Lambda的例子  对应的函数式接口
布尔表达式  (List<String> list) -> list.isEmpty()   Predicate<List<String>>
创建对象  () -> new Apple(10) Supplier<Apple>
消费一个对象 (Apple a) -> System.out.println(a.getWeight())

Consumer<Apple>

 从一个对象中选择/提取  (String s) -> s.length()

Function<String, Integer>或
ToIntFunction<String>

 合并两个值  (int a, int b) -> a * b  IntBinaryOperator
比较两个对象 

(Apple a1, Apple a2) ->
a1.getWeight().compareTo(a2.getWeight())

Comparator<Apple>或
BiFunction<Apple, Apple, Integer>
或ToIntBiFunction<Apple, Apple>

函数式接口异常:

任何函数式接口都不允许抛出受检异常(checked Exception。 如果需要Lambda表达式抛出异常,有两种方式:

1,定义一个自己的函数式接口,并声明受检异常:

@FunctionalInterface
public interface BufferedReaderProcessor {String process(BufferedReader b) throws IOException;
}
BufferedReaderProcessor p = (BufferedReader br) -> br.readLine();

2,把Lambda包在一个try/cache块中。

Function<BufferedReader, String> f = (BufferedReader b) -> {try {return b.readLine();}catch(IOException e) {throw new RuntimeException(e);}
};

Lambda表达式类型检查:

Lambda的类型是从使用Lambda的上下文推断出来的。上下文中Lambda表达式需要的类型成为目标类型。例如:

List<Apple> heavierThan150g = filter(inventory, (Apple a) -> a.getWeight() > 150);

类型检查过程:

1,找出filter方法的声明

2,要求它是predicate<Apple>对象的第二个正式参数。

3,Predicate<Apple>是一个函数式接口,定义了一个叫做test的抽象方法。

4,test方法描述了一个函数描述符,它可以接受一个Apple,并返回一个boolean.

5, filter的任何实际参数都匹配这个要求。

Lambda表达式类型推断:

编译器可以了解Lambda表达式的参数类型,这样可以在Lambda语法中省去标注参数类型,即:

List<Apple> greenApples = filter(inventory, a -> "green".equals(a.getColor()));

但有时显式写出类型更易读,有时候去掉更易读,需要自己权衡。

当Lambda仅有一个类型需要推断的参数时,参数名称两边的括号也可以省略。

方法引用:

参考:http://www.cnblogs.com/chenpi/p/5885706.html

什么是方法引用

  简单地说,就是一个Lambda表达式。在Java 8中,我们会使用Lambda表达式创建匿名方法,但是有时候,我们的Lambda表达式可能仅仅调用一个已存在的方法,而不做任何其它事,对于这种情况,通过一个方法名字来引用这个已存在的方法会更加清晰,Java 8的方法引用允许我们这样做。方法引用是一个更加紧凑,易读的Lambda表达式,注意方法引用是一个Lambda表达式,其中方法引用的操作符是双冒号"::"。

方法引用例子

先看一个例子

首先定义一个Person类,如下:

package methodreferences;import java.time.LocalDate;public class Person
{public Person(String name, LocalDate birthday){this.name = name;this.birthday = birthday;}String name;LocalDate birthday;public LocalDate getBirthday(){return birthday;}public static int compareByAge(Person a, Person b){return a.birthday.compareTo(b.birthday);}@Overridepublic String toString(){return this.name;}
}

假设我们有一个Person数组,并且想对它进行排序,这时候,我们可能会这样写:

原始写法

package methodreferences;import java.time.LocalDate;
import java.util.Arrays;
import java.util.Comparator;public class Main
{static class PersonAgeComparator implements Comparator<Person> {public int compare(Person a, Person b) {return a.getBirthday().compareTo(b.getBirthday());}}public static void main(String[] args){Person[] pArr = new Person[]{new Person("003", LocalDate.of(2016,9,1)),new Person("001", LocalDate.of(2016,2,1)),new Person("002", LocalDate.of(2016,3,1)),new Person("004", LocalDate.of(2016,12,1))};Arrays.sort(pArr, new PersonAgeComparator());System.out.println(Arrays.asList(pArr));}
}

其中,Arrays类的sort方法定义如下:

public static <T> void sort(T[] a, Comparator<? super T> c)

这里,我们首先要注意Comparator接口是一个函数式接口,因此我们可以使用Lambda表达式,而不需要定义一个实现Comparator接口的类,并创建它的实例对象,传给sort方法。

使用Lambda表达式,我们可以这样写:

改进一,使用Lambda表达式,未调用已存在的方法

package methodreferences;import java.time.LocalDate;
import java.util.Arrays;public class Main
{public static void main(String[] args){Person[] pArr = new Person[]{new Person("003", LocalDate.of(2016,9,1)),new Person("001", LocalDate.of(2016,2,1)),new Person("002", LocalDate.of(2016,3,1)),new Person("004", LocalDate.of(2016,12,1))};Arrays.sort(pArr, (Person a, Person b) -> {return a.getBirthday().compareTo(b.getBirthday());});System.out.println(Arrays.asList(pArr));}
}

然而,在以上代码中,关于两个人生日的比较方法在Person类中已经定义了,因此,我们可以直接使用已存在的Person.compareByAge方法。

改进二,使用Lambda表达式,调用已存在的方法

package methodreferences;import java.time.LocalDate;
import java.util.Arrays;public class Main
{public static void main(String[] args){Person[] pArr = new Person[]{new Person("003", LocalDate.of(2016,9,1)),new Person("001", LocalDate.of(2016,2,1)),new Person("002", LocalDate.of(2016,3,1)),new Person("004", LocalDate.of(2016,12,1))};Arrays.sort(pArr, (a, b) -> Person.compareByAge(a, b));System.out.println(Arrays.asList(pArr));}
}

因为这个Lambda表达式调用了一个已存在的方法,因此,我们可以直接使用方法引用来替代这个Lambda表达式,

改进三,使用方法引用

package methodreferences;import java.time.LocalDate;
import java.util.Arrays;public class Main
{public static void main(String[] args){Person[] pArr = new Person[]{new Person("003", LocalDate.of(2016,9,1)),new Person("001", LocalDate.of(2016,2,1)),new Person("002", LocalDate.of(2016,3,1)),new Person("004", LocalDate.of(2016,12,1))};Arrays.sort(pArr, Person::compareByAge);System.out.println(Arrays.asList(pArr));}
}

在以上代码中,方法引用Person::compareByAge在语义上与Lambda表达式 (a, b) -> Person.compareByAge(a, b) 是等同的,都有如下特性:

  • 真实的参数是拷贝自Comparator<Person>.compare方法,即(Person, Person);
  • 表达式体调用Person.compareByAge方法;

四种方法引用类型

静态方法引用

我们前面举的例子Person::compareByAge就是一个静态方法引用。

特定实例对象的方法引用

如下示例,引用的方法是myComparisonProvider 对象的compareByName方法;

        class ComparisonProvider{public int compareByName(Person a, Person b){return a.getName().compareTo(b.getName());}public int compareByAge(Person a, Person b){return a.getBirthday().compareTo(b.getBirthday());}}ComparisonProvider myComparisonProvider = new ComparisonProvider();Arrays.sort(rosterAsArray, myComparisonProvider::compareByName);

任意对象(属于同一个类)的实例方法引用

如下示例,这里引用的是字符串数组中任意一个对象的compareToIgnoreCase方法。

        String[] stringArray = { "Barbara", "James", "Mary", "John", "Patricia", "Robert", "Michael", "Linda" };Arrays.sort(stringArray, String::compareToIgnoreCase);

构造方法引用

如下示例,这里使用了关键字new,创建了一个包含Person元素的集合。

Set<Person> rosterSet = transferElements(roster, HashSet<Person>::new);

transferElements方法的定义如下,功能为集合拷贝,
public static <T, SOURCE extends Collection<T>, DEST extends Collection<T>>DEST transferElements(SOURCE sourceCollection,Supplier<DEST> collectionFactory) {DEST result = collectionFactory.get();for (T t : sourceCollection) {result.add(t);}return result;
}

什么场景适合使用方法引用

当一个Lambda表达式调用了一个已存在的方法

什么场景不适合使用方法引用

当我们需要往引用的方法传其它参数的时候,不适合,如下示例:

IsReferable demo = () -> ReferenceDemo.commonMethod("Argument in method.");

Lambda 和 方法引用实战

第一步,传递代码

public class AppleComparator implements Comparator<Apple> {public int compare(Apple a1, Apple a2) {return a1.getWeight().compareTo(a2.getWeight());}
}inventory.sort(new AppleComparator());

第二步,使用匿名类

inventory.sort(new Comparator<Apple>() {public int compare(Apple a1, Apple a2) {return a1.getWeight().compareTo(a2.getWeight());}
})

第三步,使用Lambda表达式

inventory.sort((Apple a1, Apple a2) -> a1.getWeight().compareTo(a2.getWeight()));

Java编译器可以根据Lambda出现的上下文来推断Lambda表达式参数的类型,所以可以改写为:

inventory.sort((a1, a2) -> a1.getWeight().compareTo(a2.getWeight()));

Comparator具有一个叫做comparing的静态方法,可以接受一个Function来提取Comparable键值,并生成一个Comparator对象,如下:

Comparator<Apple> c = Comparator.comparing((Apple a) -> a.getWeight());

所以再次改写:

inventory.sort(comparing((a) -> a.getWeight()));

第四步,使用方法引用

inventory.sort(comparing(Apple::getWeight));

复合Lambda表达式

1,比较器复合

  a,逆序

inventory.sort(comparing(Apple::getWeight).reversed())

  b, 比较器链

inventory.sort(comparing(Apple::getWeight).reversed().thenComparing(Apple::getCountry));

2, 谓词复合

  a,negate

Predicate<Apple> notRedApple = redApple.negate();

  b, and

Predicate<Apple> redAndHeavyApple = redApple.and(a -> a.getWeight() > 150);

  c, or

Predicate<Apple> redAAndHeavyAppleOrGreen = redApple.and(a -> a.getWeight() > 150).or(a -> "green".equals(a.getColor()));

3, 函数复合

  a,andThen 相当于g(f(x))

Function<Integer, Integer> f = x -> x + 1;
Function<Integer, Integer> g = x -> x * 2;
Function<Integer, Integer> h = f.andThen(g);
int result = h.apply(1); // result = 4

  b, compose 相当于f(g(x))

Function<Integer, Integer> f = x -> x + 1;
Function<Integer, Integer> g = x -> x * 2;
Function<Integer, Integer> h = f.compose(g);
int result = h.apply(1); // result = 3

转载于:https://www.cnblogs.com/IvySue/p/6737133.html

《Java 8 实战》(二)—— Lambda相关推荐

  1. 《Java 8实战》 之 Lambda

    <Java 8实战> 之 Lambda 文章目录 <Java 8实战> 之 Lambda Lambda 表达式 Lambda 长什么样子 深入理解Lambda 初始化信息 创建 ...

  2. java爬取网页数据_Python网络爬虫实战(二)数据解析

    Python网络爬虫实战 (二)数据解析 本系列从零开始阐述如何编写Python网络爬虫,以及网络爬虫中容易遇到的问题,比如具有反爬,加密的网站,还有爬虫拿不到数据,以及登录验证等问题,会伴随大量网站 ...

  3. java 拉姆表达式_Java8 lambda表达式10个示例

    Java 8 lambda表达式示例 转自importNew 原文链接 例1.用lambda表达式实现Runnable 我开始使用Java 8时,首先做的就是使用lambda表达式替换匿名类,而实现R ...

  4. Java 快速开发二维码生成服务

    点击上方蓝色"程序猿DD",选择"设为星标" 回复"资源"获取独家整理的学习资料! 来源 | 公众号「码农小胖哥」 1. 前言 不知道从什么 ...

  5. 【Java】实战Java虚拟机之五“开启JIT编译”

    今天开始实战Java虚拟机之五"开启JIT编译" 总计有5个系列 实战Java虚拟机之一"堆溢出处理" 实战Java虚拟机之二"虚拟机的工作模式&qu ...

  6. Java 函数式编程和 lambda 表达式

    为什么要使用函数式编程 函数式编程更多时候是一种编程的思维方式,是种方法论.函数式与命令式编程的区别主要在于:函数式编程是告诉代码你要做什么,而命令式编程则是告诉代码要怎么做.说白了,函数式编程是基于 ...

  7. Java Socket实战之四 传输压缩对象

    2019独角兽企业重金招聘Python工程师标准>>> 本文地址:http://blog.csdn.net/kongxx/article/details/7259834 Java S ...

  8. Java 8 实战学习笔记

    Java 8 实战学习笔记 @(JAVASE)[java8, 实战, lambda] 文章目录 Java 8 实战学习笔记 参考内容 Lambda表达式 Lambda环绕执行模式(抽离步骤) 原始代码 ...

  9. Java Socket实战之六 使用NIO包实现Socket通信

    2019独角兽企业重金招聘Python工程师标准>>> 本文地址:http://blog.csdn.net/kongxx/article/details/7288896 Java S ...

  10. Java 8 新特性 lambda表达式

    / Created by Manager on 2021/4/1. Java 8 新特性 lambda表达式 StreamAPI 新日期 新注解 */ 视频连接 1https://www.bilibi ...

最新文章

  1. java ajax datatype_理解jquery ajax中的datatype属性选项值
  2. 一句话搞定deepin中安装cuda的问题
  3. 现代密码学3.4--CPA安全,多次加密
  4. GPTEE中的Crypto API的使用
  5. python abs()函数是什么意思?
  6. C++封装、继承、多态
  7. C++primer 第 4 章 表达式 4.1基础 4 . 2 算术运算符 4 .3 逻辑和关系运算符 4 . 4 赋值运算符 4 .5 递增和递减运算符 4.6成员访问运算符
  8. CGCKD2021大会报告整理(3)--贝叶斯深度网络
  9. Redmi K50系列年后登场:最高搭载天玑9000+2K直屏
  10. 货物贸易外汇监测系统 企业版_企业能耗在线监测系统介绍
  11. currentTarget与target的区别
  12. Servlet中将JavaBean对象传递到JSP页
  13. 无线通信模块的双卡单待
  14. 电视盒子线刷固件教程B860AV2.1-A-M-T版
  15. 如何下载视频号的视频
  16. 使用Foxmail管理hotmail邮箱时,只能接收邮件而无法发送邮件的就解决办法
  17. Porting:telnet远程登录开发板、制作并烧写cramfs和ext4和ramdisk三种格式根文件系统镜像
  18. mysql中使用CONCAT()函数,TRUNCATE()函数将小数转换成百分比
  19. Java语言编写简单分数计算器
  20. 2023届 计算机毕业设计 选题 计算机专业 毕业设计题目 推荐

热门文章

  1. html显示数据库图片django,django将图片上传数据库后在前端显式的方法
  2. python实现洗牌算法_为什么渔民耶茨最有用的洗牌算法?
  3. 怎么做 慢充 话费_警惕!冒充亲友骗充话费卷土重来
  4. 两条线段相切弧_两条直线间的圆弧连接
  5. linux判断是否能上网_母亲提醒女儿:判断一个男人是否值得嫁,从这两点就能轻易看出...
  6. php模拟超级课程表,一个功能完善、UI简洁的仿超级课程表的课表控件 TimetableView...
  7. Android系统的命令行操作及C程序编译
  8. 机器学习中的算法(4.2):SVM----针对线性可分问题理解
  9. 机器学习中算法的性能评估
  10. 解决ros安装 使用roscore命令测试问题