美文网首页
lambda表达式

lambda表达式

作者: 一个小箱子 | 来源:发表于2019-02-26 09:54 被阅读0次

Java 8 主要是在原来面向对象的基础上增加了函数式编程的能力。

lambda表达式是一段可以传递的代码,它可以被执行一次或多次。

目录

一、为什么要使用lambda表达式

在lambda出现之前,在Java中向其他代码传递一段代码并不容易。

由于Java是一个面向对象的语言,因此你不得不构建一个属于某个类的对象,由该对象的某个方法来包含所需的代码。

// a simple example of how to sort a list of strings in prior versions of Java
List<String> names = Arrays.asList("peter", "anna", "mike", "xenia");

Collections.sort(names, new Comparator<String>() {
    public int compare(String a, String b) {
        return b.compareTo(a);
    }
});

lambda表达式的出现,使得在Java语言中传递一段代码也很容易。

二、lambda 表达式的语法

以上面的sort a list of strings 为例,使用lambda表达式的语法,可以这样写:

// a simple example of how to sort a list of strings using lambda
Collections.sort(names, (String a, String b) -> {
    return a.compareTo(b);
});

由上面的例子,不难发现,lambda表达式的语法格式为:参数列表、箭头->,以及一个代码块

在该语法基础上,还有一些附加语法:

// 如果代码块比较简单,可以用一个表达式替代:
Collections.sort(names, (String a, String b) -> b.compareTo(a));

// 如果表达式没有参数,用一对空的小括号表示:
() -> { System.out.println("hi~"); }

// 如果表达式的的参数类型是可以被推导出的,就可以省略参数类型:
Collections.sort(names, (a, b) -> b.compareTo(a));

// 如果表达式只有一个参数,且该参数类型可被推导出来,就可以省略小括号:
s -> { System.out.println(s); }

// 可以像对待方法参数一样,向lambda表达式的参数添加final修饰符和注解:
(final String name) -> ...
(@NonNull String name) -> ...

三、函数式接口

functional interface,即函数式接口,是指只有一个抽象方法的接口。

a.对该接口的其他方法,如default方法、static方法没有要求

b.对于接口中声明的Object类中的方法,如equals()等,不算在抽象方法的统计里面(https://docs.oracle.com/javase/specs/jls/se8/html/jls-9.html#jls-9.4.1.2)

使用@FunctionalInterface注解来标注一个函数接口,可以在编译期确保一个接口只有一个抽象方法。

也就是说,这个注解也不是必须的,不用这个注解标注,也没啥问题

函数式接口与lambda表达式有什么关系?

对一个函数式接口,你可以通过lambda表达式来创建该接口的对象。

// 1.通过lambda表达式来创建函数式接口的对象
Comparator<String> comparator = (a, b) -> { return b.compareTo(a); };

// 2.不使用FunctionalInterface标注函数式接口的例子
public interface MyFunctionalInterface<T> {
    int compare(T o1, T o2);
}
MyFunctionalInterface<String> myFunctionalInterface = (a, b) -> { return b.compareTo(a); };

java与其他语言的不同:

在很多其他的支持函数式编程的语言中,可以直接将lambda表达式赋值给一个函数类型的变量。但是,Java的设计者们没有使用这种方式,他们坚持使用熟悉的接口概念,而没有将函数类型加入到Java中。

你甚至不能将一个lambda表达式赋值给一个Object类型的变量,因为Object不是一个函数式接口。

四、方法引用

如果要传递的代码已经有别的方法实现了,可以直接传递该方法引用。

有3种方法引用的传递方式:

  1. 对象::实例方法

  2. 类::静态方法

  3. 类::实例方法

前两种方式,方法引用等同于提供方法参数的lambda表达式;第三种方式,第一个参数会成为执行方法的对象。

// 方式1,对象::实例方法
System.out::println // 相当于 System.out::println(x)
this::equals // 相当于 this.equals(x)
// 方式2,类::静态方法
Math::pow // 相当于Math.pow(x,y)
// 方式3,类::实例方法
String::compareToIgnoreCase  // 相当于 x.compareToIgnoreCase(y)

五、构造器引用

构造器引用可以看作一种特殊的方法引用,它与方法引用类似,不同的是构造器引用中方法名是new。

对拥有多个构造器的类,使用那个构造器,取决于上下文。

ArrayList::new // 相当于 new ArrayList()
int[]::new // 相当于 x -> new int[x]

六、变量作用域

一个例子:

// 使用lambda表达式声明一个方法sayHi()
public void sayHi(int count) {
        new Thread(() -> {
            for (int i = 0; i < count; i++) {
                System.out.println("hi~");
            }
        }
        ).start();
}
// 调用这个方法,打印10次hi~
sayHi(10);

变量count并没有在lambda表达式中定义,而是方法sayHi()的参数变量。

lambda表达式可能在sayHi(10)返回之后才执行,此时参数变量已经销毁了,怎么会能正常打印结果呢?

lambda表达式中的变量作用域:

一个lambda表达式分为3部分:

  1. 一段代码

  2. 参数

  3. 自由变量的值(不是参数且没有在lambda代码中定义的变量)

这里的count即为lambda表达式中的自由变量,它的值已经被lambda表达式捕获了。(含有自由变量的代码块,被称作“闭包”)

可以将一个表达式转化为只含有一个方法的对象,这样自由变量的值就会被复制到该对象的实例变量中。

在lambda表达式中,被引用变量的值不能被更改。

七、默认方法

八、接口中的静态方法

参考资料:

《写给大忙人看的 Java SE 8》

java-8-tutorial

相关文章

网友评论

      本文标题:lambda表达式

      本文链接:https://www.haomeiwen.com/subject/alboyqtx.html