前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >解构Lambda表达式

解构Lambda表达式

作者头像
简熵
发布2023-03-06 21:46:37
2660
发布2023-03-06 21:46:37
举报
文章被收录于专栏:逆熵逆熵

解构Lambda表达式

1.1 行为参数化传递给方法

java8将方法和函数,Lambda函数提升为一等公民,可以作为值进行传递。

传统的行为参数化

  1. 通过对象值作为参数传递的,在对象中定义的方法封装了行为。通过合理的接口,抽象出顶层的父接口,设计加多个子类实现达到多种不同的行为通过一个参数进行传递。缺点是对于每一次的行为处理都需要实例化一个子类对象,可能这个对象只用了一次,太浪费了。
  2. 匿名类,可以同时声明并实例化一个类,随用随建。缺点是还需要写一个创建对象并实现方法的大块头,用多了很臃肿
  3. Lambda表达式,直接将行为方法作为参数传递给方法。a -> system.out.println(a.getName());

1.2 Lambda——匿名函数

把方法作为值传递虽然很有用,但是有时有些方法我们不想具体定义,这时候Lambda函数就登场了。Lambda表达式的基本语法是(parameters)->expression或者(parameters)->{statements;}

Lambda表达式的组成部分:参数列表+箭+Lambda主体(如果是表达式,可以省略{},如果是语句必须加入{})

Lambda表达式可以被赋值给一个变量,也可以传递给一个接受函数式的接口作为参数的方法。

1.2.1 函数式接口

定义:只定义一个抽象方法的接口,其中默认方法不算在内。

@FunctionalInterface注解不是必需的,它只是告诉使用者这是一个函数式接口,并且在一个标注了该注解的接口中如果存在除了默认方法或者继承Object方法之外的抽象方法的个数多于1个的时候,编译器会提示报错Multiple non-overriding abstract methods found in interface Lambdasinaction.chap3.Lambdas.checkedConsumer

代码语言:javascript
复制
@FunctionalInterface
public interface Predicate<T> {

    /**
     * Evaluates this predicate on the given argument.
     *
     * @param t the input argument
     * @return {@code true} if the input argument matches the predicate,
     * otherwise {@code false}
     */
    boolean test(T t);
}

关于异常,任何函数式接口都不允许抛出检查异常(checked Exception),对于异常的处理,有两种做法,一种是将Lambda表达式包在try...catch中重新抛出运行时异常,还有种做法是自己重新定义一个抛出使用检查异常的函数式接口。

1.2.2 函数描述符

定义:函数式接口的抽象方法的签名我们称之为函数描述符,而抽象方法的签名等同于Lambda表达式的签名。比如用以下表示法()->void 来描述 Runnable接口 ,对Predicate<T>的函数描述符就是T->boolean

1.2.3 Lambda的类型检查

Lambda表达式生成的函数式接口实例如何对应函数式接口呢?

首先,Lambda的类型是从上下文中推断出来的。我们声明了函数式接口作为参数,从函数式接口的函数描述符可以知道入参,返回值还有是否抛出异常的定义。只要以上几个定义和lamdba表达式一致,那么这个lamdba表达式就可以赋值给不同的函数接口。比如以下同一个Lambda表达式就可以赋值给不通的函数式接口。

代码语言:javascript
复制
Comparator<Integer> comparator = 
    (Integer a1, Integer a2) -> a1.compareTo(a2);
ToIntBiFunction<Integer,Integer> intBF=
    (Integer a1, Integer a2) -> a1.compareTo(a2);
BiFunction<Integer,Integer,Integer> bf=
    (Integer a1, Integer a2) -> a1.compareTo(a2);

对于以上中的参数类型,java编译器也可以通过目标类型来推断参数的真实类型。

1.2.4 局部变量和外层变量

当Lambda表达式中使用外部的实例变量和静态变量没有什么限制,但是对于外部的局部变量需要申明为final或者这个局部变量不会再赋值改变。

造成已上情况的原因:

  1. 实例变量保存在java堆中,局部变量保存在java的方法栈中,多线程情况下可能这个局部变量已经被回收了,而此时还有访问的情况可能会发生。
  2. Lambda对值封闭,对变量开放。
1.2.5 方法引用

一种Lambda表达式的语法糖,针对的是Lambda中只涉及一种方法的情况,可以复用已存在的方法并传递给函数式接口。有如下的表现,可以使用语法糖对原有的Lambda进行转换:

  1. args->ClassName.staticMethod(agrs) 转换成方法引用:ClassName::staticMethod,通俗地讲,就是Lambda调用了一个类的静态方法,并且满足了函数接口的签名。 Function<String,Integer> func=s->Integer.parseInt(s); System.out.println(func.apply("122222")); //二者相等 Function<String,Integer> func1=Integer::parseInt; System.out.println(func1.apply("1222223"));
  2. (inst0,agrs)->inst0.instanceMethod(agrs) ,inst0是ClassName类型, 转换成方法引用:ClassName::instanceMethod,通俗地讲,Lambda主体中引用的某个对象的方法,该对象就是Lambda中的参数。 Function<String,Integer> func3=s->s.length(); System.out.println(func3.apply("12345")); //二者相等 Function<String,Integer> func4=String::length; System.out.println(func4.apply("1234"));
  3. (args)->extra.instanceMethod(args),转换成方法引用:extra::instanceMethod,通俗地讲,就是Lambda主体引用的一个外部实例的方法,并且满足函数式接口的签名。 Extra extra=new Extra(); Function<String,String> func5=s->extra.getName(s); Function<String,String> func6=extra::getName; System.out.println(func5.apply("12345")); System.out.println(func6.apply("1234"));
1.2.6 构造函数引用

对于类的构造函数,可以使用类似静态方法的方式ClassName::new,对于不同签名的函数式接口,只要满足签名,就可以做到相对应的适配。

代码语言:javascript
复制
//()->T
Supplier<Extra> supplier=Extra::new;
final Extra extra1 = supplier.get();
System.out.println(extra1);
//T->R
Function<String,Extra> function=Extra::new;
final Extra extra2 = function.apply("test");
System.out.println(extra2);
//T,U->R
BiFunction<String,String,Extra> biFunction=Extra::new;
final Extra extra3 = biFunction.apply("test", "new");
System.out.println(extra3);
//T,U,M->R
TreeFunction<String,String,Integer,Extra> treeFunction=Extra::new;
final Extra extra4 = treeFunction.newObj("test3", "new3", 2);
System.out.println(extra4);

1.5 默认方法

为了达到 已发布的接口改变(新增,修改方法)不影响已有的实现类接口的目的,接口声明中新增default关键字,用于新增接口新的方法,比如Collection中新增stream/parallelStream

代码语言:javascript
复制
default Stream<E> stream() {
    return StreamSupport.stream(spliterator(), false);
}

/**
     * Returns a possibly parallel {@code Stream} with this collection as its
     * source.  It is allowable for this method to return a sequential stream.
     *
     * <p>This method should be overridden when the {@link #spliterator()}
     * method cannot return a spliterator that is {@code IMMUTABLE},
     * {@code CONCURRENT}, or <em>late-binding</em>. (See {@link #spliterator()}
     * for details.)
     *
     * @implSpec
     * The default implementation creates a parallel {@code Stream} from the
     * collection's {@code Spliterator}.
     *
     * @return a possibly parallel {@code Stream} over the elements in this
     * collection
     * @since 1.8
     */
default Stream<E> parallelStream() {
    return StreamSupport.stream(spliterator(), true);
}
本文参与 腾讯云自媒体分享计划,分享自微信公众号。
原始发表:2022-12-13,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 逆熵架构 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 解构Lambda表达式
    • 1.1 行为参数化传递给方法
      • 1.2 Lambda——匿名函数
        • 1.5 默认方法
        领券
        问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档