Java新特性 lambda表达式

来源:互联网 发布:单片机复位电路特点 编辑:程序博客网 时间:2024/06/05 15:36

lambda表达式

为什么要引入lambda表达式

为了给 Mouse 监听器添加自定义代码,我们定义了一个匿名内部类 MouseAdapter 并创建了它的对象,通过这种方式,我们将一些函数功能传给 addMouseListener 方法。

someObject.addMouseListener(new MouseAdapter() {    public void mouseClicked(MouseEvent e) {        //Event listener implementation goes here...    }});

这个例子的特点是将一个代码块传递到某个对象(Listener),这个代码快会在将来的某个时间调用。

函数式语言提供了一种强大的功能——闭包,相比于传统的编程方法有很多优势,闭包是一个可调用的对象,它记录了一些信息,这些信息来自于创建它的作用域。Java 现在提供的最接近闭包的概念便是 Lambda 表达式,虽然闭包与 Lambda 表达式之间存在显著差别,但至少 Lambda 表达式是闭包很好的替代者。

lambda简介

lambda表达式是一个可传递的代码块,可以在以后执行一次或多次。

lambda语法

 1.参数、箭头以及一个表达式,如果需要多个表达式,使用{}

(String first,String second)->    {        if(first.length() < second.length() return -1;        else if(first.length() > second.length() return 1;        else return 0;    }

 2.即使lambda没参数,仍然要提供空括号,就像无参数方法一样。

 3.如果可以推导出一个lambda表达式的参数类型,则可以忽略其类型。

Comparator<String> comp    = (first, second)  //same as String first        -> first.length() - second.length();

 为什么上面的可以推导出参数类型?因为因为Comparator是一个函数式接口,如下。

    interface Comparator<T>{        int compare(T arg1, T arg2);    }

 4.如果方法只有一个参数,而且这个参数的类型可以推导得出,那么甚至可以省略小括号

    ActionListener listener = event ->        System.out.println("Hello");

 从上面例子也发现,无需制定lambda表达式的返回类型。

lambda表达式的应用

lambda转换为函数式接口

对于只有一个抽象方法的接口,需要这种接口的对象时,就可以提供一个lambda表达式。这种接口称为函数式接口。例子

    Arrays.sort(words,        (first, second) -> first.length() - second.length();

方法引用

可以使用方法引用作为函数式接口内的方法的方法体。
例如:

    Timer t = new Timer(1000,System.out::println);

表达式System.out::println是一个方法引用,它等价于lambda表达式 x->System.out.println(x);

从例子可以发现,要用::分隔方法名和对象或类名。
主要有3种情况:

 object::instanceMethod

 Class::staticMethod

 Class::instanceMethod

在前两种情况中,方法引用等价于提供方法参数的lambda表达式。例子如下:

        System.out::println//等价于 x -> System.out.println(x)        Math::pow//等价于(x, y) -> Math.pow(x, y)

对于第三种情况,即类名::实例方法这种格式,因为实例方法需要实例,所以第一个参数会成为方法的目标。例子:

        String::compareToIgnoreCase//等价于(x, y) -> x.compareToIgnoreCase(y) 

构造器引用

构造器引用和方法引用很类似,只不过方法名为new。

Person::new是Person构造器的一个引用。
具体调用哪一个构造器需要根据上下文推断。

可以用数组类型建立构造器引用。例如,int[]::new是一个构造器引用,它有一个参数:即数组的长度。这等价于x -> new int[x]

变量作用域

通常,我们希望能够在lambda表达式中访问外围方法或类中的变量。例如:

        public static void repeatMessage(String text, int delay)        {            ActionListener listener = event ->             {                System.out.println(text);                Toolkit.getDefaultTolkit.beep();            };//这个分号不能省略            new Timer(delay, listener).start();        }

思考一个问题:lambda表达式代码可能会在repeatMessage调用很久以后才运行,而那个时候参数变量text已经不存在了。

所以lambda表达式的数据结构必须存储自由变量的值,我们称之为捕获。

注意点:

 只能捕获值不会改变的变量

 在lambda表达式中声明与一个局部变量同名的参数或局部变量是不合法的。

 在lambda表达式中使用this关键字时,是指创建这个lambda表达式的方法的this参数。例如

            public class Application()            {                public void init()                {                    ActionListener listener = event ->                    {                        System.out.println(this.toString());                    }                }            }

 表达式this.toString()会调用Application对象的toString方法,而不是ActionListener实例的方法。

学会接收处理lambda表达式

 使用lambda表达式的重点是延迟执行。

举例说明,自定义一个repeat方法,将这个动作和重复次数传递到到一个repeat方法:

repeat(10, () -> System.out.println("Hello World!"));

要接收这个lambda表达式,需要选择一个函数式接口这里选择使用Runnable接口

    public static void repeat(int n, Runnabble action)    {        for(int i = 0; i < n; i++)             action.run();//此处执行lambda表达式的主体    }
原创粉丝点击