当前位置:   article > 正文

关于JAVA8的Lambda表达式

关于JAVA8的Lambda表达式

1. 水在前面

        这个礼拜忽然心血来潮把Lambda表达式学习了一遍,发现这玩意跟原来想象的好像不是一个东西,写个学习心得供以后复习用。还是那句话,这篇水文不能让你完全掌握,只是用来给我自己温习用的,或者也可以作为小伙伴的学习引路,别指望能读一篇文章就学会了。

2. 关于教材

        上周为了学习Lambda还特意跑去图书馆翻了好几本书(期间还被一个8、9的小屁孩鄙视了一番),最后关于Lambda表达式的学习,还是推荐这本《JAVA 8 实战》Raoul-Gabriel Urma,Mario Fusco 著 (pdf版 wx号:zhenyeli86  添加好友请说明技术交流,5毛党)。这本书从对Lambda表达式的背景、使用、以及原理都做了解释,还是我这种水货都能看懂的解释,还为继续往后面深挖扩展。

3. 学习心得

        关于Lambda的学习最大的体会是,重要的并不是Lambda本身,而是使用Lambda的上下文。这里可能有点抽象了,但是面向对象本身就是抽象的,先买个关子,好戏在后头。

3.1. Lambda语法

  1. (parameters) -> expression
  2. 或者
  3. (parameters) -> {statement}

3.2. Lambda是什么

        按我的理解,Lambda本质上其实是一个函数,一个没有声明函数名称的函数,从语法上就很明显看到有参数列表和函数主体,只不过多了个“->”符号而已。  

        至于函数的返回值也是一个很有意思的事情,Lambda的函数返回值类型,是根据调用它的上下文来确定的,或者说是根据你想用它的地方的上下文确定了你对Lambada函数的返回值的定义。又有点抽象了,我们继续往后看。

3.3. Lambda用在什么地方

        敲黑板,全文浓缩成的一句话在这里了:Lambda表达式可以作为一个函数的“参数”使用,该参数的类型是一个函数式接口!

  1. //调用fun
  2. fun(Lambda表达式);
  3. //其中fun的定义如下:
  4. void fun (函数式接口 c){
  5. //反正这里肯会和Lambda有关
  6. }

        神奇不?惊讶不!上文说到Lambda本质上是一个函数,就是说,一个函数的参数居然是另一个函数!对这就是Java 8的传递函数作为参数,其实我猜本质上还是值传递,只是传递了函数的地址值。

        慢着,并不是什么函数都可以把Lambda作为参数传入的,参数类型一定是“函数式接口的实例”。那什么是函数式接口呢?函数式接口就是“只有一个抽象方法的接口”。 函数式接口不能没有抽象方法,不能没有,也不能有多个,只能是一个抽象方法,至于非抽象方法,你爱整多少个就整多少个。

3.4. 稍微做个总结

        在继续水之前,我们先来做个小小的总结:Lambda 表达式本质上是一个函数,它是可以作为一个一个参数传给另一个函数使用,而这个参数的类型是函数式接口。只有一个抽象方法的接口就叫函数式接口。

3.5. Lambda怎么用

3.5.1. 挑绿苹果小程序  

        下面我们借用一个书上的例子继续水。这个例子是这样描述的,我们要写一个小功能,这个功能是在一堆苹果里面挑出绿色的苹果给用户。好的,根据这个需求,我们写一个小程序(我觉得还是要写一个可以执行的,完整的代码才方便对照阅读)。

  1. import java.util.ArrayList;
  2. import java.util.List;
  3. //定义一个小苹果
  4. class Apple{
  5. private String color;
  6. public Apple(String appleColor){
  7. this.color = appleColor;
  8. }
  9. public void setColor(String appleColor) {
  10. this.color = appleColor;
  11. }
  12. public String getColor() {
  13. return this.color;
  14. }
  15. }
  16. 举个栗子
  17. public class Demo {
  18. //第一版的挑苹果函数
  19. public static List<Apple> filterApples(List<Apple> inventory){
  20. List<Apple> result = new ArrayList<Apple>();
  21. for (Apple apple: inventory){
  22. if ("green".equals(apple.getColor())) {
  23. result.add(apple);
  24. }
  25. }
  26. return result;
  27. }
  28. //主程序
  29. public static void main(String[] args) {
  30. // TODO Auto-generated method stub
  31. List<Apple> appleList = new ArrayList<Apple>();
  32. appleList.add(new Apple("red"));
  33. appleList.add(new Apple("green"));
  34. appleList.add(new Apple("red"));
  35. //获取结果
  36. List<Apple> ret = filterApples(appleList);
  37. System.out.println("Apples: ");
  38. for (Apple app: ret) {
  39. System.out.println( app.getColor() + " apple;");
  40. }
  41. }
  42. }

        上面小程序还写得不错吧,这时候业主爸爸说我除了要挑绿色的苹果,偶尔也挑红色的苹果。作为一个有经验(被折磨多次)的搬砖工,我们立马想到,爸爸下回可能还要重量大于100克的苹果、有虫子的苹果、日本苹果甚至长得像桃子的苹果。。。

 3.5.2. 使用策略模式优化

        按以前我们除了心里问候一下,肯定也会趁机卖弄下技术,整个策略模式,反正你要怎么挑我就写个对应的策略完事。看下我们写的第二版策略模式的挑苹果小功能:

  1. import java.util.ArrayList;
  2. import java.util.List;
  3. //定义一个小苹果,这次增加了重量属性应付麻烦业主
  4. class Apple{
  5. private String color;
  6. private int weight;
  7. public Apple(String appleColor, int appleWeight){
  8. this.color = appleColor;
  9. this.weight = appleWeight;
  10. }
  11. public String getColor() {
  12. return this.color;
  13. }
  14. public int getWeight() {
  15. return this.weight;
  16. }
  17. }
  18. //苹果挑选器,函数式接口
  19. interface AppleSelector{
  20. boolean test(Apple a);
  21. }
  22. //绿色苹果挑选器
  23. class GreenAppleSelector implements AppleSelector{
  24. @Override
  25. public boolean test(Apple a) {
  26. // TODO Auto-generated method stub
  27. if ("green".equals(a.getColor()))
  28. return true;
  29. else
  30. return false;
  31. }
  32. }
  33. //红色色苹果挑选器
  34. class RedAppleSelector implements AppleSelector{
  35. @Override
  36. public boolean test(Apple a) {
  37. // TODO Auto-generated method stub
  38. if ("red".equals(a.getColor()))
  39. return true;
  40. else
  41. return false;
  42. }
  43. }
  44. //大苹果挑选器
  45. class BigAppleSelector implements AppleSelector{
  46. @Override
  47. public boolean test(Apple a) {
  48. // TODO Auto-generated method stub
  49. if (100 <= a.getWeight())
  50. return true;
  51. else
  52. return false;
  53. }
  54. }
  55. public class Demo {
  56. //第一版的挑苹果函数
  57. public static List<Apple> filterApples(List<Apple> inventory, AppleSelector selector){
  58. List<Apple> result = new ArrayList<Apple>();
  59. for (Apple apple: inventory){
  60. if (selector.test(apple)) {
  61. result.add(apple);
  62. }
  63. }
  64. return result;
  65. }
  66. //主程序
  67. public static void main(String[] args) {
  68. // TODO Auto-generated method stub
  69. List<Apple> appleList = new ArrayList<Apple>();
  70. appleList.add(new Apple("red", 100));
  71. appleList.add(new Apple("green", 200));
  72. appleList.add(new Apple("red", 50));
  73. //挑绿苹果
  74. AppleSelector greenSel = new GreenAppleSelector();
  75. List<Apple> greenRet = filterApples(appleList, greenSel);
  76. //挑大苹果
  77. AppleSelector bigSel = new BigAppleSelector();
  78. List<Apple> bigRet = filterApples(appleList, greenSel);
  79. System.out.println("Green Apples: ");
  80. for (Apple app: greenRet) {
  81. System.out.println( app.getColor() + " apple;");
  82. }
  83. System.out.println("Big Apples: ");
  84. for (Apple app: greenRet) {
  85. System.out.println( Integer.toString(app.getWeight()) + "g apple;");
  86. }
  87. }
  88. }

3.5.3. Lambda版的策略模式

        写到这里小伙伴肯定要吐槽了,水了这么久连个Lambda都没见到!好了我们来看看正主怎么用。其实写道策略模式我觉得代码已经很简洁了,毕竟我只要不断的增加AppleSelector的具体实现就好了,其他的基本不用动了。但是大牛就是大牛,觉得具体实现挑选苹果策略的这段代码就的外围就出现了很多遍,不行!

因此就到了Lambda的出场时刻了!看代码:

  1. import java.util.ArrayList;
  2. import java.util.List;
  3. //定义一个小苹果,这次增加了重量属性应付麻烦业主
  4. class Apple{
  5. private String color;
  6. private int weight;
  7. public Apple(String appleColor, int appleWeight){
  8. this.color = appleColor;
  9. this.weight = appleWeight;
  10. }
  11. public String getColor() {
  12. return this.color;
  13. }
  14. public int getWeight() {
  15. return this.weight;
  16. }
  17. }
  18. //苹果挑选器,函数式接口
  19. interface AppleSelector{
  20. boolean test(Apple a);
  21. }
  22. public class Demo {
  23. //第一版的挑苹果函数
  24. public static List<Apple> filterApples(List<Apple> inventory, AppleSelector selector){
  25. List<Apple> result = new ArrayList<Apple>();
  26. for (Apple apple: inventory){
  27. if (selector.test(apple)) {
  28. result.add(apple);
  29. }
  30. }
  31. return result;
  32. }
  33. //主程序
  34. public static void main(String[] args) {
  35. // TODO Auto-generated method stub
  36. List<Apple> appleList = new ArrayList<Apple>();
  37. appleList.add(new Apple("red", 100));
  38. appleList.add(new Apple("green", 200));
  39. appleList.add(new Apple("red", 50));
  40. //挑绿苹果
  41. List<Apple> greenRet =
  42. filterApples(appleList, (Apple apple) -> "green".equals(apple.getColor()));
  43. //挑大苹果
  44. List<Apple> bigRet =
  45. filterApples(appleList, (Apple apple) -> 100 <= apple.getWeight());
  46. System.out.println("Green Apples: ");
  47. for (Apple app: greenRet) {
  48. System.out.println( app.getColor() + " apple;");
  49. }
  50. System.out.println("Big Apples: ");
  51. for (Apple app: greenRet) {
  52. System.out.println( Integer.toString(app.getWeight()) + "g apple;");
  53. }
  54. }
  55. }

        原本在第二版代码中的AppleSelector接口的具体实现没了,被Lambda表达式取代了。Lambda表达式代替AppleSelector的具体实例作为参数传到了filterApples函数中。这就是Lambda的使用方法。

        这里我们需要注意一点,Lambda表达式在这个程序中的表现形式和AppleSelector的抽象函数要匹配,才不会报错

3.6. 编译器怎么识别Lambda

        水到这里,基本上讲完了Lambda表达式的使用了。我们再深挖一下,编译器是怎么工作的,专业的说法是,编译器是怎么做上下文推断的。

        让我们再次回顾一下3.3节水过的一句话:Lambda表达式可以作为一个函数的“参数”使用,该参数的类型是一个函数式接口!看看Lambda的使用:

  1. List<Apple> bigRet =
  2. filterApples(appleList, (Apple apple) -> 100 <= apple.getWeight());

        当编译器遇到了Lambda表达式,首先检查filterApples函数的参数类型,是一个AppleSelector,这是一个函数式接口,满足基本条件。再看Lambda的函数描述,参数是Apple类型,返回值是一个boolean类型,这个函数描述抽象出来就是 boolean fun(Apple a) 正好就是AppleSelector的唯一抽象方法声明。因此,编译器就是通过这样的推断,确定了Lambda表达式对应了AppleSelector的test方法。以上就是上下文推断的过程!

4. 水在最后

        呼应前文说到的,其实使用Lambda的上下文,比Lambda本身更有意思。可能我对函数式编程的内涵理解还不到位吧,我认为Lambda只是为我们提供了一种简化代码的机制,真正使代码优雅起来的是策略模式。至于说Lambda让代码变得可读性更强,我觉得见人见智吧,起码我感觉直接使用谓语似乎更容易理解。记得前东家的规范上就要求代码还是要简单易懂为优先,毕竟不知道以后是谁来读你写的代码。

        本来只是想过一遍Lambda的使用,结果书是越看越深,后面还有函数引用、函数式编程等没搞懂,还是留在下次再水吧。

声明:本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:【wpsshop博客】
推荐阅读
  

闽ICP备14008679号