首页 > 其他分享 >Lambda 表达式

Lambda 表达式

时间:2023-04-03 16:36:15浏览次数:42  
标签:System 接口 println out public 表达式 Lambda

目录


Java8 新特性中有几个比较主要的特性,Lambda 表达式、函数式接口、方法引用和几个新增库 Stream API、Optional 类等。

Lambda 表达式简介

什么是 Lambda ?

  • Lambda 表达式其实就是一种匿名函数,在这之前大家可能还接触过匿名内部类,虽然也是一种匿名函数,但是它并没有 Lambda 表达式那么简洁。

  • Lambda 表达式的特点就是可以传递,在使用 Lambda 表达式的时候我们可以将代码像数据一样传递。

  • 使用 Lambda 表达式能够极大的减少我们代码的冗余,而且使用也相当的方便,熟练之后会大大加快我们写代码的速度。

函数式编程思想概述:

  • 在数学中,函数就是有输入量、输出量的一套计算方案,也就是“拿数据做操作”。

  • 面向对象思想强调“必须通过对象的形式来做事情”,而函数式思想则尽量忽略面向对象的复杂语法:“强调做什么,而不是以什么形式去做”。

  • Lambda 表达式就是函数式思想的体现。


Lambda 表达式入门案例

下面用 Java8 的方法与老版本进行使用对比:

  • 尝试找出公司员工年龄大于 35 岁的所有员工,并用不同的方法来实现一下。
  • 假设有一个 Staff 类来代表员工,并使用构造函数来创建员工的集合。

Staff 类:

public class Staff {
    int age;
    String name;

    public String getName() {
        return name;
    }

    public Staff(int age, String name) {
        this.age = age;
        this.name = name;
    }

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

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

    public int getAge() {
        return age;
    }

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

    public Staff(int age) {
        this.age = age;
    }
}

实现方式一:常规方法

public List<Staff> getStaffs(List<Staff> staffs) {

    List<Staff> stas = new ArrayList<>();

    for(Staff sta: staffs){
        if (sta.getAge()>=35){
            stas.add(sta);
        }
    }
    return stas;
}

@Test
public void test1(){

    List<Staff> list = getStaffs(staffs);

    for (Staff staf: list) {
        System.out.println(staf);
    }
}

实现方式二:策略设计模式

我们需要写一个接口,然后实行这个接口,最后是调用接口。这种方法看起来就比前面的那种更有层次,而且假如又需要查找员工工资大于 5000 的员工的话,就重新写一个类来实现接口就好了

import java.util.ArrayList;
import java.util.List;


// 接口
interface MyPredicate<Staff> {
    boolean test(Staff staff);
}


// 实现接口
class filterStaffByAge implements MyPredicate<Staff> {
    @Override
    public boolean test(Staff staff) {
        return staff.getAge() >= 35;
    }
}


// 测试类
public class Test {

    // 过滤
    public List<Staff> filterStaff(List<Staff> list, MyPredicate<Staff> mp){
        List<Staff> staffs = new ArrayList<>();

        for (Staff staff: list) {
            if (mp.test(staff)){
                staffs.add(staff);
            }
        }
        return staffs;
    }

    // 测试结果
    public static void main(String[] args) {
        ArrayList<Staff> staffs = new ArrayList<>();
        staffs.add(new Staff(24, "xiaohua"));
        staffs.add(new Staff(36, "xiaoming"));
        staffs.add(new Staff(21, "xiaoguang"));
        staffs.add(new Staff(37, "xiaoliuhua"));

        List<Staff> list = new Demo().filterStaff(staffs, new filterStaffByAge());
        for (Staff staff: list) {
            System.out.println(staff);
            // Staff{age=36, name='xiaoming'}
            // Staff{age=37, name='xiaoliuhua'}
        }
    }
}

实现方式三:匿名内部类

package com.annotation;

import java.util.ArrayList;
import java.util.List;


// 接口
interface MyPredicate<Staff> {
    boolean test(Staff staff);
}


// 测试类
public class Demo {

    // 过滤
    public List<Staff> filterStaff(List<Staff> list, MyPredicate<Staff> mp){
        List<Staff> staffs = new ArrayList<>();

        for (Staff staff: list) {
            if (mp.test(staff)){
                staffs.add(staff);
            }
        }
        return staffs;
    }

    public static void main(String[] args) {
        ArrayList<Staff> staffs = new ArrayList<>();
        staffs.add(new Staff(24, "xiaohua"));
        staffs.add(new Staff(36, "xiaoming"));
        staffs.add(new Staff(21, "xiaoguang"));
        staffs.add(new Staff(37, "xiaoliuhua"));
        // 使用匿名内部类
        List<Staff> list = new Demo().filterStaff(staffs, new MyPredicate<Staff>(){
            @Override
            public boolean test(Staff staff) {
                return staff.getAge() >= 35;
            }
        });
        for (Staff staff: list) {
            System.out.println(staff);
        }
    }
}

实现方式四:Lambda 表达式

Lambda 表达式免去了上述 Java8 之前要么重新写方法,要么重新实现接口的繁琐。
Lambda 表达式是对匿名内部类的使用优化。

        // Lambda表达式
        List<Staff> list = new Demo().filterStaff(staffs, (e)->e.getAge()>=35);  // 代替接口实现
        // Java8的新特性:使用“::”方法引用
        list.forEach(System.out::println);

实现方式五:Lambda 表达式 + Stream API

前面的方法都建立在了同一个 filterStaff 方法上面,如果现在只有员工数据,没有 filterStaff 函数,那么 Java8 可不可以实现找出年龄大于 35 的员工呢?答案是可以的,那就要用到 Java8 新特性中的 Stream API 。

    staffs.stream()
        .filter((e)->e.getAge()>=35)
        .forEach(System.out::println);

Lambda 表达式语法

Lambda 表达式的标准格式:

格式:(形式参数) -> {代码块}

  • 形式参数:如果有多个参数,参数之间用逗号隔开;如果没有参数,留空即可。
  • ->:由英文中画线和大于符号组成,固定写法,代表指向动作。
  • 代码块:是我们具体要做的事情。

Lambda 表达式的使用前提:

  • 有一个接口。
  • 接口中有且仅有一个抽象方法。

Lambda 语法格式:

//语法格式一:无参数,无返回值。
() -> System.out.println("Hello");

// 语法格式二:有一个参数,并且无返回值。
(e) -> System.out.println(e);

// 语法格式三:若只有一个参数,参数的小括号可以省略。
e -> System.out.println(e);

// 语法格式四:有两个以上的参数,又返回值,有多条执行语句。
(x, y) -> {
    System.out.println("Lambda");
    return Integer.compare(x, y);
};

// 语法格式五:如果有两个以上的参数,又返回值,只有一条执行语句,Lambda 体的大括号和return可以省略。
Comparator<Integer>con=(x, y) -> Integer.compare(x, y);

// 语法格式六:Lambda表达式的参数列表的数据类型可以省略不写,因为JVM编译器可以通过上下文推断出数据类型,即“类型推断”。
(Integer x, Integery) -> Integer.compare(x, y);

示例 1:无参无返回值的抽象方法

  • 定义一个接口 (Eatable),里面定义一个抽象方法:void eat();
  • 定义一个测试类 (EatableDemo),在测试类中提供两个方法
    • 一个方法是:useEatable(Eatable e)
    • 一个方法是主方法,在主方法中调用 useEatable 方法
// 接口
public interface Eatable {
    void eat();
}

// 实现类
public class EatableImpl implements Eatable {
    @Override
    public void eat() {
        System.out.println("一天一苹果,医生远离我");
    }
}
//测试类
public class EatableDemo {
    // 在主方法中调用 useEatable 方法
    public static void main(String[] args) {

        // 实现类的方式
        Eatable e = new EatableImpl();
        useEatable(e);

        // 匿名内部类的方式
        useEatable(new Eatable() {
            @Override
            public void eat() {
                System.out.println("一天一苹果,医生远离我");
            }
        });

        // Lambda 表达式的方式
        useEatable(() -> {
            System.out.println("一天一苹果,医生远离我");
        });
    }

    private static void useEatable(Eatable e) {
        e.eat();
    }
}

示例 2:有参无返回值的抽象方法

interface Flyable {
    void fly(String s);
}

public class Test {

    public static void main(String[] args) {

        // 匿名内部类
        useFlyable(new Flyable() {
            @Override
            public void fly(String s) {
                System.out.println(s);
                System.out.println("飞机自驾游");
            }
        });
        System.out.println("--------");

        // Lambda
        useFlyable((String s) -> {
            System.out.println(s);
            System.out.println("飞机自驾游");
        });

    }

    private static void useFlyable(Flyable f) {
        f.fly("风和日丽,晴空万里");
    }
}

示例 3:有参有返回值的抽象方法

public interface Addable {
    int add(int x, int y);
}

public class AddableDemo {

    public static void main(String[] args) {
        // lambda 实现 Addable
        useAddable((int x, int y) -> {
            return x + y;
        });
    }

    private static void useAddable(Addable a) {
        int sum = a.add(10, 20);
        System.out.println(sum);
    }
}

Lambda 表达式的省略模式

省略的规则:

  • 参数类型可以省略。
  • 如果参数有且仅有一个,那么小括号可以省略。
  • 如果代码块的语句只有一条,可以省略大括号和分号,和 return 关键字。
public interface Addable {
    int add(int x, int y);
}

public interface Flyable {
    void fly(String s);
}

public class LambdaDemo {

    public static void main(String[] args) {
//        useAddable((int x, int y) -> {
//            return x + y;
//        });
        // 参数的类型可以省略
        useAddable((x, y) -> {
            return x + y;
        });

//        useFlyable((String s) -> {
//            System.out.println(s);
//        });
//        // 如果参数有且仅有一个,那么小括号可以省略
//        useFlyable(s -> {
//            System.out.println(s);
//        });

        // 如果代码块的语句只有一条,可以省略大括号和分号
        useFlyable(s -> System.out.println(s));

        // 如果代码块的语句只有一条,可以省略大括号和分号,如果有return,return也要省略掉
        useAddable((x, y) -> x + y);
    }

    private static void useFlyable(Flyable f) {
        f.fly("风和日丽,晴空万里");
    }

    private static void useAddable(Addable a) {
        int sum = a.add(10, 20);
        System.out.println(sum);
    }
}

Lambda 表达式和匿名内部类的区别

所需类型不同:

  • 匿名内部类:可以是接口,也可以是抽象类,还可以是具体类。
  • Lambda 表达式:只能是接口。

使用限制不同:

  • 如果接口中有且仅有一个抽象方法,可以使用 Lambda 表达式,也可以使用匿名内部类。
  • 如果接口中多于一个抽象方法,只能使用匿名内部类,而不能使用 Lambda 表达式。

实现原理不同:

  • 匿名内部类:编译之后,产生一个单独的 .class 字节码文件。
  • Lambda 表达式:编译之后,没有一个单独的 .class 字节码文件,对应的字节码是在运行的时候动态生成。

函数式接口

什么是函数式接口?

  • Lambda 表达式需要“函数式接口”的支持。

  • 函数式接口:接口中只有一个抽象方法,称为函数式接口。可以使用注解 @FunctionalInterface 修饰(检测是否为函数式接口)。

Java8 四大内置核心函数式接口

Java8 给我们准备了现成的函数式接口,除了很特殊的场景需要自己写函数式接口外,大部分时候我们都可以使用内置的函数式接口,下面来看看四大内置接口及其使用方法:

Consumer<T>: 消费型接口
   void accep(T t);

Supplier<T>: 供给型接口
   T get();

Function<T, R>: 函数型接口
   R apply(T t);

Predicate<T>: 断言型接口
   boolean Test(T t);

使用示例:

// 取10个随机数
List<Integer> numlist = getNumList(10, ()->(int)Math.random());
    for (Integer num: numlist) {
        System.out.println(num);
    }
}

// 取随机数
public List<Integer> getNumList(int num, Supplier<Integer> sup){
    List<Integer> list = new ArrayList<>();
    for (int i = 0; i < num; i++) {
        Integer n=sup.get();
        list.add(n);
    }
    return list;
}


//Function<T, R>:函数型接口
//用于处理字符串
public String StrHandler(String str, Function<String, String> fun){
    return fun.apply(str);
}

// 测试函数型接口
@Test
public void test3(){

    String str=StrHandler("\t\t\t 就是对快睡觉",(x)->x.trim());
    System.out.println(str);

    String uper=StrHandler("\t\t\t 就是对快睡觉",(x)->x.toUpperCase());
    System.out.println(uper);

    String newStr=StrHandler("\t\t\t  就是对快睡觉",(x)->x.substring(2,5));
    System.out.println(newStr);

}



//Predicate<T>:断言型接口
//将满足条件的字符串放入集合中
public List<String> StrFilter(List<String> list, Predicate<String> pre){
    List<String> strList = new ArrayList<>();

    for (String str: list) {
        if (pre.test(str)){
            strList.add(str);
        }
    }
    return strList;
}

// 测试断言型接口
@Test
public void test4(){

    List<String> list = Arrays.asList("aaa", "bbb", "ccc", "bbbb", "jahdgja");
    List<String> strList = StrFilter(list, s->s.length()>3);
    
    for (String str: strList) {
        System.out.println(str);
    }

}

方法引用

方法引用:指若 Lambda 体中的内容已经有方法实现了,那么我们就可以使用“方法引用”。

主要有三种语法格式:

  1. 对象::实例方法名

  2. 类::静态方法名

  3. 类::实例方法名

注意:
Lambda 体中调用方法的参数列表和返回值类型,要与函数式接口中抽象方法的参数列表和返回值一致。
如若 Lambda 参数列表中的第一个参数是实例方法的调用者,而第二个参数是实例方法的参数时,可以使用 ClassName::method

构造器引用

  • 格式:ClassName::new

  • 注意:需要调用的构造器的参数列表,要与函数式接口中抽象方法的参数列表保持一致。

数组引用:

  • 格式:Type::new

标签:System,接口,println,out,public,表达式,Lambda
From: https://www.cnblogs.com/juno3550/p/17283441.html

相关文章

  • 24-springboot-thymeleaf的表达式
    1.添加热部署,为了测试不用频繁重启<!--热部署插件--><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-devtools</artifactId><optional>true</optional><!--防止将该依赖传递到其他模块中--></depen......
  • C语言逆向分析——IF条件语句的反汇编,要熟悉各种if的姿势以及与或非表达式组合
    第四课IF语句的反汇编判断  第四课练习1intplus(intY,intX){intN=t;if(X<Y)t=t+Y;//t=N+yC}voidmain(){plus(5,4);}第五课IF...ELSE...语句的反汇编判断IF…ELSE…语句的反汇编判断:  ......
  • python系列教程208——为什么使用lambda
    声明:在人工智能技术教学期间,不少学生向我提一些python相关的问题,所以为了让同学们掌握更多扩展知识更好地理解AI技术,我让助理负责分享这套python系列教程,希望能帮到大家!由于这套python教程不是由我所写,所以不如我的AI技术教学风趣幽默,学起来比较枯燥;但它的知识点还是讲到位的了,也值......
  • mybatis OGNL表达式,xml查询中使用java的变量值
    1、参考struts2ongl访问静态变量与访问枚举Ognl表达式基本原理和使用方法2、java类packageorg.jeecg.mgt.cms.constant;publicclassEAd{publicstaticfinalIntegershow1=1;publicstaticfinalclassAA{publicstaticfinalIntegershow2......
  • Lamda表达式
    函数式接口任何接口,如果只包含唯一一个抽象方法,那么他就是一个函数式接口publicinterfaceRunnable{ publicabstractvoidrun();}对于函数式接口,我们可以通过lambda表达式来创建该接口的对象lambda表达式练习一://推导Lambda表达式publicclassTestLambda1{......
  • day11| 20.有效的括号;150.逆波兰表达式求值;1047.删除字符串中的所有相邻重复项
    20.有效的括号 题目简述:给定一个只包括'(',')','{','}','[',']' 的字符串s,判断字符串是否有效。有效字符串需满足:左括号必须用相同类型的右括号闭合。左括号必须以正确的顺序闭合。每个右括号都有一个对应的相同类型的左括号。 思路:1.利用一个栈实现2.构建一个字典,键......
  • 中缀逻辑表达式转后缀逻辑表达式
    #include<bits/stdc++.h>usingnamespacestd;/*中缀逻辑表达式转后缀逻辑表达式测试用例:0&(0|1|0)答案:001|0|&*/unordered_map<char,int>h{{'|',1},{'&',2}};strings;stringt;stack<char>stk;intmain(){cin......
  • 中缀表达式转后缀表达式
    中缀表达式转后缀表达式一、中缀表达式和后缀表达式的区别中缀表达式就是我们通常认知中的表达式,比如\[1+((2+3)*4)-5\]这样的表达式虽然容易被人所理解,但是不容易被机器所识别,为此推出了后缀表达式。后缀表达式又被叫做逆波兰表达式,逆波兰表达式不需要被括号所识别,且容易被机......
  • Hangfire 定时任务设置某个时间区间每隔一定时间触发的Cron表达式
    Cron表达式Hangfire使用类似于Linux下的Cron表达式定义时间规则,Cron表达式由6或7个由空格分隔的时间字段组成。Cron表达式时间字段(从左到右依次为):位置时间域名允许值允许的特殊字符1秒0-59,-*/2分钟0-59,-*/3小时0-23,-*/4日期1-31......
  • 常用API(Math,System,Runtime,Object,Objects,BigInteger,BigDecimal,正则表达式)
    常用API(Math,System,Runtime,Object,Objects,BigInteger,BigDecimal,正则表达式)多使用帮助文档类型取值范围和默认值Math类:​ 概念:帮助我们进行数学计算的工具类,里面的方法都是静态的,所以无需实例化这个类​ 常用方法:​ abs::获取绝对值 absExact:获取绝对值 rou......