Java学习者论坛

 找回密码
 立即注册

QQ登录

只需一步,快速开始

手机号码,快捷登录

恭喜Java学习者论坛(https://www.javaxxz.com)已经为数万Java学习者服务超过8年了!积累会员资料超过10000G+
成为本站VIP会员,下载本站10000G+会员资源,购买链接:点击进入购买VIP会员
JAVA高级面试进阶视频教程Java架构师系统进阶VIP课程

分布式高可用全栈开发微服务教程

Go语言视频零基础入门到精通

Java架构师3期(课件+源码)

Java开发全终端实战租房项目视频教程

SpringBoot2.X入门到高级使用教程

大数据培训第六期全套视频教程

深度学习(CNN RNN GAN)算法原理

Java亿级流量电商系统视频教程

互联网架构师视频教程

年薪50万Spark2.0从入门到精通

年薪50万!人工智能学习路线教程

年薪50万!大数据从入门到精通学习路线年薪50万!机器学习入门到精通视频教程
仿小米商城类app和小程序视频教程深度学习数据分析基础到实战最新黑马javaEE2.1就业课程从 0到JVM实战高手教程 MySQL入门到精通教程
查看: 476|回复: 0

[Java基础知识]解读Java8之lambda表达式

[复制链接]
  • TA的每日心情
    开心
    2021-3-12 23:18
  • 签到天数: 2 天

    [LV.1]初来乍到

    发表于 2014-9-30 17:39:00 | 显示全部楼层 |阅读模式
    Java8最值得期待的就是lambda表达式了,本文就将带你体验lambda表达式,并进行比较深入的解析。

    体验lambda表达式 好了,我们开始体验java8的新特性-lambda表达式吧!现在我们的匿名类可以写成这样子了:
      new Thread(() -> {
          System.out.println("Foo");
        }).start();

    而之前的写法只能是这样子:
      new Thread(new Runnable() {
         @Override
         public void run() {
            System.out.println("Foo");
         }
       }).start();  
      
       
       

         
       

         
       
      

       这样一看,我们似乎就是匿名类写起来简单了一点啊?而第二种方法,借助便捷的IDE,好像编写效率也没什么差别?博主开始也是这样认为,仔细学习之后,才知道其中的奥妙所在! 这里有一个重要的信息,就是()->{}这里代表一个函数,而非一个对象。可能这么说比较抽象,我们还是代码说话吧:
       public class LambdaTest {
        private static void bar(){
          System.out.println("bar");
       }
       public static void main(String[] args) {
           new Thread(LambdaTest::bar).start();
      }
    }

    看懂了么?这里LambdaTest::bar代表一个函数(用C++的同学笑了),而new Thread(Runnable runnable)的参数,可以接受是一个函数作为参数! 是不是觉得很神奇,颠覆了Java思维?在剖析原理以前,博主暂且卖个关子,我们先来讲讲什么是lambda表达式。 什么是lambda表达式 lambda表达式的由来
         絮叨几句,现代编程语言的lamdba表达式都来自1930年代初,阿隆佐・邱奇(Alonzo Church)提出的λ演算(Lambda calculus)理论。λ演算的核心思想就是“万物皆函数”。一个λ算子即一个函数,其一般形式是λx.x + 2。一个λ算子可以作为另一个λ算子的输入,从而构建一个高阶的函数。λ演算是函数式编程的鼻祖,大名鼎鼎的编程语言Lisp就是基于λ演算而建立。用过Lisp的应该都清楚,它的语法很简单,但是却有包容万物的能力。    可能搞计算机的对邱奇比较陌生,但是提起和邱奇同时代的另外一个人,大家就会觉得如雷贯耳了,那就是阿兰・图灵。邱奇成名的时候,图灵还是个大学生。邱奇和图灵一起发表了邱奇-图灵论题,并分别提出了λ演算和图灵机,加上哥德尔提出的递归函数一起,在理论上确定了什么是可计算性。至于什么是可计算性,其实博主也说不清楚,但是现代所有计算机程序语言,都可以认为是从三种之一发展而来,并与之等价的。仅此一点,其影响深远,可想而知。当年教我们《计算理论》的是一个德高望重的教授,人称宋公,每次讲到那个辉煌的年代,总是要停下来,神情专注的感叹一句:“伟大啊!”想想确实挺伟大,人家图灵大学时候就奠定了现代计算机的基础,而我们那会大概还在打DOTA… 附上大神们的照片,大家感受一下:



    现代编程语言中的lambda表达式 好了扯远了,神游过了那个伟大的时代,我们继续思考如何编代码做需求吧… 现代语言的lambda表达式,大概具备几个特征(博主自己归纳的,如有不严谨,欢迎指正):
    (1) 函数可作为输入;
    (2)函数可作为输出;
    (3)函数可作用在函数上,形成高阶函数。
    (4)函数支持lambda格式的定义。

       其实有了1、2,3也就是顺水推舟的事情,而4其实没有太大的必要性,因为一般语言都有自己的函数定义方式,4仅仅是作为一种补充。当然实现了4的语言,一般都会说:“你看我实现了lambda表达式!”(望向Java8和python同学) 在Java8中使用lambda表达式 FunctionalInterface
        Java中的lambda无法单独出现,它需要一个接口来盛放。这个接口必须使用@FunctionalInterface作为注解,并且只有一个未实现的方法。等等,什么叫接口中未实现的方法?难道接口中还可以有已实现的方法?恭喜你,猜对了!Java8的接口也可以写实现了!是不是觉得Interface和AbstractClass更加傻傻分不清楚了?但是AbstractClass是无法使用@FunctionalInterface注解的,官方的解释是为了防止AbstractClass的构造函数做一些事情,可能会导致一些调用者意料不到的事情发生。 好了,我们来看一点代码,Runnable接口现在变成了这个样子:
      @FunctionalInterface
      public interface Runnable {
         public abstract void run();
      }
    这里我们可以将任意无参数的lambda表达式赋值给Runnable:
        Runnable runnable = () -> {
          System.out.println("Hello lambda!");
       };
      runnable.run();

    lambda表达式本质上是一个函数,所以我们还可以用更加神奇的赋值:   public class HelloLambda {
        private static void hellolambda() {
          System.out.println("Hello lambda!");
       }
       public static void main(String[] args) {
         Runnable runnable = HelloLambda::hellolambda;
          runnable.run();
       }
      }
        这里看到这里,大家大概明白了,lambda表达式其实只是个幌子,更深层次的含义是:函数在Java里面可以作为一个实体进行表示了。这就意味着,在Java8里,函数既可以作为函数的参数,也可以作为函数的返回值,即具有了lambda演算的所有特性。 Function系列API
         看到这里,可能大家会有疑问?什么样的函数和什么样的lambda表达式属于同一类型?答案是参数和返回值的类型共同决定函数的类型。例如Runnable的run方法不接受参数,也没有返回值,那么Runnable接口则可以用任意没有参数且没有返回值的函数来赋值。这样概念上来说,Runnable表示的含义就从一个对象变成了一个方法。 这一点在Java8中的java.util.function包里的代码得到了验证。以最具有代表性的Function接口为例:
       @FunctionalInterface
       public interface Function<T, R> {
          R apply(T t);
       }
    有了Function,我们可以这样写:
       Function<Integer,String> convert = String::valueOf;
       String s = convert.apply(1);
    这个东东是不是很像Javascript中的函数对象?    可惜的是,这里的Function算是个半成品,它只能表示一个有单个参数,并有非void返回值的函数。像System.out.println()这种方法,因为返回值为void,是无法赋值为Function的! 怎么办?java.util.function包提供了一个不那么完美的解决方案:多定义几个FunctionalInterface呗! 于是,在Java8里有了:
       Supplier: 没有参数,只有返回值的函数
       Consumer: 一个参数,返回值为void的函数
       BiFunction: 两个参数,一个返回值的函数
       BiConsumer: 两个参数,没有返回值的函数
       …
    对于这些个API,我也没有什么力气吐槽了,反正我也想不出更好的方法…大家趁机,多学几个单词吧,嗯。
        System.out.println(String)现在是Consumer 了,String.valueOf(Integer)现在是Function 了,Collection.size()现在是Supplier 了…。要为一些较长参数的方法获取一个身份,也是挺容易的(定义一个新的FunctionInterface接口)。 我相信这个影响是深远的。例如下面一段代码,可以同一行代码将一个List 转换成一个List :
       List<String> strings = intList.stream().map(String::valueOf).collect(Collectors.<String>toList());

         当然问题也存在。因为包含了闭包等因素,FunctionInterface的序列化/反序列化会是一个相当复杂的事情。熟悉Java的开发者,也会因为lambda的引入,带来了一些困惑。俗话说活到老学到老,我倒是不介意这个新功能,你说呢?


      
      
       
       

         
       

         
       
      
    复制代码
    回复

    使用道具 举报

    您需要登录后才可以回帖 登录 | 立即注册

    本版积分规则

    QQ|手机版|Java学习者论坛 ( 声明:本站资料整理自互联网,用于Java学习者交流学习使用,对资料版权不负任何法律责任,若有侵权请及时联系客服屏蔽删除 )

    GMT+8, 2024-5-20 16:43 , Processed in 0.376666 second(s), 52 queries .

    Powered by Discuz! X3.4

    © 2001-2017 Comsenz Inc.

    快速回复 返回顶部 返回列表