登录 |  注册
首页 >  编程语言 >  java8特性 >  Java8新特性介绍

Java8新特性介绍

Java8新特性中最为重要的便是Lambda表达式和Stream API了,先来了解一下Lambda表达式吧。

lambda表达式.jpg

Lambda表达式

Lambda表达式是一个匿名函数,我们可以将Lambda表达式理解为一段可以作为参数传递的代码,通过Lambda表达式,我们可以将Java程序变得更加简洁和灵活。

来看一段程序:

@Test
public void test() {
    Comparator<Integer> comparator = new Comparator<Integer>() {
        @Override
        public int compare(Integer o1, Integer o2) {
            return o2 - o1;
        }
    };
    Set<Integer> set = new TreeSet<>(comparator);
    set.add(3);
    set.add(2);
    set.add(1);
    System.out.println(set);
}

在JDK8以前,若是想实现对TreeSet的自定义排序,我们可以创建匿名的比较器类将其传入TreeSet的构造方法,而JDK8之后,使用Lambda表达式可以简化匿名内部类的代码:

@Test
public void test() {
    Comparator<Integer> comparator = (o1, o2) -> o2 - o1;
    Set<Integer> set = new TreeSet<>(comparator);
    set.add(3);
    set.add(2);
    set.add(1);
    System.out.println(set);
}

原本匿名内部类的创建现在直接被简化成了一行代码:

Comparator<Integer> comparator = (o1, o2) -> o2 - o1;

下面就来看看Lambda该如何使用,也就是学习一下它的语法。


Lambda表达式语法

在Java8中新引入了一个操作符:-> ,它被称为箭头操作符或Lambda操作符,箭头操作符将Lambda表达式拆分成了左侧和右侧的两部分,其中左侧为Lambda表达式的 参数列表 ,右侧为Lambda表达式的功能代码,也叫 Lambda体 。对应一个具体的Lambda表达式:

Comparator<Integer> comparator = (o1, o2) -> o2 - o1;

此时箭头操作符的左侧就是Comparator接口中compare方法的参数列表,右侧即为该方法所需要实现的功能:

@FunctionalInterface
public interface Comparator<T> {
    int compare(T o1, T o2);
    ......
}

基于接口中方法声明的不同,Lambda表达式的编写方式也会随之发生相应的变化,大体分为以下几类:

  • 无参无返回值:Runnable runnable = () -> System.out.println("Hello Lambda!");

  • 有一个参数但无返回值:Consumer consumer = (s) -> System.out.println(s);

    • 若是方法只有一个参数,则可以省略小括号,所以可以简写为:Consumer consumer = s -> System.out.println(s);

  • 有多个参数有返回值,且Lambda体中有多条语句:

Comparator<Integer> comparator = (o1, o2) -> {
    System.out.println("从大到小排列");
    return o2 - o1;
};

若是Lambda体中有多条语句,则Lambda体必须用大括号包裹起来,若是只有一条语句,则可以省略大括号。

  • 有多个参数有返回值,但Lambda体中只有一条返回语句:Comparator comparator = (o1, o2) -> o2 - o1;

    • 对于这种情况,可以省略大括号和return关键字,Lambda体中只剩下需要return的内容

会发现在所有的Lambda表达式中我们并没有为参数定义任何类型,这是因为JVM编译器能够通过上下文自动推断出参数类型。

需要注意的是,并不是所有的接口实现都可以使用Lambda表达式,它需要 函数式接口 的支持,那么什么是函数式接口呢?

函数式接口指的是接口中只有一个抽象方法的接口

这非常好理解,若是接口中存在多个抽象方法,Lambda表达式是无法知晓我们到底需要实现哪个方法的,所以Lambda表达式的使用必须基于函数式接口。


JDK1.8为此专门提供了 @FunctionalInterface 注解来声明一个函数式接口,倘若在接口中声明了该注解,则该接口必须拥有且只能拥有一个方法:

@FunctionalInterface
interface calc{
    void  add();
}

若不满足要求则会编译报错。


有些同学可能会发现在使用Lambda表达式实现一些功能时,还需要自己去额外编写一个函数式接口,而事实上,JDK1.8已经为我们内置了四大核心函数式接口,分别是:

  1. Consumer:消费型接口,抽象方法为:void accept(T t);

  2. Supplier:供给型接口,抽象方法为:T get();

  3. Function<T, R>:函数型接口,抽象方法为:R apply(T t);

  4. Predicate:断言型接口,抽象方法为:boolean test(T t);

通过它们就已经能够解决大部分的问题了,具体使用哪个接口可以根据自己的实际需求决定,比如若是需要实现的功能带参数而无返回值,则使用消费型接口;若是需要实现的功能无参数但有返回值,则使用供给型接口。

这里以供给型接口为例,实现一个需求,产生指定个数的整数,并放入集合中,代码如下:

@Test
public void test() {
    List<Integer> list = getList(5, () -> new Random().nextInt(20));
    System.out.println(list);
}
public List<Integer> getList(int len, Supplier<Integer> supplier) {
    List<Integer> list = new ArrayList<>();
    for (int i = 0; i < len; i++) {
        list.add(supplier.get());
    }
    return list;
}

方法引用

若Lambda体中的内容有方法已经实现了,那么就可以使用方法引用,可以理解为方法引用是Lambda表达式的另一种表现形式,方法引用主要有以下三种形式:

  1. 对象::实例方法名

  2. 类::静态方法名

  3. 类::实例方法名

来看一个例子:

@Test
public void test() {
    Consumer<String> consumer = (x)-> System.out.println(x);
    consumer.accept("Hello!");
}

这里使用Consumer消费型接口实现了一个输出字符串的功能,由于Lambda体中的内容已经被 System.out.println 实现了,所以可以简写为:

@Test
public void test() {
    Consumer<String> consumer = System.out::println; // 简写为...
    consumer.accept("Hello!");
}

然而方法引用需要遵循一个原则,即:Lambda体中的方法参数和返回值需要与函数式接口中的抽象方法声明一致,比如这里的Consumer接口中的抽象方法为:

void accept(T t);

而输出语句的声明如下:

public void println(String x) {
    synchronized (this) {
        print(x);
        newLine();
    }
}

它们都带有一个参数且无返回值,所以可以使用方法引用。

又比如:

@Test
public void test() {
    User user = new User();
    user.setName("aaa");
    Supplier<String> supplier = () -> user.getName();
    String str = supplier.get();
    System.out.println(str);
}

这里的 user.getName() 也可以使用方法引用,简写为:

Supplier<String> supplier = user::getName;

这也是因为User中get方法与函数式接口中方法参数和返回值的声明相同:

// User对象的getName方法
public String getName() {
    return name;
}
// Supplier接口的get方法
T get();

我们还可以方法引用类的静态方法,例如:

@Test
public void test() {
    Comparator<Integer> comparator = Integer::compare;
    int result = comparator.compare(1, 2);
    System.out.println(result);
}

因为Lambda体中的内容已经被compare方法实现且参数和返回值声明与Comparator接口中的抽象方法声明相同,它就能够使用方法引用:

Comparator<Integer> comparator = Integer::compare;

且compare是Integer类的静态方法,这种引用方式被称为类的静态方法引用。

最后一种形式是类的实例方法引用,比如:

@Test
public void test() {
    BiPredicate<String, String> biPredicate = (s1, s2) -> s1.equals(s2);
    boolean flag = biPredicate.test("abc", "abc");
    System.out.println(flag);
}

这里实现了函数式接口的方法使其能够判断两个字符串的内容是否相同,它能够被简写为:

BiPredicate<String, String> biPredicate = String::equals;

对于类的实例方法引用,也有它的要求,必须满足第一个参数是方法的调用者,第二个参数是调用方法的参数才能使用该引用方式。

构造器引用

构造器引用与方法引用类似,它通过 类名::new 实现,比如:

@Test
public void test() {
    Supplier<User> supplier = ()->new User();
    User user = supplier.get();
    System.out.println(user);
}

此时创建User对象的过程就可以使用构造器引用来简化:

Supplier<User> supplier = User::new;

我们都知道一个类可以有多个重载的构造器,那么构造器引用调用的是类中的哪个构造器呢?和方法引用类似,我们仍然通过构造器方法与接口中抽象方法参数和返回值的声明来判断调用哪个构造器,这里的Supplier接口中的抽象方法是一个不带参数的方法:

T get();

所以它将调用对象的无参构造方法,又比如:

@Test
public void test() {
    Function<String, User> function = User::new;
    User user = function.apply("zhangsan");
    System.out.println(user);
}

来看看Function接口中的抽象方法:

R apply(T t);

它带有一个参数,所以调用的是User对象带一个参数的构造方法:

public class User {
    private String name;
    
    public User(String name){
        this.name = name;
    }
 ......
}
下一篇: Java 8 Stream
推荐文章
  • 架构师在进行系统设计时,遵循一套复杂且综合的方法论,主要包括以下核心步骤:需求分析:理解并明确业务需求是架构设计的第一步。架构师需要与各利益相关者深入沟通,收集和分析业务需求、性能需求、安全性需求、扩展性需求等。领域建模:基于需求分析,构建抽象的业务模型或数据模型,明确系统的边界、核心实体及其关系。
  • 根据阿里交易型业务的特点,以及在双十一这样业内罕有的需求推动下,我们在官方的MySQL基础上增加了非常多实用的功能、性能补丁。而在使用MySQL的过程中,数据一致性是绕不开的话题之一。本文主要从阿里巴巴“去IOE”的后时代讲起,向大家简单介绍下我们过去几年在MySQL数据一致性上的努力和实践,以及目
  • 随着深度学习在图像、语言、广告点击率预估等各个领域不断发展,很多团队开始探索深度学习技术在业务层面的实践与应用。而在广告CTR预估方面,新模型也是层出不穷:WideandDeep、DeepCrossNetwork、DeepFM、xDeepFM,美团很多篇深度学习博客也做了详细的介绍。但是,当
  • 1.背景搜索优化问题,是个典型的AI应用问题,而AI应用问题首先是个系统问题。经历近10年的技术积累和沉淀,美团搜索系统架构从传统检索引擎升级转变为AI搜索引擎。当前,美团搜索整体架构主要由搜索数据平台、在线检索框架及云搜平台、在线AI服务及实验平台三大体系构成。在AI服务及实验平台中,模型训练平台
  • 行业算法版简介OpenSearch-行业算法版是基于阿里巴巴自主研发的大规模分布式搜索引擎搭建的一站式智能搜索业务开发平台,目前为包括淘宝、天猫在内的阿里集团核心业务提供搜索服务支持。通过内置各行业的查询语义理解、机器学习排序算法等能力,提供充分开放的引擎能力,助力开发者快速搭建智能搜索服务。Ope
  • 一.概述我们在考虑MySQL数据库的高可用的架构时,主要要考虑如下几方面:如果数据库发生了宕机或者意外中断等故障,能尽快恢复数据库的可用性,尽可能的减少停机时间,保证业务不会因为数据库的故障而中断。用作备份、只读副本等功能的非主节点的数据应该和主节点的数据实时或者最终保持一致。当业务发生数据库切换时
学习大纲