java中的lambda表达式

来源:互联网 发布:男女合唱网络流行歌曲 编辑:程序博客网 时间:2024/05/29 23:48

lambda表达式在jdk1.8中被加入进来,给古老的java语言注入了新鲜的活力。先说句题外话,我看了一些早期spark(大数据计算框架)的书,当时spark支持scala,java和python,其中,spark的很多api(如map,flatmap,filter等)在scala和python中都可以使用lambda表达式轻松搞定,很简洁,无奈的java使用匿名内部类,冗长的包名,复杂的嵌套,最内侧的方法可能只是一个简单的表达式,看着实在是别扭。自从java8支持了lambda表达式以后,不仅使java增加了新的语言特性,还因此新增了一些其他api库,如并行编程和stram api等。本篇博客主要讨论lambda表达式的语法和使用,关于并行编程和stream api会在以后的博客中专门讨论。

在java中使用lambda表达式的话,需要注意两点:一个是lambda表达式本身的语法,另一个是函数式接口(functional interface)。

labmda语法

java8新增了一个操作符->,称为lambda操作符或箭头操作符,它将lambda表达式分为两部分,箭头之前的是参数,箭头之后的是动作。例如,

(Integer n1,Integer n2) -> n1 + n2

这个就相当于

Integer method(Integer n1, Integer n2) {    return n1 + n2;}

参数的类型可以省略,因为可以推断出来。

(n1,n2) -> n1 + n2

如果只有一个参数的话,圆括号也可以省略,像这样

n1 -> n1 * n1

如果没有参数的话,箭头前面的圆括号是不能省略的,例如:

() -> 5; //固定返回5

箭头符号右侧是动作,除了我们看到的这种单行的形式之外,还可以声明一个代码段作为动作,像下面这样

n -> {    return n * n;}

这个代码段可以写的很长很长。如果有返回值的话,需要使用return语句返回。

functional interface

在java中,lambda表达式一定要结合functional interface来使用,functional interface是指一个只包含一个抽象方法的接口。下面来看一个例子。这是一个functional interface:

public interface MyInterface {    int doSomething(int number);}

然后,声明一个此类型的lambda表达式

MyInterface myInterface = n -> n + 1;int number = myInterface.doSomething(5);

可以理解为,lambda表达式从语法上简化了匿名内部类,但底层的实现好像都一样。再来看一个两个参数的例子。

public interface MyInterface2 {    int doSomething(int num1, int num2);}

下面来声明一个该类型的lambda表达式

MyInterface2 myInterface2 = (n1, n2) -> n1 + n2;number = myInterface2.doSomething(5, 9);

输出的结果是14。

我还可以使用一个更加复杂的lambda表达式

myInterface2 = (n1, n2) -> {    int max = 0;    if (n1 > max) {        max = n1;    }    if (n2 > max) {        max = n2;    }    return max;};

这个lambda表达式返回n1,n2和0中较大的一个。

myInterface2.doSomething(10, 20);   // return 20myInterface2.doSomething(-10, -20); // return 0

变量捕获

说到lambda表达式,有一个话题是绕不开的,就是闭包。但这个问题在java中被简化了好多。这个问题可以分为两种情况来讨论:
1. lambda表达式可以访问到所在的类中定义的字段(filed),也可以修改这个字段。
2. lambda表达式可以访问到外层代码块(enclosing scope)中定义的本地变量(local varable),但不能修改他们,并且,如果一个本地变量在lambda表达式中被读取的话,这个变量必须是final或事实上final(变量赋值以后就不能再任何地方再修改了)。

看下面的例子

public class App {    private int filed1 = 10;    void method1() {        int varable1 = 10;        MyInterface myInterface = n -> {            filed1 += 2;  //可读取,可修改            int m = varable1;  //可读取            //varable1 += 2;  //不可修改            return 1;        };        //varable1 += 2;    //已经在lambda表达式中被读取了,就是final了,不能被修改。    }}

在这个例子中,filed1是一个字段,所以在lambdda表达式中可读可写,而varable1是一个本地变量,在lambda表达式中只能被读取而不能被修改,并且,一旦这个变量在lambda表达式中被读取了,那么在任何地方就不能被修改了。

方法引用

lambda表达式的本质是一个匿名方法,但如果有一个方法的签名(参数列表和返回值)和functional interface的签名一样并且逻辑正好是你需要的,那么你可以使用方法引用的方式来将它赋值给你的functional interface,而无需再编写lambda表达式。方法引用是jdk1.8中被引入的新语法,它跟lambda表达式息息相关,从字面意思来看,方法引用指向一个方法,但不调用它。其实这个特性在很多编程语言中都已经支持了,java也终于支持了。像其它编程语言一样,加括号就是调用方法,不加括号就是引用方法。

方法引用可以引用四种类型的方法,分别是:静态方法,实例方法,泛型方法,构造方法,引种每种方法的语法有略有区别。下面分别看个例子。下面这个类包含三个方法,对应前三种类型

public class MyClass {    public int instanceMethod(int number) {        return number * 2;    }    public static int staticMethod(int number) {        return number * 2;    }    public <T extends Number> int genericMethod(T param) {        return param.intValue() * 2;    }}方法引用的操作符是两个冒号`::`,这也是一个新的操作符。

引用静态方法

引用静态方法的语法是ClassName::methodName

myInterface = MyClass::staticMethod;myInterface.doSomething(5);

引用实例方法

应用实例方法的语法是instance::methodName

MyClass myClass = new MyClass();myInterface = myClass::instanceMethod;myInterface.doSomething(5);

应用泛型方法

MyClass myClass = new MyClass();myInterface = myClass::<Integer>genericMethod;myInterface.doSomething(5);

引用构造方法

引用构造方法的语法是ClassName::new构造方法的返回值是它所在的类的类型。下面编写一个functional interface和一个构造函数。先来一个类和它的构造函数

public class Foo {    String msg1, msg2;    public Foo(String msg1, String msg2) {        this.msg1 = msg1;        this.msg2 = msg2;    }}

在来一个functional interface

interface FooInterface {    Foo fooMethod(String m1, String m2);}

这个里面的fooMethod的返回值是Foo,参数列表是两个String对象,与Foo类的构造函数的签名一致。下面是引用这个构造方法的方式

FooInterface fooInterface = Foo::new;Foo fooObj = fooInterface.fooMethod("hello", "world");
原创粉丝点击