Skip to content

反射与导入类

AchieveMaster 当前 AST 脚本没有删除反射能力。 它只是把旧解释器时代分散的“调类、调方法、取字段、创建对象”入口,统一收口到了 AST + ReflectionHelper 这一层。

这意味着:

  • 仍然可以调用 Java 标准库、Bukkit API、其他插件 API
  • 仍然可以导入类、构造对象、访问静态字段、走方法链
  • 但正式写法已经统一成 AST 语法,不建议继续扩写旧反射命令式语法

当前反射链路

当前实现主要由这几层组成:

组件作用
ImportClassNode处理 导入类 alias = full.class.Name
VariableNode变量解析失败后,继续尝试把名字当导入类或可加载类
NewInstanceNode处理 new Xxx(...) 构造调用
MethodChainNode处理 obj.a.b().c、静态访问、实例访问、字段访问
ReflectionHelper负责类加载、方法匹配、参数转换、缓存、访问权限放开

所以现在文档里的“反射”不是额外外挂能力,而是 AST 表达式系统的一部分。

正式支持什么

导入类

正式写法:

text
导入类 Bukkit = org.bukkit.Bukkit
导入类 ArrayList = java.util.ArrayList
导入类 HashMap = java.util.HashMap

导入执行后会发生两件事:

  1. 当前脚本上下文里写入一个别名变量,值是 Class<?>
  2. ReflectionHelper 运行时缓存里也会记住这组映射

但文档仍然建议每个脚本显式写自己的 导入类,不要依赖别的脚本之前导入过同名别名。

直接使用完整类名

不导入也可以直接写全限定类名:

text
赋值 max = java.lang.Integer.MAX_VALUE
赋值 abs = java.lang.Math.abs(-9)
赋值 list = new java.util.ArrayList()

构造对象

当前正式关键字只有 new

text
导入类 ArrayList = java.util.ArrayList
赋值 list = new ArrayList()
赋值 data = new java.util.HashMap()

当前 AST 词法和表达式解析器没有 新建 关键字入口,所以不要把 新建 Xxx() 当正式语法。

实例方法、静态方法、字段与属性

下面这些都支持:

text
导入类 Bukkit = org.bukkit.Bukkit
导入类 IntegerClass = java.lang.Integer
导入类 ArrayList = java.util.ArrayList

赋值 online = Bukkit.getOnlinePlayers().size()
赋值 max = IntegerClass.MAX_VALUE

赋值 list = new ArrayList()
list.add("a")
list.add("b")
赋值 first = list.get(0)
赋值 upper = list.get(1).toUpperCase()
赋值 size = list.size()

属性访问规则如下:

  • 先尝试 getXxx()
  • 再尝试 isXxx()
  • 都没有再尝试同名字段

也就是说:

  • itemMeta.displayName
  • player.world.name
  • someObject.enabled

这类写法本质上都走的是 getter / 字段访问反射链。

方法链

只要中间对象不是 null,就可以继续往后连:

text
赋值 worldName = player.getWorld().getName()
赋值 onlineCount = Bukkit.getOnlinePlayers().size()

如果链中某一步结果是 null,而你后面还继续访问,AST 会直接抛脚本异常,不会静默吞掉。

类型转换

当前表达式还支持 Java 风格的强转写法:

text
赋值 text = (String) item.name
赋值 amount = (int) papi.vault_eco_balance
赋值 mapObj = (java.util.Map) someValue

支持的基础类型包括:

  • int / Integer
  • long / Long
  • float / Float
  • double / Double / Number
  • boolean / Boolean
  • String

对象类型会尝试按可加载类做 isInstance 校验。

类解析顺序

不同入口的细节略有差异,但整体上当前运行时大致按下面的顺序解析类或链式起点:

  1. 当前脚本上下文里的变量
  2. 已导入类别名
  3. 完整类名
  4. 常见包下的短类名补全
  5. 指定插件类加载器
  6. 遍历所有已加载插件的类加载器

其中“常见包短类名补全”只覆盖这些前缀:

  • java.lang.
  • java.util.
  • org.bukkit.
  • org.bukkit.entity.
  • org.bukkit.inventory.
  • org.bukkit.event.
  • org.bukkit.potion.
  • org.bukkit.plugin.

这意味着:

  • ArrayListHashMapBukkit 这类名字有机会直接被解析
  • 第三方插件 API 一般仍建议写完整类名,或者先 导入类

单例兜底规则

当你把“类本身”当调用目标,而你访问的成员又不是静态成员时,当前 AST 会尝试自动找实例。

自动尝试顺序是:

  1. INSTANCE
  2. instance
  3. getInstance()
  4. inst()

示例:

text
导入类 SomeApi = com.example.SomePluginApi
赋值 result = SomeApi.getValue()

如果 getValue() 不是静态方法,但 SomeApi 符合上面的单例模式之一,方法链仍然可能成功。

这也是当前新 AST 保留旧脚本反射兼容能力的关键点之一。

参数匹配与自动转换

当前构造函数匹配、方法匹配都不是简单靠参数个数硬对。 运行时会做一层兼容打分:

  • 精确类型匹配优先级最高
  • 父子类型可赋值也能匹配
  • 数字之间允许自动转换
  • 字符串可尝试转数字或布尔
  • 支持可变参数 varargs
  • 会扫描 publicdeclared 方法 / 构造器
  • 会尝试 setAccessible(true)

所以这类调用通常都能直接落:

text
赋值 max = java.lang.Math.max(1, 2.5)
赋值 list = new java.util.ArrayList(16)

但仍然建议你别过度依赖“模糊匹配”,尤其是重载很多的第三方 API。

类对象与特殊属性

当前方法链里还有两个容易被忽略的入口:

写法含义
obj.class返回对象的 Class<?>
SomeClass.class返回类对象本身
array.length数组长度特判

示例:

text
赋值 typeName = player.class.getName()

旧语法兼容

旧反射语法目前仍有一层兼容归一,但只是为了迁移,不是正式推荐写法。

旧写法归一后
调用 MathUtil.abs(-4)MathUtil.abs(-4)
取静态字段 java.lang.Integer.MAX_VALUEjava.lang.Integer.MAX_VALUE
创建对象 List 引入包 java.util.ArrayList导入类 List = java.util.ArrayList

所以老脚本很多还能继续跑,但新脚本请直接写 AST 正式语法。

常见例子

调 Java / Bukkit API

text
导入类 Bukkit = org.bukkit.Bukkit
赋值 count = Bukkit.getOnlinePlayers().size()
判断 (count >= 50) {
  发送消息("&6服务器当前在线人数很多")
}

创建 Map 交给集成功能

text
导入类 HashMap = java.util.HashMap

赋值 data = new HashMap()
data.put("player", player.name)
data.put("progress", progress)

调第三方插件单例 API

text
导入类 SomeApi = com.example.SomePluginApi
赋值 result = SomeApi.getInstance().doSomething(player.name)

使用边界

  • 当前正式构造关键字只有 new,没有 新建
  • 反射不是空安全调用,中间结果为 null 继续链下去会报错。
  • 第三方插件类名尽量写完整路径或先导入别名,不要赌“短类名自动解析”。
  • 类引用访问非静态成员时,只有符合单例兜底规则才会成功。
  • 运行时虽然会尝试放开访问权限,但这不代表所有服务端环境、所有模块封装都一定允许。

使用建议

  • 导入类 就先导入类,不要整段脚本重复写完整类名。
  • 能走内置函数或集成函数时,优先走内置函数或集成函数。
  • 反射适合补洞、接第三方 API、处理特殊对象,不适合把整套业务都写成 Java API 拼装。
  • 需要构造 MapList、自定义对象时,优先参考 语法总览 里的表达式规则。

TQ Minecraft Server Plugin Docs