局部类与匿名类
在方法内部声明类,并即时创建接口的匿名实现
局部类与匿名类 是 CoddyKit 上的免费 Java Academy 课时。 这是第 3 节课,共 4 节。 你可以在下方免费阅读本课时的完整内容 — 然后在浏览器中使用内置代码编辑器和全天候 AI 导师进行实践。 这是 Java Academy 学习路径的一部分,你的进度在网页和 CoddyKit 应用中同步。 Java Academy 课程共包含 4 节课。
局部类与匿名类
局部类定义在方法内部。匿名类是没有名称的局部类——它将类声明和实例化合并在一起。两者都可以捕获实际上为 final 的局部变量。
局部类声明
局部类声明在方法体内部。它可以访问外围类成员以及实际上为 final 的局部变量。
static void greet(String language) {
// Local class — defined inside the method
class Greeter {
String sayHello(String name) {
// Can access 'language' — it is effectively final
return switch (language) {
case "EN" -> "Hello, " + name;
case "ES" -> "Hola, " + name;
case "FR" -> "Bonjour, " + name;
default -> "Hi, " + name;
};
}
}
Greeter g = new Greeter();
System.out.println(g.sayHello("Alice"));
}
greet("ES"); // Hola, Alice实际上为 Final 的变量
局部类和匿名类只能捕获实际上为 final的局部变量——这些变量在初始化后从未被重新赋值。
void process(int threshold) {
// threshold is effectively final (never reassigned)
Runnable r = new Runnable() {
public void run() {
System.out.println("Threshold: " + threshold);
}
};
r.run();
// threshold = 10; // If you add this, the anonymous class won't compile
}匿名类基础
匿名类可以在一条表达式中直接实现接口或继承类。
interface Comparator<T> {
int compare(T a, T b);
}
// Anonymous class implementing Comparator
java.util.Comparator<String> byLength = new java.util.Comparator<String>() {
@Override
public int compare(String a, String b) {
return Integer.compare(a.length(), b.length());
}
};
List<String> words = new ArrayList<>(List.of("banana", "fig", "apple", "kiwi"));
words.sort(byLength);
System.out.println(words); // [fig, kiwi, apple, banana]匿名类与 Lambda
对于单一抽象方法(SAM)接口,lambda 表达式可以替代匿名类。对于包含多个方法的接口,或者需要继承抽象类时,仍然需要使用匿名类。
// Anonymous class for single-method interface
Runnable r1 = new Runnable() {
public void run() { System.out.println("Running!"); }
};
// Lambda — cleaner for SAM interfaces
Runnable r2 = () -> System.out.println("Running!");
// Must use anonymous class for multi-method interface:
java.io.FilenameFilter filter = new java.io.FilenameFilter() {
public boolean accept(java.io.File dir, String name) {
return name.endsWith(".java");
}
}; // FilenameFilter is not a SAM — no lambda shorthand带状态的匿名类
与 lambda 表达式不同,匿名类可以拥有字段。当您需要在匿名实现中维护状态时,这一点非常有用。
interface Counter { int increment(); int get(); }
Counter counter = new Counter() {
private int count = 0;
public int increment() { return ++count; }
public int get() { return count; }
};
System.out.println(counter.increment()); // 1
System.out.println(counter.increment()); // 2
System.out.println(counter.get()); // 2匿名监听器模式
在 lambda 表达式出现之前,匿名类是 GUI 和 Android 编程中实现事件监听器的标准模式。
// Modern Android/Swing style: anonymous class listener
Button loginButton = new Button(); // hypothetical
loginButton.setOnClickListener(new OnClickListener() {
@Override
public void onClick() {
System.out.println("Login button clicked!");
}
});
// Modern equivalent with lambda (if OnClickListener is SAM):
loginButton.setOnClickListener(() -> System.out.println("Clicked!"));用于复杂逻辑的局部类
当您只在一个地方需要一个包含多个方法的类时,局部类非常合适——这种逻辑对 lambda 表达式来说过于复杂,对顶层类来说又过于具体。
static List<String> processData(List<String> data, String prefix) {
// Local class: complex enough for a class, used only here
class DataProcessor {
List<String> result = new ArrayList<>();
void process(String item) {
// prefix is effectively final
if (item.startsWith(prefix)) {
result.add(item.toUpperCase());
}
}
}
DataProcessor dp = new DataProcessor();
data.forEach(dp::process);
return dp.result;
}
System.out.println(processData(List.of("apple","avocado","banana"), "a"));
// [APPLE, AVOCADO]用于抽象类的匿名类
匿名类可以继承抽象类,并重写抽象方法,而无需创建具名子类。
abstract class ReportFormatter {
abstract String formatHeader(String title);
abstract String formatBody(List<String> rows);
String format(String title, List<String> rows) {
return formatHeader(title) + "\n" + formatBody(rows);
}
}
ReportFormatter csvFormatter = new ReportFormatter() {
public String formatHeader(String t) { return "\"" + t + "\""; }
public String formatBody(List<String> rows) {
return String.join("\n", rows);
}
};
System.out.println(csvFormatter.format("Sales", List.of("a,1","b,2")));双大括号初始化反模式
双大括号初始化技巧使用匿名类来初始化集合。它会创建一个带有实例初始化块的子类。请避免使用它——这种方式效率低且容易出错。
// Anti-pattern: double-brace initialization
Map<String, Integer> map = new HashMap<String, Integer>() {{
put("a", 1);
put("b", 2);
}};
// Creates an anonymous HashMap subclass — wasteful!
// GOOD: use Map.of() or Map.ofEntries()
Map<String, Integer> better = Map.of("a", 1, "b", 2);
// Or a mutable map:
Map<String, Integer> mutable = new HashMap<>(Map.of("a", 1, "b", 2));选择:Lambda、匿名类还是局部类
选择指南:
- lambda 表达式:单一抽象方法、无需额外状态且不需要继承
- 匿名类:包含多个方法、需要状态,或需要继承抽象类
- 局部类:需要在方法内部复用,包含多个方法或复杂状态
快速检查
匿名类在一条表达式中始终会做什么?
回顾:局部类与匿名类
要点:
- 局部类定义在方法内部,可以访问实际上为 final 的局部变量
- 匿名类在一条表达式中合并类声明和实例化
- 匿名类可以拥有字段和多个方法,而 lambda 表达式不能
- 对于单一抽象方法接口,lambda 表达式可以替代匿名类
- 当逻辑复杂到适合使用类、但又只针对一个方法时,请使用局部类
- 避免双大括号初始化——它会创建不必要的匿名子类
常见问题解答
「局部类与匿名类」课时是免费的吗?
是的 — 「局部类与匿名类」的完整文本可在网页上免费阅读。要进行交互式练习(内置代码编辑器和全天候 AI 导师)并解锁 Java Academy 课程的其余内容,请升级到 CoddyKit PRO。 Java Academy 课程共包含 4 节课。
「局部类与匿名类」这节课中我会学到什么?
在方法内部声明类,并即时创建接口的匿名实现 你通过在浏览器中直接运行的动手代码来练习 Java Academy,全天候 AI 导师会在你学习这节课的过程中回答你的问题。
学习 Java Academy 需要有经验吗?
无需任何先前经验。CoddyKit 上的 Java Academy 课程适合初学者到高级学习者,你可以从这里开始或从头开始,按照自己的节奏学习。 这是第 3 节课,共 4 节。
「局部类与匿名类」课时需要多长时间?
大多数 CoddyKit 课程大约需要 5–10 分钟。每节课都很精短且互动,所以你能稳步进步,并在网页和应用中从离开的地方继续。
我能在这节 Java Academy 课中编写并运行代码吗?
能。每节 Java Academy 课都包含内置代码编辑器,你可以在浏览器中直接编写并运行真实代码,并获得即时 AI 反馈 — 无需本地设置。