首页 > 其他分享 >JDK8新特性之Lambda表达式

JDK8新特性之Lambda表达式

时间:2024-06-12 16:31:40浏览次数:22  
标签:System JDK8 friend println out public 表达式 Lambda

Lambda,音标[ˈlamdə],中文翻译“拉姆达”,第11个希腊字母λ(大写Λ)。

1. 引入原因

JDK8引入Lambda表达式是为了简化匿名类相关代码。当接口比较简单,只有一个方法时,我们也不得不写许多无关业务的代码来实现匿名类。而Lambda表达式却允许将功能(functionality)视作方法参数或者视代码为数据,以省去非业务代码。

通过下边两段代码可以看出,Lambda表达式写法确实简洁很多。

//匿名类写法
new Thread(new Runnable() {
        @Override
         public void run() {
                System.out.println(Thread.currentThread().getName());
         }
}).start();
//Lambda表达式写法
new Thread(() -> System.out.println(Thread.currentThread().getName())).start();

2. 语法

Lambda表达式由3部分组成。

① 参数:小括号内以逗号分隔的形参列表。如果只有一个形参,可省去小括号。以下几种写法都是正确的:

//多个形参
(P1,P2,P3 ... Pn)
//一个形参,可以省去小括号
(P1)  或 P1
//没有形参,需要保留小括号
()

② 箭头 ->

③ 主体:表达式或大括号括内语句块

表达式可以理解为单行,不需要显式返回结果的语句;语句块则是多行或者需要显示返回结果的语句。语句块必须在大括号内。例如:

//表达式
System.out.println("I am an expression")
或者
x == y

//语句块
{
     boolean r = x == y;
     System.out.println(r ? "x等于y" : "x不等于y");
     return r;
}

3. 使用示例

假设我们在开发一个名为KK的即时聊天工具,里边有个很重要的功能就是根据条件查找好友。好友有姓名、年龄、性别和手机号码等属性:

import lombok.Data;
/**
 * 好友类
 */
@Data
public class Friend {

    public enum Sex {
        MALE, FEMALE, OTHER
    }

    private String name;
    private int age;
    private Sex gender;
    private String mobile;
}

好友人数上限是500个,我们可以暂时把所有好友都放在一个List里。

最开始,我们只想根据性别搜索,所以写了一个很简单的方法:

public static void search(List<Friend> friends, Sex gender) {
    for (Friend f : friends) {
        if (f.gender == gender) {
            System.out.println(f);
        }
     }
}

后来我们又想加上年龄作为搜索条件,于是修改成了这样:

public static void search(List<Friend> friends, Sex gender, int high, int low) {
    for (Friend f : friends) {
        if (f.gender == gender && f.age < high && f.age >= low) {
            System.out.println(f);
        }
    }
}

改了参数列表,会导致原本调用此方法的地方报错,无法编译。为了避免出现这种情况,我们决定把判断方法独立到一个类里,并修改查询方法,如下:

/**
 * 判断方法类
 */
public interface CheckFriend {
    /**
     * 入参都是Friend, 不论是Friend属性改变或者判断条件变化,都不会影响调用方
     * @param friend
     * @return true or false
     */
    boolean check(Friend friend);
}

//相应的查询方法修改成这样
public static void search(List<Friend> friends, CheckFriend cf) {
    for (Friend f : friends) {
        if (cf.check(f)) {
            System.out.println(f);
        }
    }
}

//调用查询方法时,使用匿名类
public static void main(String[] args) {
    List<Friend> friends = new ArrayList<>(512);
    Friend.search(friends, new CheckFriend() {
        @Override
        public boolean check(Friend friend) {
             return friend.getAge() > 15 && friend.getAge() <= 20 && friend.getGender() == Friend.Sex.MALE && friend.getName().startsWith("张三");
        }
    });
}

CheckFriend接口只有一个方法,我们可以使用Lambda表达式来简化代码:

//根据年龄、性别和姓名查询
Friend.search(friends, friend -> friend.getAge() > 15 && friend.getAge() <= 20 && friend.getGender() == Friend.Sex.MALE && friend.getName().startsWith("张三"));

如果其他调用方查询条件不同,也可以很方便的修改:

//根据性别和手机号码查询
Friend.search(list, friend -> friend.getGender() == Friend.Sex.MALE && friend.getMobile().startsWith("188"));

到这里,我们已经对Lambda表达式有了基本认识。虽然已经精简了一部分代码,但是还有优化空间,比如可以使用函数式接口Predicate<T>来替代CheckFriend:

//使用Predicate做判断
public static void searchByPredicate(List<Friend> friends, Predicate<Person> tester) {
    for (Friend f : friends) {
        if (tester.test(f)) {
            System.out.println(f);
        }
    }
}
//调用查询方法
Friend.searchByPredicate(list, friend -> friend.getGender() == Friend.Sex.MALE && friend.getMobile().startsWith("188"));

4. 目标类型推断

方法所期望的Lambda表达式类型就是目标类型,Java编译器根据Lambda表达式所在位置的上下文或情景来推断表达式类型即为目标类型推断。因此,上边的示例中,同一个

Lambda表达式:

friend -> friend.getGender() == Friend.Sex.MALE && friend.getMobile().startsWith("188")

既可以做为CheckFriend类型用到search方法,也可以做为Predicate类型用到

searchByPredicate方法。这也要求我们只能在 Java 编译器可以确定目标类型的情况下使用 Lambda表达式:

  • 变量声明
  • 赋值语句
  • 返回语句
  • 数组初始化器,比如初始化一个CheckFriend数组
CheckFriend[] arr = new CheckFriend[]{f -> f.getAge() == 18, f -> f.getAge() > 18};
  • 方法或构造函数参数

对于方法参数,java编译器还需要根据重载解析和类型参数来推断目标类型。

  • Lambda表达式主体
Function<Integer, Predicate<String>> func = x -> s -> s.length() > x;
  • 条件表达式 ?:
Function<Integer, String> func = true ? (i -> "Positive") : (i -> "Negative");
  • 强制类型转换

5. 访问外部变量

与匿名类一样,Lambda表达式也可以访问其外部作用域的局部变量,且这些变量也必须是final或有效final的;不同的是,Lambda表达式不会引入新的作用域。

假设有个接口Tester:

public interface Tester {
    void test(int x);
}

先来看一个匿名类的示例:

public class OuterClass {

    //OuterClass作用域
    public int x = 0;

    void haha(int x) {
        //haha方法内作用域
        int y = x;
        int z = 1;
        new Tester() {
            //tt匿名类作用域
            int y = 2; //可以定义与方法内作用域相同名称的变量

            @Override
            public void test(int x) {
                //z = 2; //想要改变外部作用域变量的值,报错
                System.out.println("test x: " + x);
                System.out.println("OuterClass x: " + OuterClass.this.x);
                System.out.println("y: " + y);
                System.out.println("tester y: " + this.y);
                System.out.println("haha z: " + z);
            }
        }.test(y);
    }

    public static void main(String[] args) {
        OuterClass oc = new OuterClass();
        oc.haha(22);
    }
}

//结果
test x: 22
OuterClass x: 0
y: 2
tester y: 2
haha z: 1

再来看Lambda表达示的:

public class OuterClass {
    //OuterClass作用域
    public int x = 0;

    void haha(int x) {
        //haha方法内作用域
        int y = x;
        int z = 1;
        //Tester tt = x -> { //与外部作用域变量x重名,报错
        //  int y = 1; //与外部作用域变量y重名,报错
        Tester tt = w -> {
            //z = 2; //想要改变外部作用域变量的值,报错
            System.out.println("w: " + w);
            System.out.println("x: " + x);
            System.out.println("z: " + z);
            System.out.println("OuterClass x: " + this.x);
        };
        tt.test(y);
    }

    public static void main(String[] args) {
        OuterClass oc = new OuterClass();
        oc.haha(22);
    }
}

//结果
w: 22
x: 22
z: 1
OuterClass x: 0

以上就是关于Lambda表达式的一些基础知识,后期我会在此基础上介绍JDK8中其他的特性,比如函数式接口等。

参考内容

[1] Lambda Expressions (The Java" Tutorials > Learning the Java Language > Classes and Objects)

标签:System,JDK8,friend,println,out,public,表达式,Lambda
From: https://blog.csdn.net/zhilan_note/article/details/139411547

相关文章

  • c# 正则表达式验证"身份证","手机号","邮箱地址","邮编"
    publicstaticclassVerify{///<summary>///验证手机号码///</summary>///<paramname="str_handset"></param>///<returns></returns>publicstaticboolIsHandset(stringstr_handset)......
  • 3.2.5 表达式求值
    表达式求值数据结构严蔚敏版3.2.5表达式求值,c++代码实现。注意:输入表达式不能有空格#include<stack>#include<string>#include<vector>#include<unordered_map>#include<algorithm>#include<iostream>#include<iomanip>usingnamespacestd;......
  • 华为OD刷题C卷 - 每日刷题 23(提取字符串中的最长表达式,模拟目录管理功能 - 完整实现)
    1、提取字符串中的最长表达式目标是从一个给定的字符串中提取出最长的合法简单数学表达式,并计算该表达式的值。如果存在多个同样长度的合法表达式,则选择第一个出现的表达式进行计算。简单数学表达式的规则:只包含0-9的数字和+、-、*三种运算符。所有数字的计算结果不超过......
  • 【Java之JDK8新特性】
    文章目录一、Java8中的Lambda表达式如何改善Java代码?二、在Java8中,接口有哪些新特性?三、如何使用Java8的StreamAPI进行集合处理?四、Java8的Optional类有什么用途?五、Java8中Date-TimeAPI的改进有哪些?六、Java8中的方法引用是什么,它有什么好处?七、Java8中的Compl......
  • 表达式求值--后缀 C++实现
    #include<iostream>#include<string>#include<stack>usingnamespacestd;intPostFixRun(stringch){stack<int>stk;for(inti=0;i<ch.size();i++){if(ch[i]>='0'&&ch[i]<='9')......
  • JAVA lambda表达式方法引用+构造器引用
    若Lambda体中的内容有方法已经实现了,使用“方法引用”注意:Lambda体中调用方法的参数列表与返回值类型,要与函数式接口中抽象方法的函数列表和返回值类型保持一致。静态方法与实例方法的区别1、静态方法属于整个类所有,因此调用它不需要实例化,可以直接调用(类.静态方法())。实例......
  • 表达式求值的相关语法知识(C语言)
    目录整型提升整型提升的意义整型提升规则整型提升实例算术转换赋值转换操作符的属性C语言的语法并不能保证表达式的执行路径唯一!!!问题表达式整型提升        C的整型算术运算总是至少以缺省整型类型的精度来进行的。为了获得这个精度,表达式中的字符和短整......
  • Linux -- 正则表达式基础
    提示:制作不易,可以点个关注和收藏哦。前言        虽然我们这一节的标题是正则表达式,但实际这一节实验只是介绍grep,sed,awk这三个命令,而正则表达式作为这三个命令的一种使用方式(命令输出中可以包含正则表达式)。正则表达式本身的内容很多,要把它说明清楚需要单独一门......
  • 【C++练级之路】【Lv.23】C++11——可变参数模板、lambda表达式和函数包装器
    快乐的流畅:个人主页个人专栏:《算法神殿》《数据结构世界》《进击的C++》远方有一堆篝火,在为久候之人燃烧!文章目录一、可变参数模板1.1参数包的概念1.2参数包的展开1.3emplace系列二、lambda表达式2.1lambda的格式2.2捕捉列表2.3lambda的原理2.4......
  • Mat的lambda方式像素高效遍历(C++11)
    Mat的lambda方式像素高效遍历(C++11)文章目录Mat的lambda方式像素高效遍历(C++11)前言一、Mat的lambda方式像素高效遍历二、代码实现总结前言图像遍历是图像处理中的经典操作,快速高效的进行像素遍历对性能的提升至关重要。一、Mat的lambda方式像素高效遍历OpenCV4......