Lambda——让你的代码更优雅

简介

Lambda 表达式是推动 Java 8 发布的最重要新特性,也是Java进入函数式编程的第一步。学会使用Lambda表达式可以让你的代码变得精简而优雅。
Lambda 其实也可以称为一种匿名函数,但它可以帮我们简化匿名内部类的使用。

  • 什么是匿名内部类?

    匿名内部类,就是没有名字的一种嵌套类。

  • 为什么使用?

    一个接口/类的方法的某个实现方式在程序中只会执行一次,但为了使用它,我们需要创建它的实现类/子类去实现/重写。此时可以使用匿名内部类的方式,可以无需创建新的类,减少代码冗余。

  • 案例

    public interface InterfaceDome {
        void show();
    }
    public static void main(String[] args) {
        InterfaceDome interfaceDome = new InterfaceDome() {
            @Override
            public void show() {
                System.out.println("这里使用了匿名内部类");
            }
        };
        //调用接口方法
        interface01.show();
    }
    

语法

(parameters) -> expression

(parameters) ->{ statements; }

以下是lambda表达式的重要特征:

  • 可选类型声明:参数类型声明可省略,编译器可以统一识别参数值(即类型推断)。

    例如:

    // 接收2个int型整数,输出他们的和
     (int x, int y) -> {
                System.out.println(x + y);
            };
    // 省略写法
     (x, y) -> {
                System.out.println(x + y);
            };
    
  • 可选的参数圆括号:一个参数可省略圆括号,但多个参数需要定义圆括号。

    例如

    // 接收一个数值x,返回2*x值
    (x) -> {
                return x * 2;
            };
    // 省略写法
     x -> {
                return x * 2;
            };
    
  • 可选的大括号:方法主体只有一个语句,则可省略大括号。

    例如

     (x, y) -> {
                return x + y;
            };
     // 省略写法
     (x, y) -> x + y;
    
  • 可选的返回关键字:如果主体只有一个表达式返回值则编译器会自动返回值,大括号需要指定表达式返回了一个数值。

    例如

    (x, y) -> {
                x++;
                y++;
                return x + y;
            } 
    

实践

lambda 引入了两个核心思想

  • 行为参数化 (方法函数可作为参数传递进其他方法中)
  • 方法引用

行为参数化

注意:Java中的lambda它需要一个函数式接口(只有一个方法的接口)来实现,lambda表达式方法体其实就是函数接口方法的实现。

@FunctionalInterface
public interface TestInterface {
    int test(int x, int y);
}

// 使用案例: x+y 即为test方法的实现代码块
TestInterface testInterface = (x, y) -> x + y;

@FunctionalInterface作为注解用来声明该接口为函数式接口。这个注解是非必须的,只要接口符合函数式接口的标准,虚拟机会自动判断。但使用该注解可以避免其他合作开发人员往接口中添加新的方法。

四大核心函数式接口:

函数式接口参数类型返回值类型描述
Consumer(消费型)T处理一个接受T类型的值
Function(功能性)TR处理T类型的值,并返回R类型的值
Supplier(供给型)T接受一个T类型的值
Predicate(断言型)Tboolean处理T类型的值,返回true或者false
示例

按照年龄将User排序

public class test {
    public static void main(String[] args) {
        List<User> list = Arrays.asList(
                new User(1, "张三", 9500),
                new User(2, "李四", 10000),
                new User(3, "王五", 8000)
        );
        System.out.println("排序前:");
        System.out.println(list);
        list.sort((a, b) -> Integer.compare(a.getAge(), b.getAge()));
        System.out.println("排序后:");
        System.out.println(list);
    }
}

class User {
    private int id;
    private String name;
    private int age;

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

    public int getId() {
        return id;
    }

    public String getName() {
        return name;
    }

    public int getAge() {
        return age;
    }

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

方法引用

方法引用也是函数式接口的一个实例,通过方法的名字来指向一个方法,也是Lambda表达式的一种语法。

什么时候使用?

  1. 创建了函数式接口的匿名内部类对象
  2. 重写了函数式接口的抽象方法并在重写的方法中调用被引用的方法

简而言之:就是用lambda创建了函数式接口的实现类对象,是其他方法的方法体

静态方法引用
  • 格式
    • 类名::静态方法
  • 范例
    • Integer::compare
    • Integer类的方法:public static int compare(int x, int y) 比较x,y的大小
public static void main(String[] args) {
    // 匿名内部类创建,<Integer>这个泛型一定要带,不然下面就只能是object
    Comparator comparator = new Comparator<Integer>() {
        @Override
        public int compare(Integer a, Integer b) {
            return Integer.compare(a, b);
        }
    };

    // lambda方式创建,<Integer>这个泛型一定要带,不然报错
    Comparator<Integer> comparator1 = (a, b) -> Integer.compare(a, b);

    // 方法引用,<Integer>这个泛型一定要带,不然报错
    Comparator<Integer> comparator2 = Integer::compare;
}

Lambda表达式被类方法替代的时候,它的形式参数全部传递给静态方法作为参数

对象方法引用
  • 格式
    • 对象名::非静态方法名
  • 范例
    • String::equals
    • String类的方法:public boolean equals(Object anObject) 比较参入参数的anObject 与调用对象是否相同
public static void main(String[] args) {
    // 这里的<String, String>类型不能丢,因为下面用的是String,如果不声明类型就是Object
    BiPredicate biPredicate = new BiPredicate<String, String>() {
        @Override
        public boolean test(String o, String o2) {
            return o.equals(o2);
        }
    };

    // 这里变量没有声明类型,那么就是Object,equals也是调用的Object的
    BiPredicate biPredicate1 = (a, b) -> a.equals(b);

    // 这里类型必须要写,因为后面写明了调用String的equals
    BiPredicate<String, String> biPredicate2 = String::equals;
}
构造方法引用
  • 格式
    • 类名::new
  • 范例
    • User::new
    • User类构造方法:public User(String name),通过name参数构造User类
public interface Converter {
    User creat(String name);
}

class User {
    private int id;
    private String name;
    private int age;

    public User(String name) {
        this.name = name;
    }
}
public static void main(String[] args) {
        // 匿名内部类
        Function function = new Function<String, User>() {
            @Override
            public User apply(String name) {
                return new User(name);
            }
        };

        // lambda
        Function<String, User> function1 = name -> new User(name);

        // 构造引用
        Function<String, User> function2 = User::new;
    }

扩展

变量作用域

Lambda 表达式,也可称为闭包(能够读取其他函数内部变量的函数),但在访问局部变量要注意如下 3 点:

  1. 可以直接在 Lambda 表达式中访问外层的局部变量;
  2. 在 Lambda 表达式当中被引用的变量的值不可以被更改;
  3. 在 Lambda 表达式当中不允许声明一个与局部变量同名的参数或者局部变量。
    在这里插入图片描述
    关于第二点,不可以被更改:lambda 表达式中不能更改、也不能被后面的代码修改值,否则会编译错误。若想在lambda 表达中修改引用标记需要在外层局部变量加上 final 修饰词。

为什么有此限制?

  • 实例变量和局部变量的实现不同:实例变量都存储在堆中,而局部变量则保存在栈上。如果在线程中要直接访问一个非final局部变量,可能线程执行时这个局部变量已经被销毁了。因此,Java 在访问自由局部变量时,实际上是在访问它的副本,而不是访问原始变量。如果局部变量仅仅赋值一次那就没什么区别(即隐性的具有 final的语义)
  • 这个局部变量的访问限制也是 Java 为了促使你从命令式编程模式转换到函数式编程模式,这样会很容易使用 Java 做到并行处理。

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

  • 所需类型不同

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

  • 使用限制不同

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

  • 实现原理不同

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

关注微信公众号菜鸟乐编程,更多精彩等你发现…关注微信公众号菜鸟乐编程,更多精彩等你发现

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值