java 8中的lambda表达式

JAVA ginotang 855℃ 0评论

Lambda表达式简介

lambda表达式是JDK8新增的功能,可在一定程度上简化程序的代码。我们都知道,java在lambda表达式出现之前,它是一个纯面向对象的编程语言,lambda表达式的出现可以理解为java8对函数式编程的特定支持。lambda表达式由两个部分组成,它们分别是lambda表达式的声明和定义。声明部分的学术名称叫函数式接口,定义部分就是这个接口的实现。

最简单的lambda表达式

有一定编程经验的人可能会经常见到如下的代码

实际上这一行简单的代码就是函数式接口的实现,它由两个部分组成,第一个部分是箭头左边的参数列表,多个参数可用逗号( , )分隔;第二个部分是右边的表达式。上述lambda表达式的作用是对给定的参数n加一后返回。

当参数只有一个的时候,可把括号省略:

什么是函数式接口

lambda表达式也是一种类型。例如Integer,String都是一种类型,类型的使用一般都会经过声明和赋值,例如:

n -> n+1 可以理解为lambda表达式的赋值部分,处于等号左边的就是函数式接口,也就是说,函数式接口是lambda表达式的类型定义。

函数式接口的创建

函数式接口和普通的java接口几乎一样,不同的是,函数式接口里面只能定义一个方法。例如:

接口中的方法接受一个int类型的参数,返回值也是int。@FunctionalInterface注解不是必须的,添加这个注解有助于我们在创建接口的时候就可以发现错误,例如接口里面添加了两个方法。创建了接口之后,就可以像普通对象一样创建lambda表达式:

函数式接口并不要求方法的实现是唯一的,只要它们的实现是兼容的,就可以多次实现接口的方法(和变量的多次赋值类似)。兼容是指方法签名一样,具体的实现可以不一样:

在第二个实现中,和第一个实现一样都是接受一个参数且返回int类型,但是第二个实现中是把参数乘2。

类型推断

一般情况下,我们不需要对lambda表达式的参数类型进行显式声明,编译器可以根据实际情况对参数的类型进行推断。当然你写上类型也不会错,只会显得多此一举:

如果有多个参数,一旦显式声明了其中一个参数的类型,那么必须为所有的参数声明类型:

lambda表达式的两种类型

这两种类型分别是:

  • 只有一行代码的lambda表达式
  • 具有多行代码的lambda表达式

一行代码的表达式已经介绍过,具有多行代码的表达式叫做块表达式,代码使用{}括起来。和单行的lambda表达式不同,块表达式的return关键字不能省略,例如:

变量捕获

lambda表达式可以访问其外层作用域内定义的变量,这个被访问的变量就是被捕获的变量。这个时候实际上形成了其他语言中的闭包。后果是被捕获后的变量会自动转变为final。

正确的代码:

错误的代码:

变量捕获

当i在lambda表达式中使用,i就自动变为final,任何时候对i的修改都是错误的。

之所以有这个限制,是为了线程安全着想。试想一下,如果多个线程可以随意对一个变量进行修改,那么被修改的变量最后是一个什么值?其糟糕程度是无法想象。

泛型函数式接口

当lambda表达式操作的参数只有类型不同的时候,使用泛型函数式接口可以大大减少程序的代码量,泛型函数式接口类似java的方法重载。

例如前面的add方法,除了操作int类型外,我们还想操作double类型,那么,使用泛型函数式接口是一个好办法。

接口的实现:

作为参数传递

lambda表达式作为函数的参数来传递是最常见的需求。因为lambda表达式是一种类型,自然地,它就可以作为参数来传递。

方法引用

方法引用可以使我们创建的函数式接口和实现相分离。也就是说,接口是我们创建的,但它的实现可以不是我们自己写的,而是通过用现有的方法为它赋值的方式来实现。当然,这个现有的方法必须和接口中的方法兼容。JDK8定义了一个新的操作符来使用方法引用,它就是 ::

方法引用有三种方式,分别是:

  • 类::静态方法
  • 对象::实例方法
  • 类::实例方法

类::静态方法

下面的例子使用的是类::静态方法

从上面的例子可以看到,我们并没有实现lambda表达式,只是在需要的时候把和接口兼容的方法传递过去。

对象::实例方法

对象::实例方法和类::静态方法基本一样,不同的只是传递方法之前要把源对象先实例化出来,还是上面的例子:

类::实例方法引用

这种方式的引用好处是无需要实例化对象,就可以对类的任意对象方法进行引用。不过需要注意的是,对于使用方法引用的函数中,该函数的第一个参数的类型必须与这个类一致,因为该实例方法在运行时由第一个参数执行。这段话可能难以理解,但结合下面的代码来看,就很好理解。

例如:

Arrays.sort方法就是使用方法引用的函数,它的第一个参数是stringsArray,类型是String[],和后面的方法引用类型不一致。不过,由于sort方法中使用了循环,每次处理的对象就是String类型的,和String::compareToIgnoreCase中的String类型一致。

 

 

转载请注明:Pure nonsense » java 8中的lambda表达式

喜欢 (0)
0 0 投票数
文章评分
订阅评论
提醒
guest
0 评论
内联反馈
查看所有评论
0
希望看到您的想法,请您发表评论x
()
x