必须用匿名内部类而非Lambda的场景包括:接口含多个抽象方法、需调用父类构造器、声明实例字段、重写多个方法或在实例初始化块中执行逻辑,如WindowAdapter子类或双大括号初始化。
匿名内部类在现代Java开发中已基本被Lambda表达式和方法引用替代,仅在特定场景下仍有不可替代性——比如需要继承非函数式接口的抽象类、或需在实例初始化块中执行逻辑时。
Lambda只能用于函数式接口(仅含一个抽象方法),一旦接口有多个抽象方法,或你需要调用父类构造器、声明实例字段、重写多个方法,就必须用匿名内部类。
WindowAdapter 是典型例子:它继承自 WindowAdapter 抽象类,而该类包含多个空实现的回调方法,你通常只重写其中一两个,
final 实例变量并初始化,比如缓存某个计算结果,Lambda做不到这点new ArrayList(initialCapacity) {{ add("x"); }} (双大括号初始化,虽不推荐但属匿名内部类的合法用法)匿名内部类能访问的局部变量必须是“实际上的 final”(effectively final)——即定义后未被重新赋值。编译器会检查这一点,违反时直接报错 local variables referenced from an inner class must be final or effectively final。
String[] holder = new String[1];)或使用原子引用(AtomicReference),而非绕过限制强行声明 final
list.add(...))不受限,受限的是变量本身的指向变更匿名内部类隐式持有外部类实例的引用,若其生命周期长于外部类(例如注册为监听器后未注销),就会阻止外部类被GC回收,造成内存泄漏。
Handler + 匿名内部类是经典泄漏源,解决方案是用静态内部类 + 弱引用,或改用 WeakReference 显式管理removeXXXListener,否则监听器及其捕获的上下文将持续驻留真正需要匿名内部类的地方越来越少,但理解它与 Lambda 的边界、以及它如何绑定外部作用域,仍是排查泄漏和读懂老代码的关键。别为了“看起来简洁”硬套匿名类,先确认接口是否函数式、是否真需要实例状态——多数时候,new Object() { ... } 只是技术惯性的残影。