Repository navigation
Functional Interfaces
有些情况下,你要把一个 lambda 传给 Java 代码,但 Java 不知道 .apply(...) 是什么。例如下面这段:
Some cases require passing a lambda to Java code, but Java does not know what .apply(...) means. For example:
var r = new Runnable(() -> runcmd("CONSOLE", "say hi"));
r.run();
这里的 Runnable 是 Java 自带的一个接口,它只定义了一个方法 run()。HimDSL 会把 lambda 适配成 Runnable 实例:调用 r.run() 时,实际执行 lambda 的 body。
Runnable is a Java interface with a single method run(). HimDSL adapts the lambda into a Runnable instance: calling r.run() executes the lambda body.
这样的接口叫函数式接口:只有一个抽象方法。Java 标准库里有很多,例如 Runnable、Consumer、Function、Supplier、Predicate。把 lambda 传进去,就能让 Java 代码在合适的时机调用你的逻辑。
Such an interface is called a functional interface: exactly one abstract method. Java's standard library has many — Runnable, Consumer, Function, Supplier, Predicate. Passing a lambda in lets Java call your logic at the right moment.
把一个 lambda 转成函数式接口,用 new:
Convert a lambda into a functional interface with new:
var task = new Runnable(() -> {
runcmd("CONSOLE", "say 一秒钟后执行");
});
task.run();
new Runnable(lambda) 会生成一个实现 Runnable 的对象,其 run 方法就是 lambda 的 body。之后凡是需要 Runnable 的地方,都可以用 task。
new Runnable(lambda) produces an object implementing Runnable, whose run method is the lambda body. Wherever a Runnable is needed, task fits.
注意这里用 var 声明变量。函数式接口通常带泛型参数,泛型语法不属于 HimDSL,所以必须用 var 让编译器自己推断。写 Runnable task = ... 是可以的(Runnable 没有泛型),但写 Consumer c = ... 就要小心——请统一用 var。
Note the use of var. Functional interfaces usually carry generic parameters, and generic syntax is not part of HimDSL, so var is mandatory. Runnable task = ... works (Runnable has no generics), but Consumer c = ... is trickier — stick to var throughout.
某些 Bukkit API 接收 Runnable 参数。例如调度一次性延迟任务:
Some Bukkit APIs take a Runnable. For example, scheduling a one-shot delayed task:
int main() {
var later = new Runnable(() -> {
runcmd("CONSOLE", "say 3 秒后执行");
});
// 伪代码:假设有调度函数接收 Runnable
// scheduleLater(later, 60);
}
HimDSL 本身提供了更简便的调度方式(@BukkitRunnable),所以日常脚本里不常直接手写 Runnable。但当你需要调用某个只收 Runnable 的 Java API 时,就需要用到这层适配。
HimDSL itself offers a simpler scheduling mechanism (@BukkitRunnable), so everyday scripts rarely hand-write Runnable. But when you call a Java API that only accepts a Runnable, this adaptation comes in.
Consumer 接收一个参数、不返回值。Function 接收一个参数、返回一个值。它们的泛型参数在 HimDSL 里不写出来,交给 var 推断:
Consumer takes one argument and returns nothing. Function takes one argument and returns a value. Their generic parameters are not written in HimDSL; var handles inference:
var callback = new Consumer((player p) -> {
runcmd("CONSOLE", "say 玩家: " + p.name);
});
var square = new Function((int x) -> x * x);
用 var 声明,是因为 HimDSL 语法里没有 <T>、<T, R> 这样的泛型写法。想把它们显式写出来是不支持的:
Use var because HimDSL has no <T> / <T, R> syntax. Writing them explicitly is not supported:
Consumer<Player> cb = ... // ✗ 语法错误
Function<Integer, Integer> f = ... // ✗ 语法错误
所以规则很简单:凡是接口名后面带尖括号的,就省略尖括号,改用 var。
So the rule is simple: whenever the interface name would carry angle brackets, drop them and use var.
Predicate 接收一个参数、返回布尔值,常用于"过滤":
Predicate takes one argument and returns a boolean, common for "filtering":
var isHighHp = new Predicate((player p) -> {
return p.health > 15;
});
HimDSL 本身没有集合过滤 API,但如果某个插件提供接收 Predicate 的方法,就能传进去。
HimDSL has no built-in collection filtering, but if a plugin exposes a method taking a Predicate, you can pass one.
把 lambda 适配到接口时,HimDSL 按照以下规则:
When adapting a lambda to an interface, HimDSL follows these rules:
-
接口必须是函数式接口(只有一个抽象方法)。
-
lambda 的形参个数必须等于那个抽象方法的参数个数。
-
参数类型不强制匹配。运行时由适配层负责转换。
-
The interface must be a functional interface (one abstract method).
-
The lambda's parameter count must equal the method's parameter count.
-
Parameter types are not strictly checked; the adaptation layer converts at runtime.
如果接口有多个抽象方法,HimDSL 会报错,因为无法确定 lambda 对应哪个方法。
If the interface has more than one abstract method, HimDSL errors — it cannot tell which one the lambda implements.
var bad = new SomeMultiMethodInterface((int x) -> x); // ✗ 报错
标准库里几乎所有函数式接口都是单抽象方法,所以一般不会遇到这个问题。自定义接口时,需要注意只留一个抽象方法。
Nearly every standard functional interface has a single abstract method, so this rarely happens. Custom interfaces must leave exactly one abstract method.
函数式接口主要出现在与 Java 生态交互时:调用第三方插件提供的方法、注册监听器、使用 Java 标准库 API。
Functional interfaces mainly appear when interacting with Java ecosystems: calling third-party plugin methods, registering listeners, using standard library APIs.
日常脚本里你用 @EventHandler 和 @BukkitRunnable 就能满足绝大多数需求——它们已经内置了框架支持,不用手写 Runnable。
Everyday scripts satisfy nearly all needs with @EventHandler and @BukkitRunnable — the framework already handles those, no hand-written Runnable needed.
只有当你遇到"这个方法要求我传一个 Runnable,但 HimDSL 没提供对应的便捷语法"时,才需要本章的内容。
You only need this chapter when you hit "this method needs a Runnable and HimDSL has no shorthand for it".
如果 HimDSL 已经提供更直接的写法,优先用直接写法:
When HimDSL already offers a direct form, prefer it:
-
定时任务:用
@BukkitRunnable,不用new Runnable。 -
事件监听:用
@EventHandler,不用适配Listener。 -
GUI 点击:直接
slot.onLeftClick = lambda,不用手动new Consumer。 -
Timers: use
@BukkitRunnable, notnew Runnable. -
Event listeners: use
@EventHandler, not a hand-adaptedListener. -
GUI clicks: assign
slot.onLeftClick = lambda, no need tonew Consumer.
函数式接口是兜底方案,用于那些 HimDSL 没有内置支持的场合。
Functional interfaces are a fallback, for cases HimDSL does not cover natively.
-
函数式接口 = 只有一个抽象方法的接口。
-
用
new InterfaceName(lambda)适配。 -
泛型参数不写,一律用
var声明。 -
匹配规则:单抽象方法 + 参数个数一致。
-
类型不强制匹配,运行时转换。
-
只在调用外部 Java API 时用到。
-
HimDSL 内置支持的场景(定时、事件、GUI)优先用内置写法。
-
Functional interface = an interface with one abstract method.
-
Adapt with
new InterfaceName(lambda). -
Generic parameters are omitted; declare with
var. -
Matching: single abstract method + same parameter count.
-
Types are loose; converted at runtime.
-
Only needed when calling external Java APIs.
-
For timers, events, GUI, use HimDSL's built-in forms first.
函数部分到此结束。四章覆盖了:具名函数与返回值、重载与解析规则、lambda 与闭包、函数式接口适配。后面的内容会大量使用这四章的知识,但不会再引入新的函数语法。
The Functions section ends here. Four chapters covered: named functions and return values, overloading and resolution, lambdas and closures, and functional-interface adaptation. Later chapters use all four but introduce no new function syntax.
下一部分进入数据结构。第十章 [数组 / Arrays] 从最基础的数组讲起。
The next part covers data structures. Chapter [数组 / Arrays] starts with the most basic: arrays.
- 首页 / Home
- 安装与快速开始 / Installation & Quick Start
- 第一个脚本 / Your First Script
- 命令速查 / Command Reference
- 自动执行脚本 / Auto-run Scripts
- 定义函数 / Defining Functions
- 函数重载 / Function Overloading
- Lambda 表达式 / Lambda Expressions
- 函数式接口 / Functional Interfaces
- 选择器总览 / Selectors Overview
- 选择器过滤器 / Selector Filters
- EntityRef 引用 / EntityRef
- 占位符参考 / Placeholders Reference
- PlaceholderAPI 桥接 / PAPI Bridge
- 创建 GUI / Creating a GUI
- GUISlot 槽位 / GUISlot
- 点击回调 / Click Callbacks
- onClose 回调 / onClose Callback
- GUI 刷新 / GUI Refresh
- 数学函数 / Math Built-ins
- 高精度计算 / BigDecimal Built-ins
- 字符串函数 / String Built-ins
- 集合函数 / Collection Built-ins
- 文件 I/O / File I/O
- 命令与 sleep / Commands & sleep