java8-01-lambda

[TOC]

0 lambda的传说

其实我也没法说清楚他到底是什么鬼……

就好比你没见过某种颜色,我再怎么描述都没法描述清楚,还是亲自看看他长啥样吧


还记得中学的时候,数学里经常出现的那个符号吗?

先放一个从百度图库偷来的图片(CSDN打上的水印,不关我事哈……)

lambda

就是这货,此处的lambda 就是它……

传说中这玩意儿是比程序员还疯狂的数学家的口头禅……

上面的废话都看完了????

虽然见到了他,但是还是不清楚他到底是什么……

好了,不扯皮了,开讲啦


  • 百科里是这么描述的:

Lambda 表达式”是一个匿名函数,可以包含表达式和语句,并且可用于创建委托或表达式目录树类型。

看百科的描述好像有那么点感觉了。可是干嘛非要搞个这么高冷的解释呢?

  • 个人是这么理解的:

lambda 类似于一个可调用的代码块或者游离函数。当然也可以有入参。

1 瞄一眼他长啥样?

  • 示例

比如下面这样:

Comparator<Integer> c = (i, j) -> Integer.compare(i, j);

等价的原始Java代码长这样:

Comparator<Integer> c = new Comparator<Integer>() {
  @Override
  public int compare(Integer o1, Integer o2) {
    return Integer.compare(o1, o2);
  }
};

用上lambda是什么体验?

  • 代码量好像少了
  • 逼格好像提高了
  • 代码好像更优雅了

2 lambda各种外观

至少在Java里lambda的爸爸妈妈姐姐弟弟爷爷奶奶七大姑八大姨……可能都是长这个样子的。

同时,据老夫多年的断案经验来推断,lambda的本尊应该也是这个样子的:

(Type1 param1,Type2 param2, ...) -> {
  // 一些乱七八糟、乌漆嘛黑的处理操作();
  return ret;
}
  • 外观一:没有入参的时候
()->{return ret;}
  • 外观二:有参数的时候
(Type p)->{return ret;}

// 参数类型可以省略(隐式推掉)
(p)->{return ret;}

// 一个参数的时候,参数列表外的括号也可以没有
p->{return ret;}
  • 外观三:函数体只有一行的时候
// 方法体只有一行的时候,或括号连同结尾的分号都可以省略
e->ret
  • 外观四:没有返回值的时候
e->{}
  • 方法引用和构造器引用
    • instance::instanceMethod
    • className::staticMethod
    • className::instanceMethod
Arrays.asList(null, "", "   ", "HelloWorld", "ByeBug")//
  .stream().filter(StringUtils::isNotBlank)//
  .forEach(System.out::println);
  • ……

3 lambda使用场景

再看一个例子:

// 原始方法启动一个线程
new Thread(new Runnable() {
  @Override
  public void run() {
  }
}).start();

// lambda版
new Thread(()->{}).start();

不难看出,整个匿名内部类中最关键的代码其实就是:

public void run() {
}

所以,lambda中关键部分也就是这部分代码了。

其实,用注解java.lang.FunctionalInterface修饰的接口都可以用于lambda表达式中。

这种接口,都是只有一个方法的接口。

另外,只要你的接口只有一个方法,即使是没有@FunctionalInterface注解修饰,也是可以用lambda的(很多时候编译器还是很聪明的,他会自动推断的)。

总之,lambda只钟爱函数式接口(@FunctionalInterface)。


4 再来个示例

public class HouseInfo {

    private Integer houseId; // 小区ID
    private String houseName;// 小区名
    private Integer browseCount; // 浏览数
    private Integer distance;// 距离 KM
    // constructor
    // getters
    // setters
}
  • 有如下数据
List<HouseInfo> houseInfos = Lists.newArrayList(//
    new HouseInfo(1, "恒大星级公寓", 100, 1), //
    new HouseInfo(2, "汇智湖畔", 999, 2), //
    new HouseInfo(3, "张江汤臣豪园", 100, 1), //
    new HouseInfo(4, "保利星苑", 23, 10), //
    new HouseInfo(5, "北顾小区", 66, 23), //
    new HouseInfo(6, "北杰公寓", null, 55), //
    new HouseInfo(7, "保利星苑", 77, 66), //
    new HouseInfo(8, "保利星苑", 111, 12)//
);
  • 按距离排序
Collections.sort(houseInfos, (h1, h2) -> {
    if (h1 == null || h2 == null)
      return 0;
    if (h1.getDistance() == null || h2.getDistance() == null)
      return 0;
    return h1.getDistance().compareTo(h2.getDistance());
});
  • 输出小区名
houseInfos.stream().map(h -> h.getHouseName()).forEach(System.out::println);
  • 动态条件过滤
// 该函数式接口用来当做测试条件
public static interface Condition<T> {
  boolean conditionTest(T e);
}

// 定义一个方法来输出满足condition测试结果的小区
public void printWithFilter(List<HouseInfo> houseList, Condition<HouseInfo> condition) {
  houseList.stream().filter(e -> condition.conditionTest(e)).forEach(System.out::println);
}

@Test
public void test4() {
  List<HouseInfo> houseInfos = Lists.newArrayList(//
    new HouseInfo(1, "恒大星级公寓", 100, 1), //
    new HouseInfo(2, "汇智湖畔", 999, 2), //
    new HouseInfo(3, "张江汤臣豪园", 100, 1), //
    new HouseInfo(4, "保利星苑", 23, 10), //
    new HouseInfo(5, "北顾小区", 66, 23), //
    new HouseInfo(6, "北杰公寓", null, 55), //
    new HouseInfo(7, "保利星苑", 77, 66), //
    new HouseInfo(8, "保利星苑", 111, 12)//
  );
  // 打印小区名包含北字的小区
  printWithFilter(houseInfos, e -> e != null && e.getHouseName().contains("北"));
  // 打印距离大于10KM的小区
  printWithFilter(houseInfos, e -> e != null && e.getDistance() != null && e.getDistance() > 10);
}
  • 字符串转大写
Arrays.asList("HelloWord", "ByeBug")
  .stream().map(String::toUpperCase).forEach(System.out::println);
  • 其实在Scala里lambda更加直观
List("HelloWord", "ByeBug").map(_.toUpperCase()).foreach(println _);

5 lambda的好基友

通过上面的示例,再结合实际。不难发现最常用的lambda的形式如下:

  • 单个输入,无输出
  • 单个输入,单个输出
  • 无输入,输出单个类型
  • 两个不同类型的输入,第三种类型的输出
  • 两个不同类型的输入,其中一种类型的输出
  • ……

其实在每次使用的时候,没必要自己去新建这些函数式接口以支持lambda,JDK就已经给lambda内置了很多好基友:

  • Consumer: 单个输入,无输出
public interface Consumer<T> {
  void accept(T t);
}
  • BiConsumer: 两个不同类型的输入,无输出
public interface BiConsumer<T, U> {
  void accept(T t, U u);
}
  • Supplier: 无输入,输出单个类型
public interface Supplier<T> {
  T get();
}
  • Function: 两个不同类型的输入
public interface Function<T, R> {
  R apply(T t); // 输入T类型,转换成R类型
}
  • ToIntFunction / ToLongFunction / ToDoubleFunction
public interface ToIntFunction<T> {
    // T类型的输入,输出int 
    // 类似于Stream的mapToInt()
    int applyAsInt(T value); 
}
  • IntFunction / LongFunction / DoubleFunction
public interface IntFunction<R> {
    R apply(int value);
}
  • BiFunction: 两个不同类型的输入,第三种类型的输出
public interface BiFunction<T, U, R> {
  R apply(T t, U u);
}
  • 条件测试
public interface Predicate<T> {
  boolean test(T t);
}

可以不用自定义函数式接口来支持lambda,上面的例子可以改成:

public void printWithFilter(List<HouseInfo> houseList, Predicate<HouseInfo> condition) {
  houseList.stream().filter(e -> condition.test(e)).forEach(System.out::println);
}

关于lambda的传说和他到底是什么鬼,看到这里应该够了。

毕竟我们的主要目的是使用它,知道他怎么用才是重点。

没必要纠结他严格的学术定义(这种事不应该是那种只会纸上谈兵的老不死干的吗?)。

在Java8里和lambda相关的主要API就是Stream了。在了解Stream的时候来顺便熟悉lambda吧。

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

推荐阅读更多精彩内容

  • 前言 人生苦多,快来 Kotlin ,快速学习Kotlin! 什么是Kotlin? Kotlin 是种静态类型编程...
    任半生嚣狂阅读 26,201评论 9 118
  • lambda表达式(又被成为“闭包”或“匿名方法”)方法引用和构造方法引用扩展的目标类型和类型推导接口中的默认方法...
    183207efd207阅读 1,478评论 0 5
  • Java8 in action 没有共享的可变数据,将方法和函数即代码传递给其他方法的能力就是我们平常所说的函数式...
    铁牛很铁阅读 1,229评论 1 2
  • 在C++11中,我们还是会看到一些新元素。这些新鲜出炉的元素可能会带来一些习惯上的改变,不过权衡之下,可能这样的改...
    认真学计算机阅读 5,477评论 1 27
  • 简介 概念 Lambda 表达式可以理解为简洁地表示可传递的匿名函数的一种方式:它没有名称,但它有参数列表、函数主...
    刘涤生阅读 3,201评论 5 18