Java中的Lambda表达式入门

Lambda表达式

从JDK8时引入

函数式编程

函数式编程(Functional programming)是一种思想方式。其忽略面向对象的复杂语法,强调做什么,而不是谁去做。

而面向对象的思想则是:先找对象,让对象做事情。

Lambda表达式的标准格式

Lambda表达式是JDK8开始后的一种新语法形式。

( ) -> {
}
  1. ()  对应着方法的形参
  2.  ->  固定格式
  3.  {}  对应着方法的方法体

注意点:

● Lambda表达式可以用来简化匿名内部类的书写

● Lambda表达式只能简化函数式接口的匿名内部类的写法

● 函数式接口:有且仅有一个抽象方法的接口(抽象类不行)叫做函数式接口,接口上方可以加@FunctionalInterface注解(加了@FunctionalInterface注解后如果定义该接口是不符合函数式接口的规范将会报错)

Lambda应用示例

首先我们定义一个函数式接口ISport,代码:

/**
 * 本接口表示会某项运动
 * 有且只能有一个待实现的方法,本例中为sport()方法
 */
@FunctionalInterface         // 函数式接口的校验注解
public interface ISport {
    void sport();           // 接口唯一一个方法
}

接着我们定义一个Person类,它有一个doSport方法需要使用ISport的实例作为参数,我们在调用Person类的doSport方法时,需要传入一个ISport接口的实例,在引入Lambda之前,我们一般会编写一个匿名类,在引入lambda之后,我们可以简化该匿名类的编写方式,代码:

public class Person {
    public static void main(String[] args) {

        // 内部匿名类的方式实现ISport接口
        doSport(new ISport() {
            @Override
            public void sport() {
                System.out.println("去游泳了。");
            }
        });

        // lambda的语法方式实现ISport接口
        doSport(() -> {
            System.out.println("去做瑜伽了。");
        });
    }

    // 定义一个方法要使用ISport对象为参数
    public static void doSport(ISport s){
        s.sport();
    }
}

// 本段代码运行后输出结果为:
// 去游泳了。
// 去做瑜伽了。

Lambda表达式的简略写法

lambda的省略规则:

总原则是 可推导,则可省略。也即凡是可以推导出来都可以省略,具体为:

  1. 参数类型可以省略不写。
  2. 如果只有一个参数,参数类型可以省略,同时括号()也可以省略。
  3. 如果Lambda表达式的方法体只有一行,大括号,分号,return可以省略不写,但这三者需要同时省略,不能省略其中的一个或两个。

如下代码,我们重写Arrays的sort类,可实现升降序排序:

import java.util.Arrays;
import java.util.Comparator;

public class SortWithDirection {

    public static void main(String[] args) {
        Integer[] arr = {2, 3, 1, 5, 6, 7, 8, 4, 9};

        // 1.匿名类写法
        Arrays.sort(arr, new Comparator<Integer>() {
            @Override
            public int compare(Integer o1, Integer o2) {
                return o1 - o2;
            }
        });

        // 2.标准lambda语法
        Arrays.sort(arr, (Integer o1, Integer o2) ->{
                return o1 - o2;
        });

        // 3.省略参数类型
        Arrays.sort(arr, (o1, o2) ->{
            return o1 - o2;
        });

        // 4.省略大括号、分号、return
        Arrays.sort(arr, (o1, o2) -> o1 - o2);
    }

}

又如,通过字符串的长度对字符串数组进行排序(升序,即短的排前面):

import java.util.Arrays;

/**
 * 通过字符串的长度对字符串数组进行排序
 */
public class StringSortByLen {
    public static void main(String[] args) {
        // 需要排序的字符串数组
        String[] arr = {"a","abcd","abc","ab"};
        System.out.println(Arrays.toString(arr));
        // 重写Arrays.sort方法进行排序,使用lambda简略写法,一行搞定
        Arrays.sort(arr, (s1, s2) -> s1.length() - s2.length());
        System.out.println(Arrays.toString(arr));
    }
}
// 本代码的输出为:
// [a, abcd, abc, ab]
// [a, ab, abc, abcd]

双冒号::操作符写法

这种方式被称为方法引用(Method Reference),它也可以提高lambda表达式的简洁性,它适用于以下场景:当你的lambda表达式只是用来调用一个已存在的方法时,也即你不用在lambda表达式里写自己的业务逻辑,而是单纯调用其他已经存在方法,比如输出一个字符串可以写作System.out::println。根据调用的方式不同可以分为:引用一个静态方法、引用一个特定对象的实例方法、引用一种特定类型的任一对象的实例方法、引用构造方法。

关于方法引用的官方文档地址

https://docs.oracle.com/javase/tutorial/java/javaOO/methodreferences.html

方法引用的几种方式,笔者简单的翻译了下:

种类 Kind语法 Syntax例子 Examples

引用一个静态方法

Reference to a static method

ContainingClass::staticMethodNamePerson::compareByAge
MethodReferencesExamples::appendStrings

引用一个特定对象的实例方法

Reference to an instance method of a particular object

containingObject::instanceMethodNamemyComparisonProvider::compareByName
myApp::appendStrings2

引用一种特定类型的任一对象的实例方法

Reference to an instance method of an arbitrary object of a particular type

ContainingType::methodNameString::compareToIgnoreCase
String::concat

引用构造方法

Reference to a constructor

ClassName::new

HashSet::new

请看示例,定义了一个Person类JavaBean,然后尝试调用Person List中的forEach方法,代码如下:

public class Person {
    private String ID;
    private String name;
    private int age;
    private float height;


    public Person() {
    }

    public Person(String ID, String name, int age, float height) {
        this.ID = ID;
        this.name = name;
        this.age = age;
        this.height = height;
    }

    /**
     * 获取
     * @return ID
     */
    public String getID() {
        return ID;
    }

    /**
     * 设置
     * @param ID
     */
    public void setID(String ID) {
        this.ID = ID;
    }

    /**
     * 获取
     * @return name
     */
    public String getName() {
        return name;
    }

    /**
     * 设置
     * @param name
     */
    public void setName(String name) {
        this.name = name;
    }

    /**
     * 获取
     * @return age
     */
    public int getAge() {
        return age;
    }

    /**
     * 设置
     * @param age
     */
    public void setAge(int age) {
        this.age = age;
    }

    /**
     * 获取
     * @return height
     */
    public float getHeight() {
        return height;
    }

    /**
     * 设置
     * @param height
     */
    public void setHeight(float height) {
        this.height = height;
    }

    public String toString() {
        return "Person{ID = " + ID + ", name = " + name + ", age = " + age + ", height = " + height + "}";
    }

    public void printPerson(){
        System.out.println(this.toString());
    }
}

Lambda的写法如下,包含双引号操作符:

import java.util.Arrays;
import java.util.List;
import java.util.function.Consumer;

public class LambdaTest {
    public static void main(String[] args) {
        Person p1 = new Person("001","王伟",45,163.2F);
        Person p2 = new Person("002","张小美",31,158.6F);
        Person p3 = new Person("003","刘睿国",53,178.0F);
        Person p4 = new Person("004","费香香",23,162.0F);
        Person p5 = new Person("005","陈希",25,170.5F);
        Person p6 = new Person("006","金奇辉",41,193.2F);

        Person[] pArr = {p1, p2, p3, p4, p5};
        List<Person> pList = Arrays.asList(pArr);
        // 1.匿名类的写法
        pList.forEach(new Consumer<Person>() {
            @Override
            public void accept(Person person) {
                System.out.println(person.getName());
            }
        });
        System.out.println("----------------------------");

        // 2.Lambda标准写法
        pList.forEach((Person person) -> {
            System.out.println(person.toString());
        });
        System.out.println("----------------------------");

        // 3.Lambda简略写法1:省略参数类型
        pList.forEach((person)-> {
            person.printPerson();
        });
        System.out.println("----------------------------");

        // 4.Lambda简略写法2:省略参数类型和大括号、分号、return
        pList.forEach((person)->
                person.printPerson()
        );
        System.out.println("----------------------------");

        // 简略成一行:pList.forEach((person)->System.out.println(person.getName()));

        // 5.Lambda简略写法4:省略参数类型和大括号、分号、return,使用::双冒号调用方法
        // 此调用应属于:引用一种特定类型的任一对象的实例方法 Reference to an instance method of an         
        // arbitrary object of a particular type
        pList.forEach(Person::printPerson);
    }
}

Lambda小结

1、Lambda表达式的基本作用?

简化函数式接口的匿名内部类的写法。

2、Lambda表达式有什么使用前提?

必须是接口的匿名内部类,接口中只能有一个抽象方法

3、Lambda的好处?

Lambda是一个匿名函数,我们可以把Lambda表达式理解为是一段可以传递的代码,它可以写出更简洁、更灵活的代码,作为一种更紧凑的代码风格,使Java语言表达能力得到了提升。

笔者短评

笔者认为编码者在工作中如果盲目的引入Lambda,反而会造成代码可读性的降低,对于代码的后期维护可能并不友好。另外对于Java严谨的语法来说,简略的Lambda语句是对Java豪华型的一种挑战。笔者更倾向于使用匿名类。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

唐骁虎

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值