跳转至

14 内部类与匿名类

本章目标

  • 理解内部类的四种形式
  • 掌握匿名类(模组事件监听常用)
  • 了解静态内部类与局部内部类
  • 理解为什么模组代码里到处都是匿名类

14.1 什么是内部类?

定义在另一个类里面的类,叫内部类。它可以访问外部类的成员(包括 private)。

public class Player {
    private int level = 1;

    // 内部类
    public class Inventory {
        public void show() {
            System.out.println("玩家等级: " + level);   // 可以访问外部类私有成员
        }
    }
}

// 使用(非静态内部类需要外部类实例)
Player p = new Player();
Player.Inventory inv = p.new Inventory();
inv.show();

14.2 静态内部类(static nested class)

加 static,不持有外部类引用,最常用、最推荐:

public class GameData {
    private static int version = 3;

    public static class PlayerData {      // 静态内部类
        private String name;
        private int level;

        public PlayerData(String name, int level) {
            this.name = name;
            this.level = level;
        }

        public String describe() {
            // 只能访问外部类的静态成员
            return name + " Lv." + level + " (数据版本 " + GameData.version + ")";
        }
    }
}

// 使用
GameData.PlayerData data = new GameData.PlayerData("小明", 30);

为什么常用

静态内部类不依赖外部类实例,逻辑独立又和外部类相关,是组织代码的好方式。模组里"主类 + 数据类"经常这么搭配。

14.3 局部内部类与匿名类

局部内部类:定义在方法里

public void run() {
    class Helper {          // 只在这个方法里用
        void help() { System.out.println("帮助中…"); }
    }
    new Helper().help();
}

匿名类:没有名字,直接 new 接口/抽象类

// 普通做法:定义实现类
class Dog implements Soundable {
    public void makeSound() { System.out.println("汪汪"); }
}

// 匿名类做法:用一次不命名
Soundable dog = new Soundable() {
    @Override
    public void makeSound() {
        System.out.println("汪汪");
    }
};
dog.makeSound();

匿名类适合"只使用一次"的实现,不用单独建类文件。

14.4 模组里最常见的匿名类场景

监听事件(Java 8 之前的写法,现在多数用 Lambda,第 16 章):

// 匿名类:实现接口
button.setOnClickListener(new OnClickListener() {
    @Override
    public void onClick() {
        System.out.println("按钮被点击了");
    }
});

// Lambda 等价写法(第 16 章)
button.setOnClickListener(() -> System.out.println("按钮被点击了"));

Forge 事件监听也常见:

// Forge 事件总线注册(匿名类风格)
MinecraftForge.EVENT_BUS.register(new Object() {
    @SubscribeEvent
    public void onPlayerJoin(PlayerEvent.PlayerLoggedInEvent event) {
        System.out.println(event.getEntity().getName().getString() + " 加入了游戏");
    }
});

14.5 内部类访问外部变量:必须 final/有效 final

方法里的局部变量被内部类/匿名类使用时,必须是有效 final(初始化后不再修改):

public void test() {
    String msg = "你好";     // 有效 final(后面没改)
    Runnable r = new Runnable() {
        @Override
        public void run() {
            System.out.println(msg);   // ✅
        }
    };

    int count = 0;
    Runnable r2 = new Runnable() {
        @Override
        public void run() {
            // count++;    // ❌ 编译错误:count 不是有效 final
        }
    };
}

14.6 实战:模组场景

// 给物品注册右键使用逻辑(匿名类风格示意)
public class MyItems {
    public static final Item MAGIC_WAND = new Item(new Item.Properties()) {
        @Override
        public ActionResult<ItemStack> use(Level level, Player player, InteractionHand hand) {
            player.sendSystemMessage(Component.literal("✨ 魔法杖释放了法术!"));
            return ActionResult.success(player.getItemInHand(hand));
        }
    };
}

14.7 小练习

  1. 写一个外部类 School,内部静态类 Student(姓名/分数),main 里创建几个学生
  2. 用匿名类实现一个接口 Greeter { String greet(String name); }
  3. 验证"方法局部变量被匿名类使用必须有效 final"

本章小结

  • 内部类可以访问外部类成员
  • 静态内部类最常用(不依赖外部实例)
  • 匿名类适合一次性实现,模组事件监听常见
  • 被内部类使用的局部变量必须有效 final

下一章:15 枚举与注解

🏠 返回总站