Lambda表达式与匿名内部类的对比详解

Lambda表达式与匿名内部类的对比详解

1. 语法简洁性
  • Lambda表达式
    仅适用于函数式接口(只有一个抽象方法的接口),语法简洁。
    示例

    Runnable r = () -> System.out.println("Hello Lambda");
    
  • 匿名内部类
    适用于任何接口或抽象类,需完整定义类结构。
    示例

    Runnable r = new Runnable() {
        @Override
        public void run() {
            System.out.println("Hello Anonymous Class");
        }
    };
    

关键差异
Lambda省去了接口名、方法名和new关键字,代码更紧凑。


2. this指向
  • Lambda表达式
    this指向包围Lambda的类实例
    示例

    public class Outer {
        public void method() {
            Runnable r = () -> System.out.println(this); // 输出Outer实例
        }
    }
    
  • 匿名内部类
    this指向匿名内部类自身的实例
    示例

    public class Outer {
        public void method() {
            Runnable r = new Runnable() {
                @Override
                public void run() {
                    System.out.println(this); // 输出匿名内部类实例
                }
            };
        }
    }
    

3. 编译与性能
  • Lambda表达式

    • 编译时生成invokedynamic指令,运行时动态生成实现类。编译之后,没有生成一个单独的.class字节码文件。
    • 性能优化:JVM缓存Lambda实例,避免重复创建(如单例模式)。
  • 匿名内部类

    • 编译时生成独立的.class文件(如Outer$1.class)。
    • 性能开销:每次实例化均创建新对象。

验证方法
运行后检查项目目录下的target/classesout/production文件夹,观察生成的类文件。


4. 变量捕获
  • 共同规则
    只能访问final或**等效final(effectively final)**的局部变量。

  • Lambda表达式
    捕获的变量在Lambda内部隐式视为final,修改会编译报错。
    示例

    int count = 0;
    Runnable r = () -> count++; // 错误:count必须为final或等效final
    
  • 匿名内部类
    规则相同,但错误提示更明确。
    示例

    int count = 0;
    Runnable r = new Runnable() {
        @Override
        public void run() {
            count++; // 错误:从内部类引用的局部变量必须是final或等效final
        }
    };
    

5. 应用场景
  • 优先使用Lambda的场景

    1. 实现函数式接口(如RunnableComparator),Lambda表达式,只能是接口。
    2. 需要简洁的代码(如Stream API、事件处理器)。
    3. 性能敏感的重复实例创建场景。
  • 必须使用匿名内部类的场景

    1. 实现非函数式接口(含多个抽象方法)。
    2. 需要覆盖接口的默认方法访问protected方法
    3. 需要继承具体类抽象类

示例对比

// Lambda:仅适用于单方法接口
Function<String, Integer> lengthFunc = s -> s.length();

// 匿名内部类:可覆盖默认方法
List<String> list = new ArrayList<>() {
    @Override
    public boolean add(String s) {
        System.out.println("添加元素: " + s);
        return super.add(s);
    }
};

6. 内存与类加载
  • Lambda表达式

    • 无额外类文件,减少PermGen/Metaspace内存占用。
    • JVM内部通过LambdaMetafactory动态生成实现。
  • 匿名内部类

    • 每个类生成独立的.class文件,增加元空间负担。
    • 类加载器需加载更多类,影响启动速度。

7. 总结对比表
特性Lambda表达式匿名内部类
适用接口仅函数式接口任意接口或抽象类
语法简洁性
this指向外部类实例内部类自身实例
类文件生成无独立类文件生成Outer$1.class文件
性能开销低(实例可缓存)较高(每次实例化新对象)
变量捕获等效final等效final
覆盖方法不支持支持(可覆盖默认方法)

何时选择Lambda或匿名内部类?

  • 选择Lambda

    • 实现函数式接口且无需覆盖默认方法。
    • 追求代码简洁性和性能优化。
    • 示例:Thread启动、集合排序、Stream操作。
  • 选择匿名内部类

    • 需实现多方法接口或抽象类。
    • 需覆盖接口的默认方法或调用父类protected方法。
    • 示例:自定义List实现、GUI事件监听器(如Swing中的ActionListener)。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

步行cgn

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值