0Pricing
Java Academy · 课时

局部类与匿名类

在方法内部声明类,并即时创建接口的匿名实现

局部类与匿名类 是 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 反馈 — 无需本地设置。

此课程中的所有课时

  1. 静态嵌套类
  2. 内部类与访问外部类
  3. 局部类与匿名类
  4. 选择合适的嵌套策略
← 返回 Java Academy