博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
Lambda表达式(Java)
阅读量:7076 次
发布时间:2019-06-28

本文共 5985 字,大约阅读时间需要 19 分钟。

我们都知道Java 8 支持Lambda表达式,但是平时开发中也很难用到这个东东,但是作为专业的程序员,技多不压身(其实我是在学Kotlin中,发现里面大量的运用到了Lambda表达式,看的我一脸懵逼,所以只好来学习学习,不然怎么出去装逼,怎么骚浪贱)。好,收,让我们来看看Lambda的前世今生。

一、Lambda到底是什么鬼?

说到Lambda,不得不说到函数式编程,说到函数式编程不得不说到λ演算,是数学家Church提出的。λ演算中最关键的要素就是函数被当作变量处理能够参与运算。(看到这里我社会大佬,才是这些数学家好嘛,打call,打call)。

继续继续,我们来看看Lambda的官方定义。

A lambda expression is like a method: it provides a list of formal parameters and a body - an expression or block - expressed in terms of those parameters. 翻译如下:Lambda表达式类似于一种方法:它提供了一个形参列表和一个表达式或用这些形参的代码块。

我去。反正官方的解释我是一点都没有看懂。个人觉得Lambda表达式更像是“语法糖”, 不仅使代码变的更加简洁,还是代码更有阅读性。说了这么多,无码无真相。让我们来探讨探讨Lambda表达式。开车开车,请上车~~~

1.1 Lambda表达式分析

那在Java中如果我们要给一个变量赋值,我们一般的操作如下:

int a = 1; String b = "abc"; boolean c = true;复制代码

但是如果我们根据λ演算要素,将函数当做变量处理,将函数赋值给一个变量。

addMethod = public void add(int a,int b){	 return a+b; }复制代码

哇,这代码真是蛇皮,这与我们实际编码中书写的Lambda表达式代码完全不一样好嘛,对啊!作为一个程序员,我们万事都要讲究一个优雅好嘛,不优雅,你就是要我命,你造否?所以为了代码简洁与优雅。Sun公司的大佬们移除了一些不必要的声明。我们一步一步的来分析。

1.1.1 去除函数修饰符
addMethod = void add(int a,int b){	return a+b;}复制代码

上面操作,我们发现了我们移除了public 修饰符,设想,如果我们需要将一个函数赋值给一个变量,那么起访问权限控制的,肯定是变量的修饰符,所以说函数的修饰符可以移除。我们继续往下走。

1.1.2 去除函数名称
addMethod = void (int a,int b){	return a+b;}复制代码

去除函数名称,主要的原因是,既然我已将函数赋值给变量了。那所有的操作都是与这个变量相关,所以函数的名称是可以不要的。对实际的操作没有影响。

1.1.3 去除函数返回类型
addMethod = (int a,int b){	return a+b;}复制代码

为什么会去掉函数返回类型呢?函数内部到底有没有返回值,你觉得编译器心里难道没有一点B数嘛,所以我们完全可以省略这个返回类型。

1.1.4 去除参数类型
addMethod = (a, b){	return a+b;}复制代码

去掉参数类型,同1.1.2原因,编译器知道传入的参数类型。所以可以省略。

1.1.5 参数与函数体区分
addMethod = (a, b)->{	return a+b;}复制代码

为了更好的将参数与函数体分开。Java中的语法是使用**"->"**来区分,这些代码看起来更简洁。

1.2 Lambda表达式对应变量类型

到现在为止,我们已经成功的将一个函数赋值给一个变量。这个时候我们需要理解的是这个变量的具体类型是什么,我们都知道我们声明变量的时候,前方都会声明其类型。那这个变量的类型到底是什么呢?

在Java8中,Lambda表达式所对应的类型都是接口,Lambda所表达的就是那个接口对应函数的具体代码。可能大家这里不是很清楚。概念还是很模糊。我们看一下下面的例子,我相信大家就能清楚的明白了。

//声明一个Person接口  public interface Person {        void smile(int time);    } //在java8以前我们实现接口 Person p = new Person() {            @Override            public void smile(int time) {                System.out.println("我笑了" + time + "秒");            }        }; //通过Lambda实现接口Person p = time -> System.out.println("我笑了" + time + "秒");复制代码

观察上述代码,我们发现。通过Lambda表达式来实现接口,相比之前的实现接口的方式,Lambda表达式是代码更加清晰,且代码量较少。

1.2.1 Lambda表达式表达的方法个数。

说到这里,我相信大家都有一个疑问,就是假如Person接口中不止有一个smile方法,还有其他方法呢?那这个时候我该怎么用Lambda表达式来表示呢? 这个问题其实在最初的Lambda表达式分析那里已经侧面表现了。既然是将函数赋值给变量,那么一个变量对应的函数就只能有且只有一个,不然怎么知道你在调用的时候具体调用的哪个函数呢?你说呢,兄die.

说到这里,有的兄die就会说,我怎么才能让我的接口有且只有一个方法呢?,那就引出我们的**@FunctionalInterface**注解,直接通过英文直译的话意思是“函数式接口”。我去,什么鬼,不了解其意思,我们就直接看源码吧。

An informative annotation type used to indicate that an interface * type declaration is intended to be a functional interface as * defined by the Java Language Specification. * * Conceptually, a functional interface has exactly one abstract * method.  Since {@linkplain java.lang.reflect.Method#isDefault() * default methods} have an implementation, they are not abstract.  If * an interface declares an abstract method overriding one of the * public methods of {@code java.lang.Object}, that also does * not count toward the interface's abstract method count * since any implementation of the interface will have an * implementation from {@code java.lang.Object} or elsewhere.  @Documented@Retention(RetentionPolicy.RUNTIME)@Target(ElementType.TYPE)public @interface FunctionalInterface {}复制代码

这里我截取了部分FunctionalInterface的部分注释,@FunctionalInterface 注解主要是用于修饰接口。且修饰的接口中有且只有一个方法,到这里我们就明白了,那么我可以用**@FunctionalInterface**注解来修饰我们的接口啦。这个接口是不是很牛逼啊。

二、Lambda表达式语法

在了解Lambda的由来,以及表现形式后,我们现在可以来了解一下Lambda的语法啦,知道原理,当然也必须知道怎么使用,对吧。(授人鱼不如授人以渔,你说是不是这个道理,哈哈哈,秀秀我的语文功底)。

  • Lambad表达式总是在花括号中;
  • 其参数(如果有的话)在 —>之前声明(参数类型可以省略);
  • 函数体(如果存在的话)在—>之后声明;

基本语法:

(formal parameter list) -> { expression or statements}复制代码

2.1 Lambda表达式例子

Lambda 表达式实际上是一种匿名方法实现(有的人说是匿名内部类实现,看个人怎么理解了),Lambda表达式的求值不会导致函数体的执行,而是在调用该方法后发生,下面是一些Lambda表达式的例子:

() -> {}                //无参,空的函数体⑤        () -> 42                //无参,函数体返回42⑥        () -> null              // 无参,函数体返回null⑦        () -> { return 42; }    //  无参,函数体返回42        () -> { System.gc(); }  // 无参,执行语句,函数体返Void        () ->  System.gc()  // 无参,执行语句,函数体返Void        () -> {                 // 带返回语句的完整函数体            if (true) return 12;            else {                int result = 15;                for (int i = 1; i < 10; i++)                result *= i;                return result;            }        }        (int x) -> x+1              // 声明类型的单个参数⑧        (int x) -> { return x+1; }  // 声明类型的单个参数        (x) -> x+1                  // 隐藏参数类型的单个参数③        x -> x+1                    // 花括号可选④                (String s) -> s.length()      // 声明类型的单个参数        (Thread t) -> { t.start(); }  // 声明类型的单个参数        s -> s.length()               // 隐藏参数类型的单个参数        t -> { t.start(); }           // 隐藏参数类型的单个参数⑩        (int x, int y) -> x+y  // 声明参数类型的多个参数①        (x, y) -> x+y          // 隐藏参数类型的多个参数②        (x, int y) -> x+y    // 错误:不能同时单个的声明类型或隐藏类型        (x, final y) -> x+y  // 错误:没有推断类型的修饰符复制代码

相信大家从上面例子中可以看出一下语法规则,那我们将这语法规则分为两个部分Lambda参数规则lambda函数体规则

2.2 Lambda参数规则

  • 参数列表必须是以逗号分隔的形式。见①
  • 参数列表的参数类型是可选的,如果未指定参数类型,将从上下文进行推断。见②
  • 参数列表参数的个数大于2则必须用小括号括起来 。见②
  • 参数列表参数的个数为1,则小括号可以省略。见⑩
  • 如果函数体中,没有使用参数,那么必须指定空括号。见⑤⑥⑦

2.3 Lambda函数体规则

  • 如果函数体只包含单条表达式或语句,就不需要使用大括号。见⑧⑨

三、 Lambda使用注意事项

现在我们基本了解了Lambda的使用方式,已经使用场景,那么现在我们看看在Lambda使用中需要注意的问题。

3.1 Lambda访问变量为final类型

我们都知道在匿名内部类中访问变量时,需要将变量修饰为final类型。因为如果该该内部类运行在另一线程中,必将会出现线程安全的问题。(这里肯定有很多读者就会提出这样一个问题,那这个内部类修饰变量为final和我们Lambda表达式有毛关系啊?)请听我细细道来。

如果你仔细阅读了该文,你应该知道Lambda表达式相当于声明匿名内部类。只是换了一种表达方式而已。那么本质是一样的。所以在访问变量的时候我们也需要注意这些问题。看下面代码。

public interface A {        void fuck();    }	public void test() {        int count = 10;        A a = () -> { count++};//错误 count 类型为final类型。    }复制代码

观察上述代码,大家肯定会疑惑,为什么我这里没有申明count为final类型。因为在java8中,在编译器编译的时候会将Lambda表达式访问的变量,自动声明为final类型。有点语法糖的味道。

总结

通过上文分析。我们发现Lambda表达式不仅使程序变得更加简洁。还能节省程序员的代码量,节省了很多码代码的时间。再有一个我觉得更重要的是,这样代码看起来很骚,你说是吗?

参考

站在巨人的肩膀上。可以看得更远。感谢下列博主,和官方文档。人类的知识是大家的。

最后,附上我写的一个基于Kotlin 仿开眼的项目(ps: 其实在我之前,已经有很多小朋友开始仿这款应用了,但是我觉得要做就做好。所以我的项目和其他的人应该不同,不仅仅是简单的一个应用。但是,但是。但是。重要的话说三遍。还在开发阶段,不要打我),欢迎大家follow和start

转载地址:http://dpcml.baihongyu.com/

你可能感兴趣的文章
MySQL各版本的性能特性(从4.0版本开始)
查看>>
linux包之bash之内置命令ulimit
查看>>
HBase与Zookeeper数据结构查询
查看>>
QQ等软件可以联网 网页打不开
查看>>
c++ 使用socket实现C/S端文件的下载传输
查看>>
JMF获取设备列表失败,获取视频设备失败?
查看>>
国内 Mono 相关文章汇总
查看>>
Python模块学习 ---- datetime
查看>>
MS SQL Server Quarter Function
查看>>
linux日志(常用命令)
查看>>
history
查看>>
Leetcode: Arranging Coins
查看>>
HttpUtil 【判断网络连接的封装类】
查看>>
【转】TCP分段与IP分片
查看>>
iOS 多线程 NSOperation、NSOperationQueue
查看>>
delphi执行查询语句时的进度条怎么做
查看>>
CF 335A(Banana-贪心-priority_queue是大根堆)
查看>>
python的memcache使用如果对key设置了一个int型
查看>>
Leetcode: Longest Substring with At Most Two Distinct Characters
查看>>
173. Binary Search Tree Iterator
查看>>