Lambda表达式详解

作者: 贪婪的君子 | 来源:发表于2017-04-03 08:50 被阅读1311次

    在Java SE 8中新增了一项比较有用的特性,这项特性可以是我们的代码变得更加简洁(concise),这就是Lambda表达式了。

    1. 概述


    Lambda表达式是Java SE 8 中新增的一项特性,Lambda表达式是由以下三部分组成:

    • 由逗号分隔的,被包含在同一个小括号下的多个参数列表(A comma-separated list of formal parameters enclosed in parentheses.),在这里要说明这个小括号,当只有一个参数时可以省略,但当括号内没有参数的时候,不可省略。
    • 一个箭头符号(The arrow token, ->
    • 代码实体(Body),可以是由一个简单的表达式(如,算术表达式或**return value; **这种形式),也可以是一个由多个表达式共同组成并被包含在同一个大括号中的实体。
    (char a, char b) -> {
        if (a != b) return "a + b";
        else return "a - b";
        } //包含了Lambda表达式的所有构成部分
    ( ) -> return 10;    //一个简单的Lambda表达式,表示其返回了常量10
    

    Lambda表达式的出现是为了是代码更加简洁高效,也能够避免程序员大量的重复低效的工作。

    2. Lambda表达式具体实现


    2.1 完整示例

    <i class = "icon-pencil"></i>

    interface Computer {
        public String argu();
    }
    
    public class ComputerDemo {
        String brance;
        int price;
    
        public ComputerDemo() {
            brance = "ASUS";
            price = 4999;
        }
    
        public void printArgu(Computer c) {
            System.out.println(c.argu());
        }
    
        public String getBrance() {
            return brance;
        }
    
        public int getPrice() {
            return price;
        }
    
        public String print() {
            return this.brance + " costs " + this.price + " CNY";
        }
    
        public static void main(String[] args) {
            ComputerDemo computer = new ComputerDemo();
            computer.printArgu(() -> {
                return computer.getBrance() + " costs " 
                    + computer.getPrice() + " CNY";
            });
    
            computer.printArgu(() -> computer.print());
            computer.printArgu(computer::print);
        }
    }
    

    2.2 函数式接口

    想要理解Lambda表达式,首先需要了解什么是函数式接口

    • 只包含一个抽象方法的接口。也称其为(单实例抽象方法接口, 即Single Abstract Method Interface,SAM接口

    函数式接口在Java SE 8 中引入,并且接口都含有一个新增的@FunctionalInterface注解,用于编译级错误检查。

    Note:
    关于函数式接口,在这里只需要知道其为一个只包含一个抽象方法的接口就可以了,不会影响对后续内容的理解。

    如在示例中所定义的Computer接口:

    interface Computer {
        public String argu();
    }
    

    我们看到,在这个接口中,只存在了一个方法用于返回属性字符串。
    如果我们向当前的Computer接口中增加一个新的方法,那还是函数式接口吗?当然不是,试着增加一个方法后,再编译这段代码时会提示,Computer接口不是一个函数式接口。

    2.3 方法引用

    先看看 :: 运算符,熟悉C++的人都知道,:: 运算符可以规定访问权限,标明所属关系,隐藏再现等,而在Java中::表示一种引用。
    我们现在所要接触的是方法引用(Method Reference),包含如下几种:

    • 静态方法引用(Reference to a static method),使用ContainingClass::staticMethodName的方式访问。
    • 特定对象的实例方法引用(Reference to an instance method of a particular object),使用containingObject::instanceMethodName的方式访问。
    • 特定类型的任意对象方法引用(Reference to an instance method of an arbitrary object of a particular type),使用ContainingType::methodName的方式访问。
    • 构造器引用(Reference to a constructor),使用ClassName::new的方式访问。

    再回头看之前的示例,发现其中存在一个print( )方法以及其对应的使用

    public String print( ){     //....      }
    computer.printArgu(() -> computer.print());
    computer.printArgu(computer::print);
    

    结合现在所讲的方法引用,可以很清楚的看出computer::print就是一种特定对象实例方法的引用。其中Lambda表达式 ( ) -> computer.print( ) 与之等价。

    2.4 无名函数

    与匿名内部类相似,我们可以将Lambda表达式看作是一个函数,没有名字的函数,但不同的是,匿名内部类依然使用了new关键字创建一个实例,而Lambda表达式则是直接实现了函数式接口中的方法作为传入参数。

    2.5 变量作用域

    了解了Lambda表达式的基本语法,知道什么是函数式接口以及方法引用之后,关于Lambda表达式的内容基本就已经介绍完了,凭借这些,我们就已经可以写出简单的Lambda表达式了。
    在接下来我们需要知道的是Java语言中规定的Lambda表达式访问权限。

    • Lambda表达式只能引用值不会改变的变量,即final或是effectively final这一点与[Local Class][id](局部内部类)相似。这是最重要的一个限制,其后的访问都基于此限制(如无此限制,在并发执行操作时会不安全)。
    • 可以访问当前方法的变量,也可以访问当前类的公有,私有的成员,这一点与内部类相同。
    • 适用于命名冲突和屏蔽规则,如在方法中不能存在两个同名的局部变量,Lambda表达式也同样不能出现这种情况。
    • this关键字,Lambda表达式中使用this指向创建这个表达式方法的this参数。

    <i class = "icon-pencil"></i>示例程序

    import java.awt.*;
    import java.awt.event.*;
    import javax.swing.*;
    
    public class Demo {
        private static final int WIDTH = 300;
        private static final int HEIGHT = 200;
    
        public String text = "Demo";
    
        public void Tip() {
            JFrame frame = new JFrame("Test");
            frame.setSize(300, 200);
            frame.setVisible(true);
            frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
            JButton button = new JButton("Lambda");
            button.setSize(100, 50);
            frame.add(button); 
            int x = 1;
            button.addActionListener(event -> {
                JOptionPane.showMessageDialog(null,
                 this.getText()); 
                JOptionPane.showMessageDialog(null,
                 this.text);
                //this的引用指向当前实例对象,可以调用该实例对象的方法,也可以直接访问实例的属性值
                x++;
                //这一步操作会在编译时出现错误,提示:从lambda 表达式引用的本地变量必须是最终变量或实际上的最终变量
            });
        }
    
        public String getText() {
            return text;
        }
    
        public static void main(String[] args) {
            new Demo().Tip();   
        }
    }
    

    以上代码也可以表现Lambda表达式使用在GUI编程时相较于匿名内部类的好处。
    [id]:http://blog.csdn.net/ai977313677/article/details/63253137

    3. 总结


    回顾前文可以知道,Lambda表达式具有简洁、高效等多种特点,概念上看起来很复杂,但是Lambda表达式实质上就是一个代码块,外加传入代码的变量规范

    作为Java SE 8中新增的特性,也许是设计者考虑到不让接口的概念更加模糊,也许是为了维持我们所熟悉的Java接口概念,所以Lambda表达式并不是作为一个新的函数类型出现的,但无论怎么说,Lambda表达式是为了方便程序员更有效率的进行程序开发而出现的这一点上,无法否认。

    相关文章

      网友评论

        本文标题:Lambda表达式详解

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