java8中的lambda表达式,看这篇就够了

Lambda表达式

Lambda是简洁的标识可传递匿名函数的一种方式。“互动”事件驱动下,最终面向对象编程和函数式编程结合才是趋势。 java中,一段代码的传递并不容易。因为JAVA是面向对象的语言,如果要传递一段代码,必须先构建类,再生成对应的对象来传递所要的代码。在之前,JAVA的设计者都抗拒加入这一特性,虽然JAVA现有的特性也能通过类和对象实现类似的API但是这样复杂且不易于使用。在后期,问题早已不是JAVA是不是要变成一门使用函数式编程的语言,而是如何实现这种改变。在java8之前已经有了多年的实验,然后JAVA8来了。


特性

  1. 匿名:lambda表达式不像面向对象的方法一样,有确定的名称。
  2. 函数:虽然lambda不是对象的方法,属于某个特定的类。但是lambda表达式一样的有参数列表、函数主体 返回类型和异常声明
  3. 传递:lambda表达式可以作为参数传递
  4. 简洁:无需像匿名类一样有固定模板的代码,lambda写得少而想得多
  5. JAVA8中 可以为接口增加静态方法、可以为类增加默认方法

一、lambda表达式介绍

1.1 lambda表达式结构:

在这里插入图片描述

1.2 常见的Lambda表达式

//1、单个参数
(String s)->s.length()
//2、单个对象
(Apple a)->a.getWeight()>150
//3、多参数,多语句
(int a,int b)->{
    System.out.println(a);
    System.out.println(b);
}
//4、空参数,返回int值42
()->42
//5、多对象参数
(Applea1,Applea2)->a1.getWeight().compareTo(a2.getWeight())




1.3 基本语法

  • (参数…)-> 表达式 隐式返回表达式结果

  • (参数…)->{执行语句} 可用return语句 显示返回执行结果

  • 函数式接口不允许抛出受检异常

  • 注意:当参数只有一个时,也可以去掉参数的括号。原因是java编译器的自动类型推断


1.4 类型检查

  1. Lambda的类型由上下文推断而来

  2. 同样的lambda表达式,不同的函数式接口,只要方法的签名一致,同样的表达式可以用于不同的函数是接口。

  3. 只有函数式接口的实现,能承载lambda表达式

  4. Objecto=()-{System.out.print("HellowWorld")}这是不合法的 因为Object不是一个函数式接口


1.5 类型推断

  1. Lambda表达式可以省略参数的类型,java编译器能自动推断

  2. 当lambda只有一个参数需要推断类型时,参数两边的括号可以省略

List<Apple> c=filter(inventory,a->"green".equals(a.getColor()));
Comparator<Apple> c=(a1,a2)->a1.getWeight.compareTo(a2.getWeight());



1.6 变量作用域

JAVA8之前 内部类只允许访问final修饰的变量,现在使用lambda表达式,一个内部类可以访问任何有效的final局部变量-任何值不会发生变化的变量

  1. java限制了 lambda表达式访问的自由变量,值是不可更改的,因为这会导致出现无法预料的并发问题。

  2. java编译器的限制是有限的,只对局部变量有效,如果使用静态变量,或者示例变量,编译器不会提示任何错误。这样仍然是不安全的。

  3. 可以用数组 int[] counter =new int[1]; button.setOnAaction(event->counter[0]++); 任然可以让lambda对局部变量进行重新赋值。

  4. lambda表达式的方法体,与被嵌套的代码块具有同样的作用域,所有适用同样的命名冲突和变量屏蔽规则。

1.7 方法引用

对于已有的方法,如果希望作为lambda表达式来使用,可以直接使用方法引用

三种方法引用的情况

  1. 对象::实例方法
  2. 类::静态方法
  3. 类::实例方法

在第一种和第二种方法引用种,方法的引用等于提供方法参数的lambda表达式
例如:

  • System.out::println() 等同于 System.out.print(x)
  • Math::pow 等同于 (x,y)->Math.pow(x,y)

对于第三种,则相当于第一个参数成为执行方法的对象
例如:String::compareToIngnoreCase 等同于(x,y) x.compareIngoreCase(Y);



1.8 构造器引用

对于构造器引用,相当于根据构造器的方法的参数,生成一个构造的对象的一个lambda表达式
例如:StringBuilder::new 可以表示为 (Stiring s)->new StringBuilder(s); 具体引用哪个构造器,编译器会根据上下文推断使用符合参数的构造器。





二、在何处使用lambda表达式



2.1 函数式接口介绍

总结:就是只定义了一个抽象方法的接口,即使有一堆的default方法(default方法是为了增强某些API但避免现有大范围改动所有API所以推出了默认方法)

不同接口的默认方法冲突问题
如果实现的接口已有一个默认方法,但是另一个父类或者接口也有同样的默认方法。

  1. 如果是父类和接口默认方法一致,那么直接使用父类的方法实现,忽略接口中的默认方法(类优先规则,如果尝试重写默认方法toString 那么永远都不会优于Object的toString)

  2. 如果一个父接口提供了一个默认方法,另一个接口也提供了同名称和参数的方法(不论是否默认方法)那么都必须覆盖改方法。

其他:
接口中重写Object类的方法,例如 Comparator 一般是为了关联javadoc的注释。




2.2 常见的函数式接口

介绍:函数式接口的抽象方法的签名,基本就是lambda表达式的签名,这种抽象方法称为 函数描述符

Predicate接口

方法签名为,输入某个对象 返回布尔结果

    /**
     * java.util.Predicate 是一个只有test方法,返回布尔值的一个函数式接口,
     * 与其类似的还有用于比较,排序的Comparator接口,其只有一个返回整数的比较接口
     * @param list
     * @param p
     * @param <T>
     * @return
     */
    public static <T> List<T> filter(List<T> list, Predicate<T> p){
        List<T> result=new ArrayList<>();
        for (T t : list) {
            if (p.test(t))
                result.add(t);
        }
        return result;
    }

    public static void main(String[] args) {
        //Predicate函数式接口示例
        List<Apple> appleList=new ArrayList<>();
        List<Apple> resulAppleList=filter(appleList,(Apple a)->a.getColor().equals("red"));
    }



Counsumer接口

Accept ()方法签名为,输入某个对象 返回void

    /**
     * 常用2:Consume
     * consume接口定义了一个 名为accept的抽象方法,接收泛型 T 返回void
     * 可用来访问T类型的对象,并且执行某些操作。
     * 如下用其创建,一个foreach方法,可以实现对所有List的遍历。且对每个对象执行consume定义的操作。
     * 该foreach方法,java8之后成了List接口的default方法。
     * @param list
     * @param <T>
     */
    public static <T> void foreach(List<T> list, Consumer<T> consumer){
        for (T t : list) {
            consumer.accept(t);
        }
    }
    
    
 //Consume函数式接口示例,遍历列表执行某项操作
foreach(appleList,(Apple a)->{if (a.getColor()==null);a.setColor("garly");});
appleList.forEach((Apple a)->{if (a.getColor()==null);a.setColor("garly");});



Function接口

Apply() 方法签名:输入某个对象、返回某个对象

    /**
     * 常用2:Consume
     * consume接口定义了一个 名为accept的抽象方法,接收泛型 T 返回void
     * 可用来访问T类型的对象,并且执行某些操作。
     * 如下用其创建,一个foreach方法,可以实现对所有List的遍历。且对每个对象执行consume定义的操作。
     * 该foreach方法,java8之后成了List接口的default方法。
     * @param list
     * @param <T>
     */
    public static <T> void foreach(List<T> list, Consumer<T> consumer){
        for (T t : list) {
            consumer.accept(t);
        }
    }
    
    
        //Consume函数式接口示例,遍历列表执行某项操作
        foreach(appleList,(Apple a)->{if (a.getColor()==null);a.setColor("garly");});
        appleList.forEach((Apple a)->{if (a.getColor()==null);a.setColor("garly");});



2.3 常见的Lambda和已有的实现

案例 Lambda例子 对应的函数式接口
布尔表达式 (List<String> list) ->list.isEmpty() Predicate<List<String>
创建对象 ()->new APPle() Supplier<Apple>
消费一个对象 (Apple a->{sout(a.getColor());} Consumer<Apple>
从一个对象中提取 (Apple a)>a.geWeight() Function<Apple> 或者其特殊化的 ToIntFunction<Apple>
合并两个值 (int a,int b)->a+b IntBinaryOperator
比较两个对象 (Apple a1,Apple a2)->a1.getWeight().compareTo(a2.getWeight()) Comparator<Apple> BigFunction<Apple,Apple,Integer> ToIntBigFunction<Apple,Apple>


2.4 针对装箱拆箱的优化

java的基本类型和引用类型之间,会自动的进行拆箱装箱,但是本质是吧原始类型包裹起来再保存在堆内存,所以装箱后需要更多内存。java位基本的类型定义了特有的函数式接口,一般只需要加上原始类型的前缀即可

输入基本类型的函数式接口:

  1. DoublePredict、IntConsumer、LongBinaryOperate

输出基本类型的函数式接口:

  1. ToIntFunction

2.5 复合Lambda函数

    List<Apple>apples=newArrayList<>();
    apples.add(newApple("red",11));
    apples.add(newApple("red",12));
    apples.add(newApple("green",13));
    
    /**
    *对排序lanmbda进行复复合-比较器链
    *1、默认逆序方法:reversed()
    *2、多级比较:thenComparing()
    *example:对apples按照颜色排序后,进行逆序,如果颜色一样再按照重量递增
    */
    Comparator<Apple>comparator=Comparator.comparing(Apple::getColor).reversed().thenComparing(Apple::getWeight);
    apples.sort(comparator);
    
    
    /**
    *谓词复合且、或、非
    *1、negate否定
    *2、and且
    *3、or或
    *example:对不是红色的苹果进行过滤,且收集重量大于100的苹果
    */
    Predicate<Apple>redApplePredicate=a->a.getColor().equals("red");
    Predicate<Apple>notRedApple=redApplePredicate.negate();
    List<Apple>notRedAppleList=apples.stream().filter(notRedApple.and(apple->apple.getWeight()>100)).collect(Collectors.toList());
    
    
    /**
    *函数复合
    *1、andThen将前一lambda执行结果,作为后一表达式的参数
    *2、compose将后一表达式的结果作为前一表达式的参数
    *example;complexReult=g(f(x))例如g(f(1))step1:1+1=2step2:(1+1)*2+""
    */
    Function<Integer,Integer>f=x->x+1;
    Function<Integer,String>g=x->x*2+"";
    Function<Integer,String>complexResult1=f.andThen(g);


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

推荐阅读更多精彩内容