Lambda表达式

Lambda表达式

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

匿名——我们说匿名,是因为它不像普通的方法那样有一个明确的名称:写得少而想得多!
函数——我们说它是函数,是因为Lambda函数不像方法那样属于某个特定的类。但和方法一样,Lambda有参数列表、函数主体、返回类型,还可能有可以抛出的异常列表。
传递——Lambda表达式可以作为参数传递给方法或存储在变量中。
简洁——无需像匿名类那样写很多模板代码。

基本语法

(parameters) -> expression(表达式)
(parameters) -> { statements; }(语句,注意语句的花括号)

  • 例如
  1. () -> {}
    这个Lambda没有参数,并返回void。它类似于主体为空的方法:public void run() {}。
  2. () -> "Raoul"
    这个Lambda没有参数,并返回String作为表达式(Lambda没有return语句,因为已经隐含了return)。
  3. () -> {return "Mario";}
    这个Lambda没有参数,并返回String(利用显式返回语句)。
  • 使用案例
使用案例 Lambda示例
布尔表达式 (List<String> list) -> list.isEmpty()
创建对象 () -> new Apple(10)
消费一个对象 (Apple a) -> { System.out.println(a.getWeight());}
从一个对象中选择/抽取 (String s) -> s.length()
组合两个值 (int a, int b) -> a * b
比较两个对象 (Apple a1, Apple a2) - > a1.getWeight().compareTo(a2.getWeight())
  • 用处(函数式接口)
    所以在哪能用的上Lambda呢?
    可以在函数式接口上使用Lambda表达式

    函数式接口就是只定义一个抽象方法的接口
    Java API中本来就已经有的一些函数式接口
    例如:

         public interface Comparator<T> {
              int compare(T o1, T o2);
         } 
    
        public interface Runnable{
                void run();
         } 
    

    所以函数式接口可以干什么呢?

Lambda表达式允许你直接以内联的形式为函数式接口的抽象方法提供实现,并把整个表达式作为函数式接口的实例,(具体说来,是函数式接口一个具体实现的实例)。你用匿名内部类也可以完成同样的事情,只不过比较笨拙:需要提供一个实现,然后再直接内联将它实例化。(Runnable是一个只定义了一个抽象方法run的函数式接口)

Lambda表达式允许你直接内联,为函数式接口的抽象方法提供实现,并且将整个表达式作为函数式接口的一个实例!

使用Lambda:

    Runnable r1 = () -> System.out.println("Hello World 1");

使用匿名类:

  Runnable r2 = new Runnable(){
       public void run(){
             System.out.println("Hello World 2");
        }
   }; 
  • 函数描述符
    函数式接口的抽象方法的签名基本上就是Lambda表达式的签名。我们将这种抽象方法叫作函数描述符。

    例如,Runnable接口可以看作一个什么也不接受什么也不返回(void)的函数的签名,因为它只有一个叫作run的抽象方法,这个方法什么也不接受,什么也不返回(void)。() -> void代表参数列表为空,且返回void的函数。这正是Runnable接口所代表的。

那么Lambad如何做类型检查呢?
Lambda的类型是从使用Lambda的上下文推断出来的。上下文(比如,接受它传递的方法的参数,或接受它的值的局部变量)中Lambda表达式需要的类型称为目标类型。

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

image.png
  1. 找出filter方法的声明。
  2. 要求它是Predicate<Apple>(目标类型)对象的第二个正式参数。
  3. Predicate<Apple>是一个函数式接口,定义了一个叫作test的抽象方法。
  4. test方法描述了一个函数描述符,它可以接受一个Apple,并返回一个boolean。
  5. filter的任何实际参数都必须匹配这个要求。
    注意:如果Lambda表达式抛出一个异常,那么抽象方法所声明的throws语句也必须与之匹配。

可以粗略的理解为Lambda表达式可以被赋给一个变量,或传递给一个接受函数式接口作为参数的方法,当然这个Lambda表达式的签名要和函数式接口的抽象方法一样。

类型推断
Java编译器会从上下文(目标类型)推断出用什么函数式接口来配合Lambda表达式,这意味着它也可以推断出适合Lambda的签名,因为函数描述符可以通过目标类型来得到。这样做的好处在于,编译器可以了解Lambda表达式的参数类型,这样就可以在Lambda语法中省去标注参数类型。

没有类型推断:
Comparator<Apple> c =
 (Apple a1, Apple a2) -> a1.getWeight().compareTo(a2.getWeight()); 
有类型推断:
Comparator<Apple> c =
 (a1, a2) -> a1.getWeight().compareTo(a2.getWeight()); 
  • 使用局部变量
    Lambda可以没有限制地捕获(也就是在其主体中引用)实例变量和静态变量。但局部变量必须显式声明为final,或事实上是final。换句话说,Lambda表达式只能捕获指派给它们的局部变量一次。

原因:

  1. 实例变量和局部变量背后的实现有一个关键不同。实例变量都存储在堆中,而局部变量则保存在栈上。如果Lambda可以直接访问局部变量,而且Lambda是在一个线程中使用的,则使用Lambda的线程,可能会在分配该变量的线程将这个变量收回之后,去访问该变量。因此,Java在访问自由局部变量时,实际上是在访问它的副本,而不是访问原始变量。如果局部变量仅仅赋值一次那就没有什么区别了——因此就有了这个限制。
  2. 这一限制不鼓励你使用改变外部变量的典型命令式编程模式
  • 双冒号运算符:
    Lambda及其等效方法引用的例子:
Lambda 等效的方法引用
(Apple a) -> a.getWeight() Apple::getWeight
() -> Thread.currentThread().dumpStack() Thread.currentThread()::dumpStack
(String s) -> System.out.println(s) System.out::println

构建方法引用

  1. 指向静态方法的方法引用(例如Integer的parseInt方法,写作Integer::parseInt)。
  2. 指 向 任意类型实例方法 的方法引用(例如 String 的 length 方法,写作String::length)。
  3. 指向现有对象的实例方法的方法引用(假设你有一个局部变量expensiveTransaction用于存放Transaction类型的对象,它支持实例方法getValue,那么你就可以写expensiveTransaction::getValue)。

构造函数引用
对于一个现有构造函数,你可以利用它的名称和关键字new来创建它的一个引用:ClassName::new
---摘自《Java8实战》

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 215,384评论 6 497
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 91,845评论 3 391
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 161,148评论 0 351
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 57,640评论 1 290
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 66,731评论 6 388
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 50,712评论 1 294
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,703评论 3 415
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,473评论 0 270
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,915评论 1 307
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,227评论 2 331
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,384评论 1 345
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,063评论 5 340
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,706评论 3 324
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,302评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,531评论 1 268
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,321评论 2 368
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,248评论 2 352

推荐阅读更多精彩内容

  • 前段时间一直在看lambda表达式,但是总感觉吃不透,在深入了解lambda表达式的时候,需要很多基础的知识栈。这...
    西瓜真好吃丶阅读 2,723评论 0 7
  • 简介 概念 Lambda 表达式可以理解为简洁地表示可传递的匿名函数的一种方式:它没有名称,但它有参数列表、函数主...
    刘涤生阅读 3,201评论 5 18
  • 转载自:《深入理解Java 8 Lambda(语言篇——lambda,方法引用,目标类型和默认方法)》——Luci...
    琦小虾阅读 711评论 0 5
  • 声明:本文翻译自The Java™ Tutorials(官方文档) 简述 匿名类有一个问题,如果匿名类的实现非常简...
    猴子小皮球阅读 4,652评论 0 9
  • 在我父母亲那一辈,许多都是介绍对象。所谓对眼了就结婚了。这么多年来,很多都是相忍着过日子。我小时候听过他们无数的争...
    凌澜阅读 336评论 0 0