java8-Lambda学习笔记

Lambda表达式

Lambda表达式简介

​ lambda表达式是从java8开始引入的,目的主要是为了简化代码,允许把函数作为⼀个⽅法的参数传递进⽅法中。在java8之前通常使用的是匿名类,但匿名类的问题是:如果匿名类的实现非常简单(例如仅包含一个方法的接口),则匿名类的语法可能看起来笨拙且不清楚,而且不能将一个函数(方法)作为参数传递给另一个方法。而使用Lambda表达式使您能够执行此操作,将功能视为方法参数,或将代码视为数据。

​ Lambda表达式是⼀种匿名函数(对Java⽽⾔这并不完全准确),通俗的说,它是没有声明的⽅法,即没有访问修饰符、返回值声明和名字的⽅法。使⽤Lambda表达式的好处很明显就是可以使代码变的更加简洁紧凑。Lambda表达式的使⽤场景与匿名类的使⽤场景⼏乎⼀致,都是在某个功能(⽅法)只使⽤⼀次的时候。

Lambda表达式语法

Lambda表达式通常使⽤(param)->(body)语法书写,基本格式如下:

//没有参数
() -> body
// 1个参数
(param) -> body
// 或 (param) ->{ body; }
// 多个参数
(param1, param2...) -> { body }
// 或 (type1 param1, type2 param2...) -> { body }
// ⽆参数,返回值为字符串“lambda”
() -> "lambda";
// 如果有1个String参数,直接打印结果
(System.out::println);
// 或
(String s) -> System.out.print(s)
// 有1个参数(数字),返回2倍值 
x -> 2 * x;
// 有2个参数(数字),返回差值
(x, y) -> x – y
 
// 有2个int型整数,返回和值 
(int x, int y) -> x + y

Lambda 表达式的结构总结如下:

  • Lambda 表达式可以具有零个,一个或多个参数。
  • 可以显式声明参数的类型,也可以由编译器自动从上下文推断参数的类型。例如 (int a) 与刚才相同 (a)
  • 参数用小括号括起来,用逗号分隔。例如 (a, b)(int a, int b)(String a, int b, float c)
  • 空括号用于表示一组空的参数。例如 () -> 42
  • 当有且仅有一个参数时,如果不显式指明类型,则不必使用小括号。例如 a -> return a*a
  • Lambda 表达式的正文可以包含零条,一条或多条语句。
  • 如果 Lambda 表达式的正文只有一条语句,则大括号可不用写,且表达式的返回值类型要与匿名函数的返回类型相同。
  • 如果 Lambda 表达式的正文有一条以上的语句必须包含在大括号(代码块)中,且表达式的返回值类型要与匿名函数的返回类型相同。

Lambda用法案例

对字符串集合进行排序:

package com.lambda.demo;

import java.util.*;

/**
 * @Author LQY
 * @Date 2020/8/14
 * @Description lambda表达式案例
 */
public class MyDemo {


    /**
     * java8之前对字符串集合进行排序常用操作
     */
    public static void strListSortFun() {
        System.out.println("======对字符串集合排序常规操作=========");
        List<String> names = Arrays.asList("peter", "anna", "mike", "xenia");
        System.out.println("++++++++++排序前集合+++++++++++++++++++");
        for (String name : names) {
            System.out.println(name);
        }
        Collections.sort(names, new Comparator<String>() {
            @Override
            public int compare(String a, String b) {
                return b.compareTo(a);
            }
        });
        System.out.println("++++++++++排序后集合+++++++++++++++++++");
        for (String name : names) {
            System.out.println(name);
        }
    }

    /**
     * lambda对字符串集合进行排序
     */
    public static void strListSortLambdaFun() {
        System.out.println("======使用lambda对字符串集合排序操作=========");
        List<String> names = Arrays.asList("peter", "anna", "mike", "xenia");
        System.out.println("++++++++++排序前集合+++++++++++++++++++");
        //lambda表达式遍历集合的写法
        //names.forEach(str -> System.out.println(str));
        names.forEach(System.out::println);
//     lambada第一种写法写法
//     Collections.sort(names, (String a, String b) -> {
//          return b.compareTo(a);
//      });
//      lambada第二种写法写法,当{}代码块里只有一行代码时大括号对{}和return关键字都省略不要
//      Collections.sort(names, (String a, String b) -> b.compareTo(a));
        System.out.println("++++++++++排序后集合+++++++++++++++++++");
//      lambada第三种写法写法,Java编译器能够自动识别参数的类型,所以你就可以省略掉类型不写
        Collections.sort(names, (a, b) -> b.compareTo(a));
         names.forEach(System.out::println);
    }

    public static void main(String[] args) {
        strListSortFun();
        strListSortLambdaFun();
    }


}

运行结果:

在这里插入图片描述

自定义函数式接口案例:

Lambda表达式如何匹配Java的类型系统?每一个lambda都能够通过一个特定的接口,与一个给定的类型进行匹配。一个所谓的函数式接口必须要有且仅有一个抽象方法声明。每个与之对应的lambda表达式必须要与抽象方法的声明相匹配。由于默认方法不是抽象的,因此你可以在你的函数式接口里任意添加默认方法。

任意只包含一个抽象方法的接口,我们都可以用来做成lambda表达式。为了让你定义的接口满足要求,你应当在接口前加上@FunctionalInterface 标注。编译器会注意到这个标注,如果你的接口中定义了第二个抽象方法的话,编译器会抛出异常。

package com.lambda.demo;

/**
 * @Author LQY
 * @Date 2020/8/14
 * @Description 数值相加工具接口
 */
@FunctionalInterface
public interface INumberUtil<T> {
    /**
     * 求两数之和的方法
     * @param param1 数1
     * @param param2 数2
     * @return
     */
    T sumFun(T param1,T param2);

}

/**
     * 使用自定义函数接口,用lambda方式计算两数之和
     */
    public static void sumLambdaFun(){
        System.out.println("======使用自定义函数接口,用lambda方式计算两数之和=========");
        INumberUtil<Integer> numberUtil = (Integer num1,Integer num2)->Integer.sum(num1,num2);
        Integer sumResult = numberUtil.sumFun(10,20);
        System.out.println("=======计算结果为:"+sumResult+"========================");
    }

运行结果:
在这里插入图片描述

使用::关键字

Java 8 允许你通过::关键字获取方法或者构造函数的的引用

列如改造上面的求两数和的方法,

INumberUtil<Integer> numberUtil = (Integer num1,Integer num2)->Integer.sum(num1,num2);

用双::改造为:

INumberUtil<Integer> numberUtil = Integer::sum;

如下:

/**
     * 使用自定义函数接口,用lambda方式计算两数之和
     */
    public static void sumLambdaFun(){
        System.out.println("======使用自定义函数接口,用lambda方式计算两数之和=========");
        //常规用法1:
        //INumberUtil<Integer> numberUtil = (Integer num1,Integer num2)->Integer.sum(num1,num2);
        //常规用法2:
        INumberUtil<Integer> numberUtil = Integer::sum;
        Integer sumResult = numberUtil.sumFun(10,20);
        System.out.println("=======计算结果为:"+sumResult+"========================");
    }

使用::关键字引用构造函数:

1.先创建一个对象实体(用户)

package com.lambda.demo;

/**
 * @Author LQY
 * @Date 2020/8/14
 * @Description 用户对象
 */
public class User {

    /**
     * 用户名称
     */
    private String userName;

    /**
     * 联系电话
     */
    private String phone;

    public User() {}

    public User(String userName, String phone) {
        this.userName = userName;
        this.phone = phone;
    }

    @Override
    public String toString() {
        return "User{" +
                "userName='" + userName + '\'' +
                ", phone='" + phone + '\'' +
                '}';
    }
}

2.创建一个用户对象的工厂接口,用于生产用户对象

package com.lambda.demo;

/**
 * @Author LQY
 * @Date 2020/8/14
 */
@FunctionalInterface
public interface UserFactory<User> {
    /**
     * 创建用户
     * @param userName 用户名称
     * @param phone 联系电话
     * @return
     */
    User createUser(String userName,String phone);
}

3.使用案例:

/**
     * 使用::关键字引用构造函数
     */
    public static void referenceConstructorFun(){
        System.out.println("======使用::关键字引用构造函数=========");
        UserFactory<User> userUserFactory = User::new;
        User user = userUserFactory.createUser("测试用户1","18312398810");
        System.out.println("=====user info:"+user.toString()+"==================");
    }

Lambda的变量范围

对于lambdab表达式外部的变量,其访问权限的粒度与匿名对象的方式非常类似。你能够访问局部对应的外部区域的局部final变量,以及成员变量和静态变量。

 /**
     * lambda范围
     */
    public static void lambdaScope(){
        final Integer num = 1;
        //此地方引用了lambda表达式以外的final变量num
        INumberUtil<Integer> numberUtil = (Integer num1,Integer num2) -> Integer.sum(num1,num);
        //按代码逻辑,实质相加的是num1+num而不是num1+num2,所以结果是1+1=2
        System.out.println("结果为:"+numberUtil.sumFun(1, 2));
    }

运行结果:
在这里插入图片描述

但是与匿名对象不同的是,变量num并不需要一定是final。下面的代码依然是合法的:

/**
     * lambda范围
     */
    public static void lambdaScope(){
        Integer num = 1;
        //此地方引用了lambda表达式以外的final变量num
        INumberUtil<Integer> numberUtil = (Integer num1,Integer num2) -> Integer.sum(num1,num);
        //按代码逻辑,实质相加的是num1+num而不是num1+num2,所以结果是1+1=2
        System.out.println("结果为:"+numberUtil.sumFun(1, 2));
    }

由于num在编译的时候被隐式地当做final变量来处理,所以代码里不能改变num的值,否则则是不合法的,如下:

/**
     * lambda范围
     */
    public static void lambdaScope(){
        Integer num = 1;
        //此地方引用了lambda表达式以外的final变量num
        INumberUtil<Integer> numberUtil = (Integer num1,Integer num2) -> Integer.sum(num1,num);
        //按代码逻辑,实质相加的是num1+num而不是num1+num2,所以结果是1+1=2
        System.out.println("结果为:"+numberUtil.sumFun(1, 2));
        num = 2;
    }

结果:
在这里插入图片描述

在lambda表达式的内部能获取到对成员变量或静态变量的读写权。这种访问行为在匿名对象里是非常典型的:

package com.lambda.demo;

/**
 * @Author LQY
 * @Date 2020/8/14
 * @Description lambda表达式变量范围案例,与局部变量不同,我们在lambda表达式的内部能获取到对成员变量或静态变量的读写权。这种访问行为在匿名对象里是非常典型的。
 */
public class LambdaScopeDemo {
    static int outerStaticNum;
    int outerNum;

    void testScopes() {
        Integer num = 1;
        INumberUtil<Integer> numberUtilA = (Integer num1,Integer num2) ->{
            outerNum = 250;
            return Integer.sum(num1,outerNum);
        };
        //按代码逻辑,实质相加的是num1+outerNum而不是num1+num2,所以结果是1+250=251
        System.out.println("====numberUtilA 执行结果为:"+numberUtilA.sumFun(1, 2));

        INumberUtil<Integer> numberUtilB = (Integer num1,Integer num2) ->{
            outerStaticNum = 255;
            return Integer.sum(num1,outerStaticNum);
        };
        //按代码逻辑,实质相加的是num1+outerStaticNum而不是num1+num2,所以结果是1+255=256
        System.out.println("====numberUtilB 执行结果为:"+numberUtilA.sumFun(1, 2));
    }
}

内置函数式接口:

JDK 1.8 API中包含了很多内置的函数式接口。有些是在以前版本的Java中大家耳熟能详的,例如Comparator接口,或者Runnable接口。对这些现成的接口进行实现,可以通过@FunctionalInterface 标注来启用Lambda功能支持。

此外,Java 8 API 还提供了很多新的函数式接口,⽐如通过Predicate函数式接⼝以及lambda表达式,可以向API⽅法添加逻辑,⽤更少的代码⽀持更多的动态⾏为。

用法如下:

public static void testPredicate() {
        List<String> list = Arrays.asList("Aaa", "B", "C", "D");
        //值等于‘B’的
        list.stream().filter((str) -> ("B".equals(str))).forEach(System.out::println);
        //值的长度大于2的
        list.stream().filter((str) -> (((String) str).length() > 2)).forEach(System.out::println);

    }

运行结果:

在这里插入图片描述

Predicate可以将多个条件合并成⼀个,如下:

public static void testPredicate2() {
        List<String> list = Arrays.asList("Aaa", "BA", "C", "D");
        //包含'A'的
        Predicate condition1 = (str) -> (((String)str).contains("A"));
        //长度大于2的
        Predicate condition2 = (str) -> (((String) str).length() > 2);
        //包含'A'的并且长度大于2的
        list.stream().filter(condition1.and(condition2)).forEach((str)->{System.out.println("==包含'A'的并且长度大于2的=="+str);});
        System.out.println("********************************************************");
        //包含'A'的或者长度大于2的
        list.stream().filter(condition1.or(condition2)).forEach((str)->{System.out.println("==包含'A'的或者长度大于2的=="+str);});

    }

运行结果:
在这里插入图片描述

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值