彻底了解lambda及函数式接口

3,930 阅读3分钟

一、lambda是什么

  • 函数式编程?匿名内部类?简洁?
  • 来源于学术界λ,主要是行为参数化、简化内部类,在函数式接口中应用广泛。

二、为什么要学习lambda?

  • 不要问我为什么,厂长是我表哥。
  • 本文让你彻底学会使用lambda,不管在哪里你都能()->{} 一把嗦。

三、lambda语法

  • 一个完整的lambda表达式
(Type1 param1,Type2 param2,Type3 params)->{

	statement1;
	statement2;
	....
	return data;
}

1、()->{},lambda参数、箭头、lambda主体缺一不可

2、参数类型可以省略,而且基本都是省略

3、lambda主体中如果只有一条语句可以省略大括号、return及结尾分号

4、方法引用可以作为lambda表达式

无参lambda

  • 比匿名内部类更先进,接口名称、函数名都省略了。
  • lambda体只有一行,{}可以省略。

有参lambda

  • 很多时候idea都能优化,但是但是。。。提交的代码库中依然一堆idea提示灰色。

四、 函数式接口

  • 能够使用lambda的依据是函数式接口,即内部只有一个抽象方法的接口,并非只有一个方法。
  • lambda表达式的类型推断也是跟函数式接口有关,编译器能根据上下文信息推断出参数类型。

几个典型的函数式接口

1、BiFunction

@FunctionalInterface
public interface BiFunction<T, U, R> {
    R apply(T t, U u);

    default <V> BiFunction<T, U, V> andThen(Function<? super R, ? extends V> after) {
        Objects.requireNonNull(after);
        return (T t, U u) -> after.apply(apply(t, u));
    }
}

1、这个函数式接口在java 8 stream中大量使用。

2、有2个方法,只有1个抽象方法

3、抽象方法apply接受2个参数,类型是T,U,返回值是R。

4、下面的get()第三个参数的BiFunction函数式接口,因此你在调用get()可以传递一个符合该函数式接口规范的lambda。至于参数1、参数2怎么操作、运算、加密。。。依赖于你的lambda主体。

2、Consumer

  • 消费函数,返回值为void。

@FunctionalInterface
public interface Consumer<T> {
    void accept(T t);

    default Consumer<T> andThen(Consumer<? super T> after) {
        Objects.requireNonNull(after);
        return (T t) -> { accept(t); after.accept(t); };
    }
}

  • 返回值void,故一般使用其副作用。

3、Predicate

  • 断言/条件测试,定义一个抽象方法,必定有1个入参,返回值boolean。
  • 最常用在stream中的filter(),可以组成predicate调用链。
//以下都可以做一个Predicate的lambda
1、(s)->s.length()>0;
2、(v)->v > 100;
3、(o)->o.startWith("a");

4、Supplier

  • 不需要输入,产出T。像是一个容器,调用get返回对象。
Supplier<Integer> supplier = new Supplier<Integer>() {
            @Override
            public Integer get() {
                //返回一个随机值
                return new Random().nextInt();
            }
        };
System.out.println(supplier.get());
System.out.println(supplier.get());

方法引用

  • 简单来讲就是让你可以重复使用现有的方法定义,并像lambda一样传递他们,使用::来表示。主要有三类。
  • 核心点:函数签名要符合,即传参类型、参数数量、返回值类型还是void。

1、静态方法的方法引用

  • 静态方法作用于对象上。
  public static void main(String[] args) {
    List<String> list = Lists.newArrayList("1","2","3","4");

    List<Integer> collect1 = list.stream().map(v -> Integer.parseInt(v))
        .collect(Collectors.toList());
    List<Integer> collect2 = list.stream().map(Integer::parseInt)
        .collect(Collectors.toList());
    
  }

2、指向 任意类型实例方法 的方法引用

1、这个任意类型一般指的是你的lambda的入参类型。

2、如下面的入参是v,调用v.length可以写作v的类型String::length。

  public static void main(String[] args) {

    List<String> list = Lists
        .newArrayList("hello", "how", "are", "you", "i", "am", "fine", "thank", "you", "and",
            "you");
    Set<Integer> collect = list.stream().map(v -> v.length()).collect(Collectors.toSet());

    Set<Integer> collect1 = list.stream().map(String::length).collect(Collectors.toSet());
    System.out.println(collect);
    System.out.println(collect1);
  }

3、指向既有对象的方法的方法引用

  • 与上述第2点类似,不详细说明。

五、小结

  • 主要介绍了lambda表达式及函数式接口
  • 使用函数式接口的核心就是搞对函数签名,传入一个符合条件签名的lambda,就可以()->{}一把唆了。