Java 8 新特性(一)lambda表达式

简介: Java 9 好像也快出了,不过我连Java 8的新特性都还没认真研究过,所以这几篇文章就是来介绍Java 8的新特性的。首先,第一个重要的特性就是传说中的lambda表达式了,虽然初学可能觉得这东西很难理解,但是一旦学会了,你就会发现离不开它了。

Java 9 好像也快出了,不过我连Java 8的新特性都还没认真研究过,所以这几篇文章就是来介绍Java 8的新特性的。首先,第一个重要的特性就是传说中的lambda表达式了,虽然初学可能觉得这东西很难理解,但是一旦学会了,你就会发现离不开它了。

在现代编程语言中,lambda表达式这个语言特性已经成为了标配。当然不同语言中的lambda表达式概念和实现形式可能会稍有差别。不过不管在哪种语言中,我们都可以把lambda表达式简单的理解为匿名函数。在Java中,lambda表达式和只有一个方法的接口是差不多的,所以这样的接口又叫做SAM接口(Single Abstract Method interfaces)。

初识lambda表达式

说了半天,下面先来看看实际例子吧。我们在编写多线程代码的时候,常常需要编写实现Runnable的类。有时候为了省事,我们会写成匿名内部类的形式。

Runnable task1 = new Runnable() {
    @Override
    public void run() {
        System.out.println("任务一");
    }
};

匿名内部类不好理解,而且写起来也非常丑陋。由于Runnable接口内只有一个方法,所以它是一个SAM接口,我们可以用lambda表达式改写它。那么改写之后是什么样子的呢?不要惊讶,只需要一行代码。

Runnable task2 = () -> System.out.println("任务二");

因此,我们可以看到lambda表达式的简洁之处。下面就来详细介绍一下lambda表达式的形式。

lambda表达式形式

lambda表达式可以用在需要SAM接口对象的地方,由于编译器这时候知道所需要的接口和对应方法的签名。所以lambda表达式的类型声明可以省略,具体类型由编译器自行推断。lambda表达式的基本形式如下。

(参数列表) -> {方法体}

为了方便说明,我这里定义了一组接口,用于之后的lambda表达式实现。

@FunctionalInterface
interface Interface1 {
    void f();
}


@FunctionalInterface
interface Interface2 {
    void f(int a);
}

@FunctionalInterface
interface Interface3 {
    void f(int a, int b);
}

@FunctionalInterface
interface Interface4 {
    int f(int a, int b);
}

无参lambda

先来看看无参数的lambda表达式。特别地,如果方法体只有一行代码,可以省略方法体的大括号。

Interface1 interface1 = new Interface1() {
    @Override
    public void f() {
        System.out.println("f");
    }
};
Interface1 lambda1 = () -> {
    System.out.println("f");
};
//如果方法体只有一行代码,可以省略大括号
Interface1 lambda1a = () -> System.out.println("f");

多个参数的lambda

我们可以看到由于编译器可以进行类型推断,所以lambda表达式的参数列表不需要参数类型声明,而且由于我只有一行代码,所以大括号也省略了。

//多个参数lambda
Interface3 interface3 = new Interface3() {
    @Override
    public void f(int a, int b) {
        System.out.println(a + b);
    }
};
Interface3 lambda3 = (a, b) -> System.out.println(a + b);

带返回值的lambda

带返回值的lambda表达式其实和普通的一样,只不过需要在方法体的最后使用return语句。值得注意的是最后一个例子,如果方法体本身足够简单,可以直接将返回值表达式写在lambda表达式的右边,编译器也会正确对待这种情况。

//带有返回值的lambda
Interface4 interface4 = new Interface4() {
    @Override
    public int f(int a, int b) {
        return a + b;
    }
};
Interface4 lambda4 = (a, b) -> {
    return a + b;
};
Interface4 lambda4a = (a, b) -> a + b;

单个参数的lambda

如果lambda表达式只有一个参数,那么参数列表的小括号也可以省略,例如下面的第二个例子。下面第三个例子演示了另一个新语法方法引用,如果lambda表达式的形式是单个参数a -> 某个只有一个参数的方法(a),那么就可以简写成方法引用的形式类::方法。方法引用是一个新语法,如果见到了不要奇怪,这也是正确的Java代码。

//一个参数的lambda
Interface2 interface2 = new Interface2() {
    @Override
    public void f(int a) {
        System.out.println(a);
    }
};
Interface2 lambda2 = (a) -> System.out.println(a);
Interface2 lambda2a = a -> System.out.println(a);
Interface2 methodReference = System.out::println;

函数接口

@FunctionalInterface注解

如果查看JDK源码的一些接口,会发现它们上面添加了@FunctionalInterface注解。例如上面提到的Runnable接口。

@FunctionalInterface
public interface Runnable {

    public abstract void run();
}

这个注解用于标注函数接口,也就是上面提到的SAM接口。如果一个接口中包含了不止一个方法,那么函数接口注解就会触发编译错误。这个功能类似于@Override注解。当然并不是说要使用lambda表达式必须标记这个注解,只要接口中只有一个方法,那么就可以使用lambda表达式。

其实函数接口可以包括不止一个方法,除了一个普通的抽象方法之外,函数接口还可以包含一个在java.lang.Object类中声明的方法。Java 8新增了默认方法特性,如果接口的实现类没有实现java.lang.Object类中的这些基本方法,那么就会调用java.lang.Object类中的实现作为默认实现。

@FunctionalInterface
interface Interface5 {
    void f();

    String toString();
}

预定义的函数接口

java.util.function包下定义了大量预定义的函数接口,它们包含了各种各样的形式,基本涵盖了编程的各个方面。同学们最好对这些函数接口的形式稍作了解,以后碰到的时候就不会一头雾水了。

img_83a6f3297c7e0883b0a37f3f13a5e3bf.png
函数接口

lambda表达式应用

编写线程

前面提到了,可以使用lambda表达式简化线程代码的编写,不再需要冗长的匿名内部类。

//编写线程
Runnable task = () -> System.out.println("简单的任务");
Executor executor = Executors.newCachedThreadPool();
executor.execute(task);

编写比较器

比方说我们有一个学生类,有姓名和年龄两个字段。如果需要按照某种规则来进行排序,可以使用比较器Comparator接口来比较,利用lambda表达式同样可以简化这部分代码的编写。

class Student {
    private String name;
    private int age;
    //其余方法已省略
}

可以看到比较规则只需要一条lambda表达式即可传入,非常方便。后面两个例子是Comparator接口新增的工具方法,可以帮助我们简化比较代码的编写,这里使用了方法引用来简化代码。comparing等方法的返回值仍然是Comparator,所以可以链式调用进行多级比较。

List<Student> students = new ArrayList<>();
students.add(new Student("yitian", 25));
students.add(new Student("anna", 26));
students.add(new Student("wang5", 24));

students.sort((a, b) -> a.getAge() - b.getAge());
System.out.println(students);

students.sort(Comparator.comparing(Student::getName));
System.out.println(students);

students.sort(
        Comparator.comparing(Student::getName)
                .thenComparing(Student::getAge));
System.out.println(students);
相关文章
|
4天前
|
Java API 开发者
Java中的Lambda表达式与函数式编程####
在Java的演变过程中,Lambda表达式和函数式编程的引入无疑是一次重大的飞跃。本文将深入探讨Lambda表达式的定义、用法及优势,并结合实例说明如何在Java中利用Lambda表达式进行函数式编程。通过对比传统编程方式,揭示Lambda表达式如何简化代码、提高开发效率和可维护性。 ####
|
12天前
|
自然语言处理 安全 Java
Aviator Java 表达式引擎
AviatorScript 是一门高性能、轻量级寄宿于 JVM 之上的脚本语言。
29 10
|
10天前
|
Java API 数据处理
探索Java中的Lambda表达式与Stream API
【10月更文挑战第22天】 在Java编程中,Lambda表达式和Stream API是两个强大的功能,它们极大地简化了代码的编写和提高了开发效率。本文将深入探讨这两个概念的基本用法、优势以及在实际项目中的应用案例,帮助读者更好地理解和运用这些现代Java特性。
|
14天前
|
存储 Java API
优雅地使用Java Map,通过掌握其高级特性和技巧,让代码更简洁。
【10月更文挑战第19天】本文介绍了如何优雅地使用Java Map,通过掌握其高级特性和技巧,让代码更简洁。内容包括Map的初始化、使用Stream API处理Map、利用merge方法、使用ComputeIfAbsent和ComputeIfPresent,以及Map的默认方法。这些技巧不仅提高了代码的可读性和维护性,还提升了开发效率。
36 3
|
14天前
|
存储 安全 Java
Java Map新玩法:深入探讨HashMap和TreeMap的高级特性
【10月更文挑战第19天】Java Map新玩法:深入探讨HashMap和TreeMap的高级特性,包括初始容量与加载因子的优化、高效的遍历方法、线程安全性处理以及TreeMap的自然排序、自定义排序、范围查询等功能,助你提升代码性能与灵活性。
22 2
|
13天前
|
Java API
[Java]Lambda表达式
本文主要介绍了Java中的Lambda表达式,包括其优化匿名内部类的方式、使用规范、内置函数式接口及方法引用等内容。文章详细解析了Lambda的基础语法、参数列表、方法体的简化规则,以及如何利用Lambda优化代码。此外,还探讨了Lambda的作用域和引用规则,强调了对局部变量、成员变量和常量的访问限制,旨在帮助读者全面理解和掌握Lambda表达式的应用。
11 0
[Java]Lambda表达式
|
16天前
|
Java 测试技术 开发者
🌟Java零基础-Lambda运算符详解 🌟
【10月更文挑战第12天】本文收录于「滚雪球学Java」专栏,专业攻坚指数级提升,希望能够助你一臂之力,帮你早日登顶实现财富自由🚀;同时,欢迎大家关注&&收藏&&订阅!持续更新中,up!up!up!!
19 1
|
搜索推荐 Java 程序员
Java 12都有哪些新特性?
Java 12都有哪些新特性?
157 0
Java 12都有哪些新特性?
|
Java API Apache
Java 9都有哪些新特性?
Java 9都有哪些新特性?
122 0
|
JavaScript 前端开发 Java
Java 10都有哪些新特性?
Java 10都有哪些新特性?
146 0