第八章 lamdba表达式

第八章 lamdba表达式

1 介绍
Lambda表达式
    作用: 简化匿名内部类对象

    使用前提: 函数式接口
    函数式接口: 只有一个必须被重写的抽象方法的接口称为函数式接口
    检查函数式接口注解: @FunctionalInterface 强制检测

    语法:
        () ->  {}
        () :  要重写的抽象方法的参数列表
        -> :  箭头函数 lambda符号 ,具有上下文推到的作用
        {} :  要重写抽象方法的方法体

在这里插入图片描述

1.1 写法
  • Lambda表达式是一种匿名函数,也可以把它理解为一段代码,可以当称数据一样传递,使我们写出的代码更加的简洁、更灵活。

  • Java8中的Lambda表达式的操作符是-> ,称为箭头操作符或者是Lambda操作符,箭头操作符将Lambda分隔成2个部分。

    • 左侧:Lambda表达式的参数列表,是对应接口中抽象方法的参数列表。
    • 右侧: Lambda表达式中所需要执行的功能,即Lambda体,也是对应接口中抽象方法的实现。
  • 语法结构

    • (形参列表)->{方法体|功能的实现|操作集合}

    注意:Lambda表达式需要函数式接口的支持,Lambda表达式可以用来简化匿名内部类,但是不是所有的匿名内部类都可以用Lambda表达式

使用前提
  • 函数式接口()只有一个必须被重写的抽象方法的接口称为函数式接口
    • 检查函数式接口注解: @FunctionalInterface 强制检测
1.1.1 写法1-常规写法()->{}
//函数式接口  有且只有一个抽象方法可以被重写 如果有继承父类接口那么也只能有一个,多个的话就不是函数式接口
//无参数 无返回值
@FunctionalInterface
interface Smoke{
   void smoking();
}
public class Class001_Lambda {
    public static void main(String[] args) {
		//默认的匿名内部类的写法
		/*Smoke s = new Smoke(){

            @Override
            public void smoking() {
                System.out.println("边吸烟边吐烟圈...");
            }
        };*/
		//可以通过Lambda表达式来进行简化
        Smoke s = () -> {System.out.println("边吸烟边吐烟圈...");};
    }
}    
1.1.2 lambda写法2 :
//函数式接口  有且只有一个抽象方法可以被重写 如果有继承父类接口那么也只能有一个,多个的话就不是函数式接口
@FunctionalInterface
interface Smoke{
   void smoking();
}
public class Class001_Lambda {
    public static void main(String[] args) {
		//默认的匿名内部类的写法
		/*Smoke s = new Smoke(){

            @Override
            public void smoking() {
                System.out.println("边吸烟边吐烟圈...");
            }
        };*/
		//当lambda体(重写方法的方法体)中的语句只有一句,前后的{}可以省略
        Smoke s = () -> System.out.println("边吸烟边吐烟圈...");
    }
}
1.1.3 lambda写法3 :
//函数式接口  有多个参数 无返回值
@FunctionalInterface
interface Smoke{
   void smoking(int a,int b);
}
public class Class001_Lambda {
    public static void main(String[] args) {
		//默认的匿名内部类的写法
		/*Smoke s = new Smoke(){

            @Override
            public void smoking() {
                System.out.println("边吸烟边吐烟圈...");
            }
        };*/
		//参数的数据类型可以省略
        Smoke s = (x,y) -> System.out.println("边吸烟边吐烟圈...");
        Smoke s2 = (x,y) -> System.out.println("边吸烟边吐烟圈..."+x+y);
    }
}
1.1.4 lambda写法4 :
//函数式接口  仅有一个参数 
@FunctionalInterface
interface Smoke{
   void smoking(int a);
}
public class Class001_Lambda {
    public static void main(String[] args) {
		//默认的匿名内部类的写法
		/*Smoke s = new Smoke(){

            @Override
            public void smoking() {
                System.out.println("边吸烟边吐烟圈...");
            }
        };*/
		//参数只有一个,前后的()可以省略
        Smoke s = x -> System.out.println("边吸烟边吐烟圈...");
        Smoke s2 = x -> System.out.println("边吸烟边吐烟圈..."+x+y);
    }
}
1.1.5 lambda写法5 :
//函数式接口  仅有一个参数 
@FunctionalInterface
interface Smoke{
   int smoking(int a);
}
public class Class001_Lambda {
    public static void main(String[] args) {
		//默认的匿名内部类的写法
		/*Smoke s = new Smoke(){

            @Override
            public void smoking() {
                System.out.println("边吸烟边吐烟圈...");
            }
        };*/
		//当方法存在返回值类型,{}lambda体中只有一条语句,并且是return语句,前后的{}与return关键字可以一起省略
        //Smoke s = x -> return x*2;//可以简化成
        Smoke s = x -> x*2;
        
    }
}
1.2 应用
public class lambdatest {
    public static void main(String[] args) {
        //调用接口对象  匿名内部类
//        Fly f =new Fly() {
//            @Override
//            public void fly1() {
//                System.out.println("自由的飞翔~");
//            }
//        };
        //1 默认的写法
//        Fly f = () -> {
//            System.out.println("凤凰传奇!");
//        };
        //2 如果{} 只有一行语句   {}可以省略
//        Fly f = () -> System.out.println("荷塘月色~");
        //3 如果方法有形参  那么参数数据类型可以省略
//        Fly f = (a,b) -> System.out.println("套马滴汉子"+(a*b));
        //4 参数只有一个的话  前后的()可以省略

//        Fly f = a ->System.out.println("威威雄壮!"+a);
        //5 当方法存在返回值类型 {}lambda体体只有一行语句 且是return语句 前后的{} 和return可以省略
        Fly f = a -> a*2;
        f.fly1(3);
        System.out.println(f.fly1(4));

        f.haha();
    }
}
//接口  如果有继承就不能有方法存在
@FunctionalInterface
interface Fly extends Fly1{
    default void haha(){
        System.out.println("我是接口的默认方法!");
    };
}
//父类接口
interface Fly1{
    int fly1(int x);
}
1.3 常用的内置函数式接口
四大内置函数式接口  必须掌握***
*         消费型接口 Consumer<T>
*             void accept(T t)  有一个参数没有返回值
*
*         函数型接口 Function<T,R>
*             R apply(T t)    有一个参数一个返回值
*
*         段言型接口 Predicate<T>
*             boolean test(T t)   一个参数一个boolean返回值
*
*         供给型接口 Supplier<T>
*             T get()             没有参数一个返回值
*

消费性:只进不出
供给型:白手起家,空手套白狼
函数型:礼尚往来
断定行:鉴定评审
注意:关注的只有接口中方法的参数和返回值即可
举例说明

import java.util.ArrayList;
import java.util.List;
import java.util.function.Consumer;
import java.util.function.Function;
import java.util.function.Predicate;
import java.util.function.Supplier;

/**
 *四大内置函数式接口  必须掌握***
 *         消费型接口 Consumer<T>
 *             void accept(T t)  有一个参数没有返回值
 *
 *         函数型接口 Function<T,R>
 *             R apply(T t)    有一个参数一个返回值
 *
 *         段言型接口 Predicate<T>
 *             boolean test(T t)   一个参数一个boolean返回值
 *
 *         供给型接口 Supplier<T>
 *             T get()             没有参数一个返回值
 *
 */
public class FunctionTest001 {
    public static void main(String[] args) {
        //消费
        comMoney(1314.0,a-> System.out.println("今天给UZI主播消费了¥"+a));
        comMoney(520.0,a-> System.out.println("今天买皮肤消费了¥"+a));
        //字符串转大写
        System.out.println(getStr("hahahahahah", a -> a.toUpperCase()));
        System.out.println(getStr("hahahahahah", a -> a.concat("我是字符串")));
        //供给型  随机50~100的整数 6次存储到list集合中
        System.out.println(getIn(6,()->(int)(Math.random()*51+50)));
        System.out.println(getIn(8,()->666));
        //过滤长度不符合要求的
        System.out.println(testPredicate(List.of("asfagag", "gaaggg", "sdgdsgsg", "as", "dgh", "dd"),s->s.length()<=4));


    }
    //段言型  就是返回boolean 过滤
    public static List<String> testPredicate(List<String> list, Predicate<String> pre){
        //声明一个新的list来接收过滤后的集合
        List<String> ls =new ArrayList<>();
        //遍历原集合 并判断
        for (String str:list){
            if (pre.test(str)){
                ls.add(str);
            }
        }
        return ls;

    }
    //供给型
    public static List<Integer> getIn(int a, Supplier<Integer> sup){
        List<Integer> list = new ArrayList<>();
        for (int i=1;i<=a;i++){
            list.add(sup.get());
        }
        return list;
    }
    //函数型方法
    public static String getStr(String str, Function<String,String> fun){
        return fun.apply(str);

    }
    //写一个消费型的静态方法
    public static void comMoney(Double money, Consumer<Double> co){
        co.accept(money);
    }
    //
}

打印结果
在这里插入图片描述

1.4 方法的调用|引用
方法引用 : 当lambda体{}的实现,已经有另外的方法实现了,可以通过方法引用来引用那个方法(引用|调用)

    方法引用可以理解为: lambda表达式的另外一种表现形式
                      方法引用是用来简化lambda表达式

    要求:1)lambda体的实现,是通过调用|引用另外一个方法实现的
         2)引用的方法的参数列表与返回值要求与要重写的抽象方法的参数列表与返回值保持一致,自动匹配
         3)如果返回值匹配,但是参数列表不完全匹配,可以考虑是否满足第三种情况: 使用 类名::成员方法名 简化
            如果参数列表只有一个,这个参数作为内部调用方法的对象存在
            如果参数列表存在多个,第一个参数作为内部调用方法的对象,第二个参数开始自动匹配内部所引用方法的参数列表


    使用方式:
        对象::成员方法名 : 对象真实存在
        类名::静态方法名
        类名::成员方法名 : 对象自动匹配,对象是lambda的参数提供的
public class FunctionTest003 {
    public static void main(String[] args) {
        test03();
        test02();
        test01();
    }
    public static void test03(){
        //类名::成员方法名  lambda
//        BiPredicate<String,String> bp =(x,y) -> x.equals(y);
        //分析:lambad体是否是通过调用了另外一个 方法实现的equals
        //2)如果参数列表存在多个,第一个参数作为内部调用方法的对象,第二个参数开始自动匹配内部所引用方法的参数列表
        BiPredicate<String,String> bp = String :: equals;
        System.out.println(bp.test("zhangsan", "zhangsan"));
    }
    //类名::静态方法
    public static void test02(){
        //分析: 1)lambda体的实现是通过调用了另一个方法实现的,Math.max()
        // 2)lambda的参数列表与返回值刚好匹配到内部所调用的max()方法的参数列表与返回值
        //函数型接口 2个参数一个返回值 BiFunction
//        BiFunction<Integer,Integer,Integer> bf = (x,y) -> Math.max(x,y);
        BiFunction<Integer,Integer,Integer> bf = Math::max;
        //打印结果
        System.out.println(bf.apply(2452,-245235));
    }
    //对象::成员方法名
    public static void test01(){
        //分析:
        //1)lambda体的实现是通过调用了println方法实现的
        //2)抽象方法accept参数列表与返回值类型匹配到println方法参数列表与返回值
        //--> 方法引用简化
        Consumer com = o -> System.out.println(o);
        com.accept("我是消费型接口");

        Consumer com1 = System.out ::println;
        com1.accept("我也是消费型");
    }
}

结果打印
在这里插入图片描述

1.5 构造器的调用|引用

方法引用 ::
构造器引用
数据类型::new
要求: lambda参数列表与构造器的参数列表保持一致

数组引用:
类型::new


```java
package com.yjxxt.geng.lambda001;

import java.util.Arrays;
import java.util.function.Function;

/*
    方法引用 ::
    构造器引用
        数据类型::new
        要求: lambda参数列表与构造器的参数列表保持一致

    数组引用:
        类型::new
 */
public class FunctionTest004 {
    public static void main(String[] args) {
        //构造器的引用
        Function<String,String> fun = s -> new String(s);
        System.out.println(fun.apply("hahahah"));

        //数组 引用
        Function<Integer,int[]> sup2 = (i)->new int[i];
        sup2 = int[]::new;
        System.out.println(sup2.apply(6));//未重写toString方法  打印的是对象的地址
        //默认值都是0
        System.out.println(Arrays.toString(sup2.apply(6)));
    }
}

结果打印
在这里插入图片描述

1.6 Stream流
1.6.1 概念
Java8 API添加了一个新的抽象称为流 Stream ,可以让你以一种声明的方式处理数据。

Stream 使用一种类似用 SQL 语句从数据库查询数据的直观方式来提供一种对 Java 集合运算和表达的高
阶抽象
。Stream API可以极大提高Java程序员的生产力,让程序员写出高效率、干净、简洁的代码。这
种风格将要处理的元素集合看作一种流, 流在管道中传输, 并且可以在管道的节点上进行处理, 比如
筛选, 排序,聚合等。
元素流在管道中经过中间操作(intermediate operation)的处理,最后由最终操作(terminal
operation)得到前面处理的结果。
Stream(流)是一个来自数据源的元素队列并支持聚合操作
元素是特定类型的对象,形成一个队列。 Java中的Stream并不会存储元素,而是按需计算
数据源 流的来源。 可以是集合,数组等。
聚合操作 类似SQL语句一样的操作, 比如filter, map, reduce, find, match, sorted等。
Java 8 中的 Stream 是对集合(Collection)对象功能的增强,它专注于对集合对象进行各种非常便利、
高效的聚合操作(aggregate operation),或者大批量数据操作 (bulk data operation)。

Stream :
    数据的渠道,用来操作由数据源(数组,集合)产生元素序列
    数组|集合关注数据的存储,Stream关注的是对数据的计算

    可以对根据数据源锁产生的数据一系列式的流式计算
1.6.2 特点

​ 1)Stream本身不能存储数据

​ 2)Stream不会修改原对象|数据源中的数据,在每次进行流式操作后会返回一个持有结果的新的流

​ 3)惰性加载|延迟执行: 在进行stream操作时候不会马上执行,会在获取结果时一起执行

​ 4)流式一次性的流,流是不能重复使用,因为这个流已经被使用消费了

1.6.3 使用操作
  • 创建流:
    • Collection中的成员Stream()
    • Arrays.stream(数组)
    • Stream.of(数据)
public class Class001_Stream {
    public static void main(String[] args) {
        // 1. Collection中的成员stream()
        List<Integer> ls = List.of(1,2,3,4,5);
        Stream<Integer> stream1 = ls.stream();
        ls.parallelStream();

        //2. Arrays.stream(数组)
        String[] arr = {"ABC","AB","C"};
        Stream stream2 = Arrays.stream(arr);
        System.out.println(stream2);

        //3.Stream.of(数据)
        Stream<String> stream3 = Stream.of("abc","asd","aaa");
    }
}
  • Stream使用过程
    • 获取流
    • 一系列流式的中间操作(返回持有结果的新的流)
    • 终止行为(返回真实结果)
  • 中间操作:筛选与切片
    • filter:过滤
    • limit:限制从流中获得前n个数据
    • distinct:去重
    • skip:跳过前nge数据
public class Class002_Stream {
    public static void main(String[] args) {
        List<Employee> list = List.of(
                new Employee(1001,"zhangsan",18,20000),
                new Employee(1002,"lisi",25,18000),
                new Employee(1003,"wangwu",30,15000),
                new Employee(1004,"zhaoliu",28,10000),
                new Employee(1004,"zhaoliu2",28,10000),
                new Employee(1004,"zhaoliu",28,10000)
        );
        //1.获取流
        Stream<Employee> stream = list.stream();

        //2.中间操作
        stream = stream.filter(employee ->employee.getAge()>18)
              .distinct()/*要求重写hashCode与equals*/
              .limit(3)
              .skip(1);

        //3.终止行为
        stream.forEach(System.out::println);
    }
}

结果打印

在这里插入图片描述

1004,“zhaoliu”,28,10000),
new Employee(1004,“zhaoliu2”,28,10000),
new Employee(1004,“zhaoliu”,28,10000)
);
//1.获取流
Stream stream = list.stream();

    //2.中间操作
    stream = stream.filter(employee ->employee.getAge()>18)
          .distinct()/*要求重写hashCode与equals*/
          .limit(3)
          .skip(1);

    //3.终止行为
    stream.forEach(System.out::println);
}

}

  • 0
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值