函数式编程(二):lambda表达式

本文详细介绍了Java 8中的Lambda表达式,包括其概念、用法、原理和自定义。Lambda允许将函数作为方法参数,简化了匿名内部类的使用,尤其在处理单方法接口时。示例展示了如何在数组排序和自定义计算中应用Lambda,以及如何通过类名::方法名的方式引用静态方法。此外,还提及Lambda在构造对象方面的应用。
摘要由CSDN通过智能技术生成

概述

函数式编程(Functional Programming)是把函数作为基本运算单元,函数可以作为变量,可以接收函数,还可以返回函数。历史上研究函数式编程的理论是Lambda演算,所以我们经常把支持函数式编程的编码风格称为Lambda表达式。

Lambda 表达式 ,简单来讲就是,Lambda 允许把函数作为一个方法的参数(函数作为参数传递到方法中)。在Java中,不能返回函数。

用法

在Java程序中,我们经常遇到一大堆单方法接口,即一个接口只定义了一个方法:

  • Comparator
  • Runnable
  • Callable

Comparator为例,我们想要调用Arrays.sort()时,可以传入一个Comparator实例,以匿名类方式编写如下:

String[] array = new String[] { "Apple", "Orange", "Banana", "Lemon" };
Arrays.sort(array, new Comparator<String>() {
    public int compare(String s1, String s2) {
        return s1.compareTo(s2);
    }
});

上述写法非常繁琐。从Java 8开始,我们可以用Lambda表达式替换单方法接口。改写上述代码如下:

String[] array = new String[] { "Apple", "Orange", "Banana", "Lemon" };
        Arrays.sort(array, (s1, s2) -> {
            return s1.compareTo(s2);
        });

采用lambda表达式比这种匿名内部类的写法大大简化了。

观察Lambda表达式的写法,它只需要写出方法定义:

(s1, s2) -> {
    return s1.compareTo(s2);
}

由此可以看出,lambda表达式的关键在于输入和输出,即参数和返回值,方法名可以省略。

如果括号中的语句只有一行,还可以简写:

Arrays.sort(array, (s1, s2) -> s1.compareTo(s2));

 输入参数的类型和返回值的类型都是由编译器自动推断的,这里推断出的返回值是int,因此,只要返回int,编译器就不会报错。

相比于匿名内部类的情况,lambda表达式,省略了class的定义,省略了输入输出参数类型的,省略了方法名,只需要专注于方法体的编写,简化了代码。

原理

既然lambda表达式如此好用,那么如何定义一个方法,使其可以使用lambda表达式作为输入参数呢?

Lambda表达式,本质上是一个匿名方法,在Java1.8中使用单方法接口来表示,并定义了一个新的注解:@FunctionalInterface来进行标识。

我们来看看Arrays.sort()是如何定义的:

public static <T> void sort(T[] a, Comparator<? super T> c) {
        if (c == null) {
            sort(a);
        } else {
            if (LegacyMergeSort.userRequested)
                legacyMergeSort(a, c);
            else
                TimSort.sort(a, 0, a.length, c, null, 0, 0);
        }
    }

这里可以看到,sort方法接收一个数组和一个 Comparator,我们再来看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);
        };
    }
...

}

这是一个单方法接口,标注了@FunctionalInterface,只有一个抽象方法compare。(equals属于Object,不算)

自定义

这里我们使用一个例子来定义应用lambda表达式:

这里我们设计一个int数组自定义变换后求和的接口,这里默认是数组所有元素加总求和,但是可以自定义数组中的元素经过变换(如加倍,乘方)后求和。

定义一个函数式接口:

@FunctionalInterface
public interface Compute {
    int compute(int number);
}

调用接口的类:

public class MyList {

    public static int mySum(int[] numbers, Compute compute){
        if (compute == null){
            return sum(numbers);
        }else {
            return Arrays.stream(numbers).map(compute::compute).sum();
        }
    }

    public static int sum(int[] numbers){
        return Arrays.stream(numbers).sum();
    }
}

测试:

public class Main {

    public static void main(String[] args) {

        int[] numbers = {1,2,3};
        int result = MyList.mySum(numbers, null);
        int doubleResult = MyList.mySum(numbers, number -> number * 2);
        System.out.println("result : " +  result);
        System.out.println("doubleResult: " + doubleResult);
    }
}

 

这里可以看到,默认的输出是加总和,可以自定义为 number * 2 后加和,注意这里的lambda表达式为:number -> number * 2,这里省略了方法名,输入输出参数类型。

与接口方法 int compute(int number) 保持一致就可以了。

这里的lambda表达式还可以独立成方法,如下:

public class Main {

    public static void main(String[] args) {

        int[] numbers = {1,2,3};
        int result = MyList.mySum(numbers, null);
        int doubleResult = MyList.mySum(numbers, Main::multiply);
        System.out.println("result : " +  result);
        System.out.println("doubleResult: " + doubleResult);
    }


    public static int doubleValue(int number){
        return number * 2;
    }

    public static int multiply(int number){
        return number * number;
    }
}

这里用Main::multiply,即类名::方法名来简写。

因为Compute接口定义的方法是int compute(int),和静态方法int multiply(int)相比,除了方法名外,方法参数一致,返回类型相同,因此,我们说两者的方法签名一致,可以直接把方法名作为Lambda表达式传入:

MyList.mySum(numbers, Main::multiply);

注意:在这里,方法签名只看参数类型和返回类型,不看方法名称,也不看类的继承关系。

构造对象

lambda表达式还有一个很神奇的应用,就是引用构造方法创建对象数组,即批量创建对象。

这个需要在了解了Stream之后使用,具体参考:

 

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值