• Lambda表达式与匿名内部类的联系和区别

    Java Lambda 表达式的一个重要用法是简化某些匿名内部类的写法,因此它可以部分取代匿名内部类的作用。

    Lambda 表达式与匿名内部类的相同点如下:

    • Lambda 表达式与匿名内部类一样,都可以直接访问 effectively final 的局部变量(如果不了解 Effectively final,可先阅读《Java8新特性之Effectively final》一节),以及外部类的成员变量(包括实例变量和类变量)。
    • Lambda 表达式创建的对象与匿名内部类生成的对象一样,都可以直接调用从接口中继承的默认方法。

    下面程序示范了 Lambda 表达式与匿名内部类的相似之处。

    @FunctionalInterface
    interface Displayable {
        // 定义一个抽象方法和默认方法
        void display();
    
        default int add(int a, int b) {
            return a + b;
        }
    }
    
    public class LambdaAndInner {
        private int age = 12;
        private static String name = "C";
    
        public void test() {
            String url = "http://c.biancheng.net/";
            Displayable dis = () -> {
                // 访问的局部变量
                System.out.println("url 局部变量为:" + url);
                // 访问外部类的实例变量和类变量
                System.out.println("外部类的 age 实例变量为:" + age);
                System.out.println("外部类的 name 类变量为:" + name);
            };
            dis.display();
            // 调用dis对象从接口中继承的add()方法
            System.out.println(dis.add(3, 5)); 
        }
    
        public static void main(String[] args) {
            LambdaAndInner lambda = new LambdaAndInner();
            lambda.test();
        }
    }

    输出结果为:

    url 局部变量为:http://c.biancheng.net/
    外部类的 age 实例变量为:12
    外部类的 name 类变量为:C
    8

    上面程序使用 Lambda 表达式创建了一个 Displayable 的对象,Lambda 表达式的代码块中的代码第 19、21 和 22 行分别示范了访问“effectively final”的局部变量、外部类的实例变量和类变量。从这点来看, Lambda 表达式的代码块与匿名内部类的方法体是相同的。

    与匿名内部类相似的是,由于 Lambda 表达式访问了 url 局部变量,因此该局部变量相当于有一个隐式的 final 修饰,因此同样不允许对 url 局部变量重新赋值。

    当程序使用 Lambda 表达式创建了 Displayable 的对象之后,该对象不仅可调用接口中唯一的抽象方法,也可调用接口中的默认方法,如上面程序代码第 26 行所示。

    Lambda 表达式与匿名内部类主要存在如下区别。

    • 匿名内部类可以为任意接口创建实例——不管接口包含多少个抽象方法,只要匿名内部类实现所有的抽象方法即可;但 Lambda 表达式只能为函数式接口创建实例。
    • 匿名内部类可以为抽象类甚至普通类创建实例;但 Lambda 表达式只能为函数式接口创建实例。
    • 匿名内部类实现的抽象方法的方法体允许调用接口中定义的默认方法;但 Lambda 表达式的代码块不允许调用接口中定义的默认方法。

    对于 Lambda 表达式的代码块不允许调用接口中定义的默认方法的限制,可以尝试对上面的 LambdaAndInner.java 程序稍做修改,在 Lambda 表达式的代码块中增加如下一行:

    // 尝试调用接口中的默认方法,编译器会报错
    System.out.println(add(3, 5));

    虽然 Lambda 表达式的目标类型 Displayable 中包含了 add() 方法,但 Lambda 表达式的代码块不允许调用这个方法;如果将上面的 Lambda 表达式改为匿名内部类的写法,当匿名内部类实现 display() 抽象方法时,则完全可以调用这个 add() 方法,如下面代码所示。

    public void test() {
        String url = "http://c.biancheng.net/";
        Displayable dis = new Displayable() {
    
            @Override
            public void display() {
                // 访问的局部变量
                System.out.println("url 局部变量为:" + url);
                // 访问外部类的实例变量和类变量
                System.out.println("外部类的 age 实例变量为:" + age);
                System.out.println("外部类的 name 类变量为:" + name);
                System.out.println(add(3, 5));
            }
        };
        dis.display();
    }

更多...

加载中...