Lambda 表达式

java8新增特性函数式编程初探

Lambda初级形态

package lambda;

public class Student {
    private String name;
    private int age;
    
    public Student(String name, int age) {
        super();
        this.name = name;
        this.age = age;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public int getAge() {
        return age;
    }

    public void setAge(int age) {
        this.age = age;
    }

    @Override
    public String toString() {
        return "name="+name+";age="+age;
    }

}

根据年龄对学生进行从小到大排序

    public static void main(String[] args) {
        List<Student> list = new ArrayList<Student>();
        list.add(new Student("大明",6));
        list.add(new Student("李梅",3));
        list.add(new Student("莉莉",10));
        list.add(new Student("山姆",19));
        list.add(new Student("李雷",15));
        
        Collections.sort(list,new Comparator<Student>() {
            @Override
            public int compare(Student o1, Student o2) {
                return o1.getAge()-o2.getAge();
            }
        });
        System.out.println(list);
    }

通过集合工具类提供的排序方法,传入自定义比较器进行排序,一种中规中矩写法。
下面通过java8新特性做一次

    public static void main(String[] args) {
        List<Student> list = new ArrayList<Student>();
        list.add(new Student("大明",6));
        list.add(new Student("李梅",3));
        list.add(new Student("莉莉",10));
        list.add(new Student("山姆",19));
        list.add(new Student("李雷",15));
        
        Collections.sort(list,(o1,o2)->o1.getAge()-o2.getAge());
        System.out.println(list);
    }

那为什么可以这样写呢?
java8新增一个很重要特性:Lambda表达式,亦作闭包;规定仅包含一个抽象方法的接口为函数式接口,用@FunctionalInterface进行标明,仅仅是告诉编译器该接口是一个函数式接口,当接口不符合函数式接口定义会编译报错,该注解不是必须的。
那么让我们追根溯源去看下Comparator接口

@FunctionalInterface
public interface Comparator<T> {

    int compare(T o1, T o2);

    boolean equals(Object obj);

    default Comparator<T> reversed() {
        return Collections.reverseOrder(this);
    }

    default Comparator<T> thenComparing(Comparator<? super T> other) {
        Objects.requireNonNull(other);
        return (Comparator<T> & Serializable) (c1, c2) -> {
            int res = compare(c1, c2);
            return (res != 0) ? res : other.compare(c1, c2);
        };
    }

    default <U> Comparator<T> thenComparing(
            Function<? super T, ? extends U> keyExtractor,
            Comparator<? super U> keyComparator)
    {
        return thenComparing(comparing(keyExtractor, keyComparator));
    }

    default <U extends Comparable<? super U>> Comparator<T> thenComparing(
            Function<? super T, ? extends U> keyExtractor)
    {
        return thenComparing(comparing(keyExtractor));
    }

    default Comparator<T> thenComparingInt(ToIntFunction<? super T> keyExtractor) {
        return thenComparing(comparingInt(keyExtractor));
    }

    default Comparator<T> thenComparingLong(ToLongFunction<? super T> keyExtractor) {
        return thenComparing(comparingLong(keyExtractor));
    }

    default Comparator<T> thenComparingDouble(ToDoubleFunction<? super T> keyExtractor) {
        return thenComparing(comparingDouble(keyExtractor));
    }

    public static <T extends Comparable<? super T>> Comparator<T> reverseOrder() {
        return Collections.reverseOrder();
    }

    @SuppressWarnings("unchecked")
    public static <T extends Comparable<? super T>> Comparator<T> naturalOrder() {
        return (Comparator<T>) Comparators.NaturalOrderComparator.INSTANCE;
    }

    public static <T> Comparator<T> nullsFirst(Comparator<? super T> comparator) {
        return new Comparators.NullComparator<>(true, comparator);
    }

    public static <T> Comparator<T> nullsLast(Comparator<? super T> comparator) {
        return new Comparators.NullComparator<>(false, comparator);
    }

    public static <T, U> Comparator<T> comparing(
            Function<? super T, ? extends U> keyExtractor,
            Comparator<? super U> keyComparator)
    {
        Objects.requireNonNull(keyExtractor);
        Objects.requireNonNull(keyComparator);
        return (Comparator<T> & Serializable)
            (c1, c2) -> keyComparator.compare(keyExtractor.apply(c1),
                                              keyExtractor.apply(c2));
    }

    public static <T, U extends Comparable<? super U>> Comparator<T> comparing(
            Function<? super T, ? extends U> keyExtractor)
    {
        Objects.requireNonNull(keyExtractor);
        return (Comparator<T> & Serializable)
            (c1, c2) -> keyExtractor.apply(c1).compareTo(keyExtractor.apply(c2));
    }

    public static <T> Comparator<T> comparingInt(ToIntFunction<? super T> keyExtractor) {
        Objects.requireNonNull(keyExtractor);
        return (Comparator<T> & Serializable)
            (c1, c2) -> Integer.compare(keyExtractor.applyAsInt(c1), keyExtractor.applyAsInt(c2));
    }

    public static <T> Comparator<T> comparingLong(ToLongFunction<? super T> keyExtractor) {
        Objects.requireNonNull(keyExtractor);
        return (Comparator<T> & Serializable)
            (c1, c2) -> Long.compare(keyExtractor.applyAsLong(c1), keyExtractor.applyAsLong(c2));
    }

    public static<T> Comparator<T> comparingDouble(ToDoubleFunction<? super T> keyExtractor) {
        Objects.requireNonNull(keyExtractor);
        return (Comparator<T> & Serializable)
            (c1, c2) -> Double.compare(keyExtractor.applyAsDouble(c1), keyExtractor.applyAsDouble(c2));
    }
}

什么鬼???问号脸!!!怎么还有一堆default、static修饰的方法,而且还有一个抽象方法boolean equals(Object obj),难道规则就是用来打破的么?

  • 默认方法
    函数式接口可以包含默认方法,因为默认方法不可以是抽象方法,其有一个默认实现,所以是符合函数式接口定义。
  • 静态方法
    函数式接口可以包含静态方法,因为静态方法不可以是抽象方法,是一个已经实现了的方法,所以是符合函数式接口定义。
  • boolean equals(Object obj)
    可以包含Object里的public方法,这些方法对于函数式接口来说,不被当成是抽象方法(虽然是抽象方法);因为任何一个函数式接口的实现,默认都继承了Object类。

Lambda语法格式:

(参数) -> {表达式;}

Lambda语法特征:

可选类型声明:可以不用声明参数类型,编译器可以根据上下文判断参数类型
可选参数圆括号:仅有一个参数无需圆括号
可选大括号:表达式仅包含一个语句,可以不写大括号以及语句末尾的分号
可选返回关键字:表达式仅包含一个语句,可以不写返回关键字return

1.多参数栗子
Collections.sort(list,(o1,o2)->o1.getAge()-o2.getAge());
2.无参数栗子
new Thread(() -> System.out.println("我是线程:"+Thread.currentThread().getName())).start();
3.单个参数栗子     
public static void main(String[] args) {    
    //Consumer:提供任意一种类型的参数,返回空值。 void accept(T t);
    Consumer con = x -> System.out.println(x);
    con.accept("666");
}

jdk8中新增函数式接口包:java.util.function
Runnable r = () -> System.out.printf(“say hello”); //没有输入参数,也没有输出
Supplier sp = () -> “hello”; //只有输出消息,没有输入参数
Consumer cp = r -> System.out.printf(r); //有一个输入参数,没有输出
Function<Integer, String> func = r -> String.valueOf(r); //有一个输入参数 有一个输出参数
BiFunction<Integer, Integer, String> biFunc = (a, b) -> String.valueOf(a + b); //有两个输入参数 有一个输出参数
BiConsumer<Integer, Integer> biCp = (a, b) -> System.out.printf(String.valueOf(a + b)); //有两个输入参数 没有输出参数
……

Lambda高级形态

方法引用
1.静态方法引用:ClassName::methodName
BiFunction<Integer,Integer,Integer> result = Integer::sum;
result.apply(1, 2);
or
BiFunction<Integer, Integer, Integer> con = (a,b)->Integer.sum(a, b);
con.apply(1,2);
2.实例上的实例方法引用:instanceReference::methodName
Set set = new HashSet();
set.add(“666”);
Predicate pr = set::contains;
pr.test(“666”);
pr.test(“233”);
or
Predicate p = ars -> set.contains(args);
p.test(“666”);
p.test(“233”);
3.构造方法引用:Class::new
Function<String, BigDecimal> f = b->new BigDecimal(b);
System.out.println(f.apply(“666”));
or
Function<String,BigDecimal> ff = BigDecimal::new;
System.out.println(ff.apply(“77”));
4.数组构造方法引用:typeName[]::new
IntFunction<int[]> i = int[]::new;
i.apply(5);
System.out.println(i.apply(1)[0]);

另:lambda表达式中可以读取并修改所在类成员变量;可读取所在方法局部变量但无法修改,而且当其读取局部变量时该变量将变为final修饰从而无法被修改。

总结

1.Lambda表达式使得代码更简洁,思路更清晰。
2.初级用法(参数)->{表达式},有三种用法。
3.高级用法ClassName::methodName,主要有四种用法

参考资料

  1. https://www.runoob.com/java/java8-lambda-expressions.html
  2. https://www.cnblogs.com/andywithu/p/7357069.html
  3. https://baijiahao.baidu.com/s?id=1614680282522143196&wfr=spider&for=pc
  4. https://shmilyaw-hotmail-com.iteye.com/blog/2251821

版权声明:本文为cat-fishing原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
本文链接:https://www.cnblogs.com/cat-fishing/p/11193538.html