Java lambda表达式

什么是lambda表达式

看看lambda表达式在其他语言中怎么使用的:

在python中有lambda表达式,在python中可以使用它定义匿名方法,并赋值给一个变量,然后那个变量就成为了一个方法,比如

f = lambda x:  (x + 1 ) if x > 100 else x - 1
# 相当于
def f(x):
    return (x + 1 ) if x > 100 else x - 1;

或者把lambda表达式传给一个方法,例如:

def m(fun,x):
    fun(x)
m(lambda x: print(x),1000)

由上面的示例可以看出来,lambda表达式是一段可执行的代码块,可传入方法内执行,且可以赋给任意变量由变量执行。
这其实也是支持函数式编程的特征,一段代码块可以在任何场所执行,不像java,需要借助对象这个承载来执行代码块。从python的执行方式就可以看出来,
我们可以在一个py文件中输入print 'hello world',然后就可以直接执行输出;但是在java中,你需要先创建一个类,然后创建main方法,在main方法里面执行System.out.println("hello world");
可以看出来,支持函数式编程能够减少很多的冗余代码,但是也因为灵活性使得代码的风格十分多变导致代码不好维护。
Java引入lambda表达式的目的,自然是想引入函数式编程的优点,让代码简洁,这也是lambda表达式的作用。

在java中怎么使用lambda表达式

既然知道了什么是lambda表达式,那么再来了解一下再Java中使用这种语言特性,有哪些方式又有那些限制和注意事项。

函数式接口

在Java中,lambda是基于函数式接口的,也可以理解成它是替换函数式接口的实例的匿名对象,且它只能转换成函数式接口。
那么什么是函数式接口呢?只有一个抽象方法的接口,只能有一个抽象接口,但是接口是可以包含default方法的(Java 8特性),如下的接口就是合法的:

@FunctionalInterface
interface FuncInter {
    void test();

    default void testdefault() {
        System.out.println("hello default");
    }
    // void test2(); error it is not a Functional interface
}

@FunctionalInterface
interface FuncInter2 {
    void test(String x);
}

@FunctionalInterface是一个标识注解,表明本接口只有一个抽象方法,如果再次添加抽象方法,会在编译期报错,加上此注解可以防止误加抽象方法。

函数式接口有了,怎么用呢,先看下在java8 以前,想要传入一段代码块是怎么做的。

    public static void main(String[] args) {
        // 匿名类的方法
        FuncInter2 f = new FuncInter2() {

            @Override
            public void test(String x) {
                // TODO Auto-generated method stub

            }

        };
        f.test("hello");
    }

如果不用匿名类,就需要创建一个类实现接口,再创建接口实例,最后调用test方法;

试用lambda表达式

既然说lambda表达式是用来替换函数式接口的,使用lambda的方法应该就可以减少相关代码的编写:

@Test
    public void testLambdasyntax() {

        // lambda表达式是用来替换函数式接口的(匿名)实例
        /*
         * 无参抽象方法
         */
        // 1. 单行语句
        FuncInter fi = () -> System.out.println("hello");
        fi.test();
        // 2. 多行语句使用{}包围
        fi = () -> {
            // TODO
            System.out.println("line 1");
            System.out.println("line 2");
        };
        fi.test();
        /*
         * 有参抽象方法
         */
        FuncInter2 fi2 = (String a) -> System.out.println(a);
        fi2.test("www");
        // 可以推断参数类型可以不写类型
        fi2 = (a) -> System.out.println("no type " + a);
        fi2.test("hello ");
        // 单个参数,且参数类型可以推断,可以不加括号
        fi2 = a -> System.out.println("no parentheses " + a);
        fi2.test("hello ");
    }

    /**
     * 把lambda传给方法 </br>
     * 实际上就是把函数式接口的匿名对象传给方法 </br>
     * 可以理解成把函数传递给方法,但是在java里面实际上还是基于对象
     */
    @Test
    public void deliverToMethod() {
        // 定义一个局部类
        class MyClassNeedInterface {
            FuncInter fi = null;

            MyClassNeedInterface(FuncInter fi) {
                this.fi = fi;
            }

            void doSomeThing() {
                fi.test();
            }

            void doSomeThing(FuncInter2 fi, String x) {
                fi.test(x);
            }
        }
        MyClassNeedInterface c = new MyClassNeedInterface(() -> System.out.println("This is from a lambda expression"));
        c.doSomeThing();
        String x = "a string ";
        // 等价于 c.doSomeThing(a->System.out.println(a), x);
        c.doSomeThing(System.out::println, x);
        c.doSomeThing(a -> System.out.println(a), x);
    }

}

确实简洁了很多。可以发现java中一个lambda表达式的形式是 参数、箭头、表达式,且可以根据情况省略括号或者参数类型,定义出来的表达式相当于一个接口的实例对象。
箭头后面的表达式如果是多行代码,使用花括号括起来。
实际上lambda表达式的用法,也就限于此类情况(替代函数式接口的匿名实例)。

方法引用

在示例deliverToMethod中有System.out::println这种形式的使用,是引用了已经存在的方法来填充接口的抽象方法,它支持三种形式:

  • instance::instanceMethod
  • Class::staticMethod
  • Class::instanceMethod 此种方法的调用 第一个参数是实例方法的调用者(方法的目标)

构造器引用

和上节的方法引用类似,只是引用的方法名是new。

闭包

闭包(closure)是计算机编程领域的专业名词,指可以包含自由(未绑定到特定对象)变量的代码块,子函数可以使用父函数中的局部变量。

lambda表达式使得java拥有了闭包这个特性。 简单来说,闭包是有数据的行为(函数、方法),是可以读取其他函数内部变量的函数。
lambda表达式具有代码块,那么自由变量是什么?所谓自由变量,是未绑定到特定对象的变量,那么外部的局部变量肯定是自由变量;
子函数可以使用父函数中的局部变量,也就是代码块可以访问外围的局部变量,还要能够携带出去。

@FunctionalInterface
interface Closure {
    int test(int a);
}
    @Test
    public void testClosure() {
        final int a = 100; // 这里的final可以不加但是得建立在后面不会修改的前提上,所以还是建议加上;
        Closure ci = (b) -> {
            System.out.println("I catch a int value of a :" + a);
            return (a + b);
        };
        // a month later
        int v = ci.test(1);
        System.out.println(v);
    }

在java中,在lambda中访问的外部局部变量,必须是final变量,即引用不可修改,如果是基本类型,必须是常量。
另外有一个很重要的规则,lambda表达式的体和使用这个表达式的块具有同一个作用域。所以同名变量会命名冲突
如:

@Test
    public void testSocpe() {
        int a = 1000;
        Closure closure = a -> a + 100; // ERROR
    }
    @Test
    public void testSocpe() {
        int b = 1000;
        Closure c = a -> this.add100(a);//this的作用域是在testScope方法里面
        System.out.println(c.test(b));// 1100

    }

    private int add100(int a) {
        return a + 100;
    }

什么时候使用lambda表达式

lambda表达式定义的可执行代码块,如果是需要立即执行的,可以直接写在执行方法里面,何必绕个圈子去调用接口的方法呢。所以lambda表达式的使用场景是,暂时不要执行的代码块,在未来因为某个事件触发而执行,所以重点在于延迟执行。

jdk提供了一系列的函数式接口,涵盖很多使用场景。常用的有:

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

推荐阅读更多精彩内容