lambda表达式

本文介绍了JavaSE8中的Lambda表达式,包括其基本语法、函数式接口的概念,以及变量捕获在匿名内部类和Lambda表达式中的应用。重点展示了如何使用lambda表达式简化代码并处理变量捕获规则。
摘要由CSDN通过智能技术生成

1. lambda表达式基础

Lambda表达式是Java SE 8中一个重要的新特性.lambda表达式允许你通过表达式来代替功能接口.lambda表达式就和方法一样,它提供了一个正常的参数列表和一个使用这些参数的主体(body,可以是一个表达式或一个代码块). Lambda 表达式(Lambda expression).基于数学中的λ演算得名.也可称为闭包(Closure).

1.1 基本语法

基本语法: (parameters) -> expression 或 (parameters) ->{ statements; }

Lambda表达式由三部分组成:
1. paramaters:类似方法中的形参列表,这里的参数是函数式接口里的参数.这里的参数类型可以明确的声明.也可不声明而由JVM隐含的推断.另外当只有一个推断类型时可以省略掉圆括号.注意:参数类型要么都省略,要么都不省略.

//接受2个参数(数字),并返回他们的和
(x, y) -> x + y//不报错,x,y的类型会被推导,可能还会产生隐式类型转换:小范围->大范围
(int x, int y) -> x + y//当传入两个int型变量时,不报错
(int x, y) -> x + y//编译报错


2. ->:可理解为“被用于”的意思.
3. 方法体:可以是表达式也可以代码块,是函数式接口里方法的实现.代码块可返回一个值或者什么都不返回,这里的代码块等同于方法的方法体.如果是表达式,也可以返回一个值或者什么都不返回.

// 1. 不需要参数,返回值为 2
() -> 2
// 2. 接收一个参数(数字类型),返回其2倍的值
x -> 2 * x
// 3. 接受2个参数(数字),并返回他们的和
(x, y) -> x + y
// 4. 接收2个int型整数,返回他们的乘积
(int x, int y) -> x * y
// 5. 接受一个 string 对象,并在控制台打印,不返回任何值(看起来像是返回void)
(String s) -> System.out.print(s)

1.2 函数式接口

谈到lambda表达式,我们不得不提函数式接口.

函数式接口定义:一个接口有且仅有一个抽象方法.

根据定义,我们可以知道,函数式接口只对接口中抽象方法的个数有要求,对是否有非抽象方法及常量没有要求.只要接口中有且仅有一个抽象方法,就是函数式接口!

我们可以加上@FunctionalInterface注解,让编译器帮我们检查这个接口是否满足了函数式接口的条件,不满足的时候会报错.

@FunctionalInterface
interface NoParameterNoReturn {
    //注意:只能有一个抽象方法
    void test();
}

@FunctionalInterface
interface NoParameterNoReturn {
    int a = 10;
    void test();
    default void test2() {
        System.out.println("JDK1.8新特性,default默认方法可以有具体的实现");
    }
}

 1.3 语法精简

1. 参数类型可以省略,如果需要省略,每个参数的类型都要省略.
2. 参数的小括号里面只有一个参数,那么小括号可以省略.
3. 如果方法体当中只有一句代码,那么大括号可以省略.
4. 如果方法体中只有一条语句,且是return语句,那么大括号可以省略,且去掉return关键字.

 

2. 变量捕获

2.1 匿名内部类

我们以下面这个例子来介绍一下,什么是匿名内部类.我们创建一个Test类,其中含有一个func方法,我们再创建一个TestDemo类,在其中创建main方法.然后,new了一个Test对象,但是我们并没有直接在()后面加上分号,而是先加上了一个大括号,在其中重写了func方法.这种语法看起来十分奇怪,但是是可以正常运行的.()后面的我们就称为匿名内部类.代码如下:

class Test {
    public void func(){
        System.out.println("func()");
    }
}

public class TestDemo {
    public static void main(String[] args) {
        new Test(){
            @Override
            public void func() {
                System.out.println("我是内部类,且重写了func这个方法!");
            }
        };
    }
}

//output:我是内部类,且重写了func这个方法!

根据上述的代码,我们可以简单分析一下,使用匿名内部类的时候,发生了些什么操作?

1. 创建了一个Test的子类(但是我们不知道它的名字,因此这个内部类也只能使用一次).

2. 这个子类重写了Test类中的func方法.

3.调用了被重写的func方法.

这种写法看似省略了许多语句,但是看着十分别扭,同时降低了代码的可读性.不推荐使用.

2.2 匿名内部类的变量捕获

简单来说,当我们在main方法中使用匿名内部类时,这个内部类会捕获main方法中的变量,如果我们要在被重写的方法中使用这些变量,要么这些变量是被final修饰的,要么确保这些变量在被使用前,其值没有被修改过,否则编译就会报错.下述代码就会编译报错:

public class TestDemo {
    public static void main(String[] args) {
        int a = 100;
        new Test(){
            @Override
            public void func() {
                a = 99;//修改了a的值
                System.out.println("我是内部类,且重写了func这个方法!");
                System.out.println("我是捕获到变量 a == "+a
                +" 我是一个常量,或者是一个没有改变过值的变量!");
            }
        };
    }
}

2.3 lambda表达式的变量捕获

在lambda表达式中,变量捕获与在匿名内部类中变量捕获的方式基本相同,下面给出代码:

@FunctionalInterface
interface NoParameterNoReturn {
    void test();
}

public static void main(String[] args) {
    int a = 10;
    NoParameterNoReturn noParameterNoReturn = ()->{
        // a = 99; error
        System.out.println("捕获变量:"+a);
    };
    noParameterNoReturn.test();
}

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值