Java 1.8新特性(lambda表达式)

lambda表达式对于Java语言来说是一个重大的变化,对于面向对象编程的Java语言来说它提供了一种倾向于函数式编程的方式。lambda表达式是一个可传递的代码块,可以在以后执行一次或多次。 

 lambda表达式的形式

有参:(参数1,参数2,...,参数n) -> {表达式}

无参:() -> {表达式}

无参单句表达式:() -> 表达式 ;

单参单句表达式:参数 -> 表达式 ;

在lambda表达式中我们无需指定返回类型,因为返回类型会根据上下文推导得出。

 

函数式接口(lambda表达式的具体实现)

这是事件监听ActionListener接口的源码,就你所看到的,里面声明了一个actionPerformed的抽象方法(也仅有一个抽象方法)。它有一个ActionEvent类型的参数。

public interface ActionListener extends EventListener {

 
    public void actionPerformed(ActionEvent e);

}

 在下面这段代码中我们可以利用lambda表达式来实例一个ActionListener类型对象,可以看出他使ActionListener接口中唯一的抽象方法actionPerformed的实现在形式上等同于该接口的一个具体实例,这时仿佛是把一个函数传递给了一个类型引用。

    ActionListener actionListener = (event)->{
        System.out.println(event);
    };

但是从面向对象的角度想,你可能会觉得此时的lambda表达式像是一个匿名内部类。从功能上和一些语法上来讲,确实相似。但是不同的是,lambda表达式所使用的接口必须是函数式接口(只有一个抽象方法的接口),而且一般lambda表达式比使用内部类更加高效。


方法引用

/*
* 定义函数式接口
* */
interface FuntionInterface{
    public void show(String a);
}

/*
* 定义使用函数式接口的类
* */
class TestLambda{
    public TestLambda(FuntionInterface a){
    /*
    * do something
    * 这里很明显,我们使用函数式接口的目的就是调用其中的抽象方法。
    * */
    a.show("FuntionInterface");
    }
}

public class FuUse{
    public static void main(String[] args) {
        /*
        * 而方法的具体实现可以延迟到使用时。
        * */
    new TestLambda(System.out::print);
    }
}
/*
* output:
* FunctionInterface
* */

上面的代码是我自己定义和使用的一个函数式接口运用lambda表达式的例子,但是我们发现在传递FunctionInterface类型是并没有使用lambda表达式,而是使用了System.out::print这种结构,这种结构就叫做方法引用它将System.out中的print方法引用给lambda表达式,这等价于使用(a)->System.out.print(a);这句lambda表达式。学过C++的小伙伴对::肯定不陌生,两者的用法和意义在调用方法上没多大区别。

那么方法引用是怎样引用的呢?

  • object::instanceMethod(实例对象引用实例方法)
  • class::staticMethod(类名引用静态方法)
  • class::instanceMethod(类名引用实例方法)

前两种相信大家看了上面的代码都是可以理解的,第三种使用类名去引用实例方法从字面上看好像有着问题,毕竟未实例化的类是不能调用实例方法的,但是它并不是字面上的那种意思。比如说String::compareToIgnoreCase,等同于(x,y)->x.compareToIgnoreCase(y);它会选择参数列表中的第一个参数作为该方法的调用者,而这里只是说明了此方法来自于那个类而已。

还有一种构造器引用:类名::new,例如:Person::new。

 

lambda表达式的自由变量

其实归根结底,lambda表达式是由三部分组成的。

  • 一个代码块(lambda表达式的体)
  • 参数列表(即()里的参数)
  • 自由变量(非参数也不在表达式中定义的变量)
public class FuUse{
    public static void fun(String text){
        //text+="ADD";  Error!
        new TestLambda(a->{
            //text+="ADD"; Error!
            System.out.println(a+text);
        });
    }
    public static void main(String[] args) {
        fun("自由变量");
    }
}
/*
 * output:
 * FuntionInterface自由变量
 * */

我们使用上面的代码介绍自由变量的使用规则,其中我们在静态方法fun中传入了一个String类型的参数text,对于a->{ System.out.println(a+text); }lambda表达式来说,text就是一个自由变量。几乎和匿名内部类一样,Java不允许传入其中的自由变量被更改,所以不管是在表达式体外还是体内,自由变量text的值都不能该表,也就是说自由变量必须是最终变量

posted @ 2018-12-08 20:52  问月晚安  阅读(118)  评论(0编辑  收藏  举报