Enum Resolution - HimxhMC/HimDSL GitHub Wiki

枚举解析机制 / Enum Resolution

从一句代码说起 / A Small Piece of Code

var m = Material.DIAMOND;

HimDSL 怎么知道 Material.DIAMOND 是"枚举值",而不是"字段 Material 的字段 DIAMOND"?

How does HimDSL know Material.DIAMOND is an enum value, not a "field DIAMOND of variable Material"?

规则是最长匹配:从代码文本里尽可能多地收集连续的 Name.Name.Name 段,然后依次尝试:

The rule is longest match: gather as many consecutive Name.Name.Name segments as possible, then try:

  1. 作为枚举路径(段数 ≥ 2)。

  2. 作为类路径(段数 ≥ 1)。

  3. As an enum path (segments ≥ 2).

  4. As a class path (segments ≥ 1).

成功即停,不再尝试更短的组合。

Stop on success — shorter combinations are not tried.


最长匹配的例子 / Longest Match in Action

Material.DIAMOND

段:["Material", "DIAMOND"]

  • 尝试枚举路径 Material.DIAMOND → 找到 → 返回枚举值。
org.bukkit.Material.DIAMOND

段:["org", "bukkit", "Material", "DIAMOND"]

  • 尝试 org.bukkit.Material.DIAMOND → 找到 → 返回枚举值。
  • 不会再尝试 bukkit.Material.DIAMOND 或 Material.DIAMOND。
Color.RED

段:["Color", "RED"]

  • 尝试 Color.RED。如果 Color 是用户枚举,返回用户枚举值。
  • 如果 Color 是 Java 类,且 RED 是它的枚举常量,返回 Java 枚举值。
  • 都没找到 → 失败。

方法调用不参与匹配 / Method Calls Are Not Enumerated

Material.values()

段扫描会停在 .values 之前——因为 values 后面紧跟着 (,说明它是方法名,不是枚举段。

The scan stops before .values — because values is followed by (, marking it as a method, not an enum segment.

解析过程:

The resolution:

  • 段 = ["Material"](只有一段)。

  • 尝试枚举路径:段数不足 → 跳过。

  • 尝试类路径:Material → 找到 → 返回类对象。

  • 后续 .values() 调用静态方法。

  • Segments = ["Material"] (one only).

  • Enum path: too few → skip.

  • Class path: Material → resolved → return class object.

  • Subsequent .values() calls the static method.

关键规则:后跟 ( 的段不纳入枚举路径。

Key rule: a segment followed by ( is not part of an enum path.


用户枚举与 Java 枚举 / User vs Java Enums

两种枚举共存,解析规则相同:

Both kinds coexist with the same resolution:

enum Color { RED, GREEN, BLUE };

Color.RED                       // 用户枚举
Material.DIAMOND                // Java 枚举

两段式的优先级:先查用户枚举,再查 Java 类。

Two-segment priority: user enums are checked before Java classes.

如果用户定义了 enum Material { DIAMOND },那么 Material.DIAMOND 会解析为用户枚举而不是 Java 的 Material.DIAMOND。这是个罕见的坑——不要给用户枚举取和 Java 类相同的名字。

If a user defines enum Material { DIAMOND }, then Material.DIAMOND resolves to the user enum, not Java's. A rare trap — do not name user enums after Java classes.


与变量的交互 / Interaction with Variables

如果 Material 是脚本里的一个变量,会怎样?

If Material is a script variable, what happens?

Material.DIAMOND            // Material 是变量名时……

解析器先检查 Material 是否在当前作用域内已声明:

The parser first checks whether Material is a declared variable in scope:

  • 是变量 → 当作 变量.字段 处理,不尝试枚举。

  • 不是变量 → 走枚举路径解析。

  • Is a variable → treat as var.field, no enum resolution.

  • Not a variable → proceed with enum resolution.

这就意味着:

This implies:

enum Material { DIAMOND };
var Material = someObject;
var x = Material.DIAMOND;    // 用的是变量,不是枚举

变量的优先级高于枚举。给变量取和类名相同的名字能覆盖类解析,但会让代码难以理解。避免这样做。

Variables take priority over enums. Naming a variable after a class shadows the class, but hurts readability. Avoid.


常见错误 / Common Mistakes

错误一:给枚举起冲突名

enum Material { STONE, DIAMOND };       // ✗ 和 Java 的 Material 冲突

用户枚举不要用 Java 类的名字。取个独一无二的:MyMaterial、GameItem。

错误二:把静态方法当枚举路径

Material.DIAMOND.name()         // ✓ 先解析枚举再调方法
Material.values                 // ✗ 这是个方法,不是字段,会失败

想拿到所有枚举值,写 Material.values()(带括号)。

错误三:变量遮蔽类名

var Diamond = Material.DIAMOND;     // 变量名和枚举常量同名
// 后面用 Diamond 时,拿到的是变量,不是枚举常量

变量名不要和枚举常量名相同,会造成混淆。

Mistake 1: Conflicting enum names

Do not name user enums after Java classes.

Mistake 2: Static methods as enum paths

Material.values (without parentheses) fails; Material.values() works.

Mistake 3: Variables shadowing class names

Avoid naming variables after classes or enum constants.


什么时候需要在意 / When You Need This

日常脚本 99% 情况下不需要。写 Material.DIAMOND 就管用,不用想解析过程。

99% of daily scripts never need this. Material.DIAMOND just works.

遇到下列情况回来查这章:

Come back when:

  • 枚举引用报"找不到"。

  • 一个表达式的结果不符合直觉(拿到了变量而不是枚举)。

  • 自己定义枚举和 Java 类重名。

  • An enum reference reports "not found".

  • An expression behaves unintuitively (variable instead of enum).

  • A user enum shares a name with a Java class.


小结 / Summary

  • 最长匹配:尽可能收集连续 Name.Name 段。

  • 后跟 ( 的段不参与枚举匹配。

  • 优先级:变量 > 用户枚举 > Java 类。

  • 用户枚举不要用 Java 类名。

  • 日常不需要关心;出错时回来查。

  • Longest match: collect as many Name.Name segments as possible.

  • Segments followed by ( are excluded.

  • Priority: variables > user enums > Java classes.

  • Never name user enums after Java classes.

  • Daily scripts ignore this; come back when something misfires.


下一步 / Next Steps

下一章 [作用域与闭包 / Scopes-and-Closures] 讲变量在哪里可见,lambda 怎么捕获它们。

Next, [作用域与闭包 / Scopes-and-Closures] covers where variables are visible and how lambdas capture them.