学习 Java8 函数式编程 (二)

和 Lambda 表达式 Say Hello

如果用一大段枯燥的文字去解释一个我们并不熟悉的概念,我觉得和看天书并无区别。我之所以选择学编程,就是因为没有什么是写段代码不能搞定的。那么废话少说,大家先看看清单一中的代码。

清单一

public class Calculater {

    public static void main(String[] args) {
        final int a = 1, b = 2;
        int result = add(new IIntegerMath() {
            @Override
            public int operation() {
                return a + b;
            }
        });

        System.out.println(result);
    }

    public static int add(IIntegerMath iIntegerMath) {
        return iIntegerMath.operation();
    }

}

interface IIntegerMath {

    int operation();

}

看完清单一中的代码,估计有人要说我了,因为这段代码在上一篇博客中已经出现过了。但这又何妨,通过一段简单的代码,我们可以挖掘很多知识。在清单一代码的 main 函数中,我调用了 add 函数,并使用了一个匿名内部类作为 add 函数的参数。匿名内部类被设计的目的之一就是,方便程序员将代码作为数据传递。

现在问题来了,大家有没有觉得这样的代码太过于冗余了。我们明明只需要 a + b 这一条语句,却附加了很多其他的代码(命令式代码)。可能由于大家已经习惯了这样的写法,但 Java8 让我们可以用更加简单的代码实现相同的功能。那么,我们一起来看清单二中用** Java8** 实现地与清单一功能相同的代码。

清单二

public class Calculater {

    public static void main(String[] args) {
        int a = 1, b = 2;
        int result = add(() -> a + b);

        System.out.println(result);
    }

    public static int add(IIntegerMath iIntegerMath) {
        return iIntegerMath.operation();
    }

}

interface IIntegerMath {

    int operation();

}

如果有人认为清单二中的代码看着不爽,那么我建议他可以去泡个澡,然后剪个头发。其实清单二中的代码我已经在上篇博客中展示了,但我并没有解释 () -> a + b 是几个意思。

我现在给大家分析下 () -> a + b ,其实这段代码就是一个 Lambda 表达式,也可以理解为一个函数。-> 将参数和 Lambda 表达式的主体分割开了,-> 的左边是参数所在的位置,() 表示无参数;-> 右边的代码是 Lambda 表达式的主体。

即使我们知道了 add 函数中那段代码是什么意思,我认为有些人对于在 add 函数中直接传入一个 Lambda 表达式还是难以理解。那么现在我用另一种方式重写清单二中 main 函数中的代码,请看清单三。

清单三

public static void main(String[] args) {
        int a = 1, b = 2;
        IIntegerMath integerMath = () -> a + b;
        int result = add(integerMath);

        System.out.println(result);
}

我相信清单三中的代码对于大家来说都很熟悉。当你无法理解使用 Lambda 表达式作为函数参数的用法,你们就将 Lambda 表达式理解为一个对象的引用,虽然按理来说我们不能这么来理解。

Lambda 表达式的多种形式

不带参数的 Lambda 表达式

清单四

public class LambdaLearn {
    public static void main(String[] args) {
        INoArguments noArguments =
            () -> System.out.println("no argument");
    }
}

interface INoArguments {
    void printOperation();
}

清单四中展示了一个不带参数的 Lambda 表达式,在 -> 的左边使用空括号 () 代表没有参数

带一个参数的Lambda表达式

清单五

public class LambdaLearn {
    public static void main(String[] args) {
        IOneArguments<Integer> oneArguments =
            (a) -> a > 0;
    }
}

interface IOneArguments<T> {
    boolean assertOneNum(T argument);
}

清单五展示了一个只带一个参数的 Lambda 表达式,因为只有一个参数,所以参数可以用括号包裹起来,也可以不用。

带多个参数的 Lambda 表达式

清单六

public class LambdaLearn {
    public static void main(String[] args) {
        IMultiArguments<Integer> multiArguments =
            (a, b) -> a + b;
    }
}

interface IMultiArguments<T> {
    T addOperation(T a, T b);
}

清单六中展示了一个带多个参数的 Lambda 表达式。因为有多个参数,所以需要用括号将多个参数包裹起来。

注意:我不能用惯性思维去阅读清单六中 Lambda 表达式。该 Lambda 表达式并不是将两个数字相加,而是创建了一个函数,用来计算两个数字相加的结果。变量 multiIArguments
的类型是 IMultiArguments<Integer>,它不是两个数字相加的和,而是将两个数字相加的那行代码。

主体用被{}包裹的 Lambda 表达式

清单七

public class LambdaLearn {
    public static void main(String[] args) {
          INoArguments multiStatement = ()  ->  {
            System.out.println("this is the first code");
            System.out.println("this is the second code");
        };
        
        INoArguments oneStatement = ()  ->  {
            System.out.println("only one code");
        };
    }
}


interface INoArguments {
    void printOperation();
}

清单七中展示了,如果 Lambda 表达式的主体有多行代码,那么就需要将多行代码用**中括号 {} **包裹。其实当 Lambda 表达式的主体只有一行代码的时候,大家可以根据自己的习惯决定是否使用中括号。

通过 Lambda 表达式来看 Java8

既成事实地final变量

在我们学 Java 基础的时候,我们就知道匿名内部类只能引用外部的 final 变量。但我们却发现,被 Lambda 表达式引用的外部变量并没有被 final 修饰。如清单八中的代码所示,被 Lambda 表达式引用的外部变量 a 和 b 并诶有被 final 修饰,这是因为 Java8 为我们省去了一些操作,这样代码看上去会更加干净舒服。虽然 a 和 b 没有被显示地被 final 修饰,但它们依然是事实上的 final 变量。你们可以根据自己的喜好,选择性地给被 Lambda 表达式引用的外部变量加上 final 修饰符。

清单八

public static void main(String[] args) {
        int a = 1, b = 2;
        IIntegerMath integerMath = () -> a + b;
        int result = add(integerMath);

        System.out.println(result);
}

类型推断

不知大家是否有注意,本文中使用的 Lambda 表达式都没有为参数指明类型,这是因为 Java8 引入了比 Java7 更加强大的目标类型推断。如清单九中的代码所示, Lambda 表达式的参数 x 并没有被指明类型,但 javac 会根据变量 atLeast 的类型 Predicate<Integer> 推断出目标类型。在日常的开发中,请大家根据具体情况选择是否给 Lambda 表达式的参数显示地指明类型

清单九

Predicate<Integer> atLeast = x -> x > 5;

interface Predicate<Integer> {
    boolean test(T t);
}

彩蛋

其实我的微信里有关注很多技术公众号,但真正喜欢并经常阅读的却寥寥无几,其中刘欣大神的码农翻身就是我非常喜欢的一个公众号。刘欣大哥是一个有 15 年工作经验的前 IBM 架构师,他是一个热爱编程的资深码农,他用心去写好每一篇博客,他的每篇博客都是一个故事,他用一个个精彩短小的故事解释有点枯燥的技术。下面是他公众号的二维码,请关注他,你们会收获很多。

码农翻身
码农翻身

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 158,560评论 4 361
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 67,104评论 1 291
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 108,297评论 0 243
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 43,869评论 0 204
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 52,275评论 3 287
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 40,563评论 1 216
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 31,833评论 2 312
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 30,543评论 0 197
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 34,245评论 1 241
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,512评论 2 244
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 32,011评论 1 258
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,359评论 2 253
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 33,006评论 3 235
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 26,062评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,825评论 0 194
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 35,590评论 2 273
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 35,501评论 2 268

推荐阅读更多精彩内容