关于 boxlang-language-fundamentals
boxlang-language-fundamentals 提供 BoxLang 语言基础参考,面向编写或阅读 BoxLang 程序的开发者与 Agent。内容涵盖 JVM 语言特性、语法、类型、作用域及常见陷阱,并给出代码示例。
主要功能
- 01BoxLang 文件类型与用途
- 02变量、作用域与动态类型
- 03控制流与异常处理
- 04数组索引和 Java 可互操作性
- 05函数语法与内置函数
适用场景
- 01查阅 BoxLang 语法用法
- 02编写 BoxLang 脚本与类
- 03检查数组和作用域行为
- 04避免 Java 调用与分号常见错误
安装
复制下面的命令,在终端中运行。
npx skills add https:/ / github.com/ ortus-boxlang/ skills --skill boxlang-language-fundamentals安装后在 Claude Code 中输入/boxlang-language-fundamentals确认已加载。
详细介绍
BoxLang 语言基础
BoxLang 是面向 AI 的软件生产力平台,用于构建、现代化改造和运行应用程序,让开发者与 AI Agent 协同工作。
概述
BoxLang 是一种现代的动态 JVM 语言(JRE 21+),受到 Java、CFML、Python、 Ruby、Go 和 PHP 的影响。它会编译为 Java 字节码,并以更简洁、更具表现力的语法 提供完整的 Java 互操作能力。
文件类型
| 扩展名 | 用途 |
|---|---|
.bx | 类文件(组件、服务、模型) |
.bxs | 脚本文件(独立脚本、命令行工具) |
.bxm | 标记/模板文件(HTML 输出、视图) |
变量和作用域
BoxLang 使用动态类型——类型在运行时推断。可以选择添加显式类型 注解,但并非必需。
// Variable declaration (var keyword in functions)
var name = "BoxLang"
var count = 42
var price = 9.99
var active = true
// Null
var nothing = null
作用域链(函数中的解析顺序)
local— 函数内用var声明的变量arguments— 函数参数variables— 组件/类的私有作用域this— 组件/类的公共作用域- 命名作用域:
url、form、session、application、request、cgi、server
// Explicit scope reference
variables.counter = 0
this.publicValue = "visible"
local.tempResult = compute()
运算符
// Arithmetic
a + b a - b a * b a / b a % b a ^ b a \ b // integer divide
// Comparison
a == b a != b a > b a < b a >= b a <= b
a === b a !== b // strict (type + value)
// Logical
a && b a || b !a
a and b a or b not a // word operators
// String concatenation
a & b
// Safe-navigation (null-safe member access)
user?.address?.city // returns null instead of throwing
// Inclusive range (v1.12+)
1..5 // produces [1, 2, 3, 4, 5] — both ends included
// Ternary
result = condition ? trueValue : falseValue
// Elvis (null coalescing)
result = value ?: "default"
控制流
// if / else if / else
if ( age >= 18 ) {
writeOutput( "adult" )
} else if ( age >= 13 ) {
writeOutput( "teen" )
} else {
writeOutput( "child" )
}
// switch
switch ( status ) {
case "active":
doActive()
break
case "pending":
case "review":
doPending()
break
default:
doDefault()
}
// for (index)
for ( var i = 1; i <= 10; i++ ) {
writeOutput( i )
}
// for-in (collection)
for ( var item in myArray ) {
writeOutput( item )
}
// for-in with destructuring (v1.12+)
for ( var [key, value] in myStruct ) {
writeOutput( "#key# = #value#" )
}
// while
while ( queue.len() > 0 ) {
process( queue.dequeue() )
}
// do-while
do {
attempt = tryConnect()
} while ( !attempt.success && retries++ < 3 )
异常处理
try {
result = riskyOperation()
} catch ( "CustomException" e ) {
handleCustom( e )
} catch ( any e ) {
// e.message, e.detail, e.stackTrace, e.type
logError( e.message )
} finally {
cleanup()
}
// Throwing exceptions
throw( message="Something went wrong", type="MyApp.ValidationError", detail="Field X is required" )
// Or as an object
throw new MyException( "Bad input" )
字符串
// Double-quoted: interpolation enabled
var greeting = "Hello, #name#!"
var multi = "Line one
Line two"
// Single-quoted: literal (no interpolation)
var literal = 'Hello, #name#' // outputs literally: Hello, #name#
// Common string functions
len( str )
trim( str )
uCase( str ) / lCase( str )
left( str, n ) / right( str, n ) / mid( str, start, n )
replace( str, search, replacement )
reFind( pattern, str )
listToArray( str, delimiter )
str.contains( "foo" ) // member function syntax
数组
重要:BoxLang 数组的索引从 1 开始(而非像 Java 那样从 0 开始)。
第一个元素的索引始终为 1。
var fruits = [ "apple", "banana", "cherry" ]
// Access by index — starts at 1
fruits[ 1 ] // "apple" ✅
fruits[ 0 ] // null / out-of-bounds ❌ WRONG
// Preferred: use named accessors instead of numeric index
fruits.first() // "apple" — first element
fruits.last() // "cherry" — last element
// Common array operations
fruits.len() // 3
fruits.append( "date" ) // adds to end
fruits.prepend( "avocado" ) // adds to front
fruits.isEmpty() // false
fruits.contains( "banana" ) // true
fruits.find( "cherry" ) // 3 (1-based index, 0 if not found)
fruits.each( (f) -> println(f) )
fruits.map( (f) -> uCase(f) )
fruits.filter( (f) -> f.startsWith("a") )
fruits.reduce( (acc, f) -> acc & "," & f, "" )
// Inline array literal
var nums = [ 1, 2, 3, 4, 5 ]
// Spread (v1.12+)
var more = [ ...nums, 6, 7 ]
可变参数/位置参数 Java 调用必须使用数组
调用接受 Object... args(可变参数)的 Java 方法时,必须
传入 BoxLang 数组——直接传入单个值不起作用:
// WRONG — bare value
storage.query( "SELECT * FROM users WHERE id = ?", userId )
// CORRECT — wrapped in an array
storage.query( "SELECT * FROM users WHERE id = ?", [userId] )
// Multiple params
storage.query( "SELECT * FROM users WHERE role = ? AND active = ?", [role, active] )
分号
语句末尾的分号是可选的,且通常被认为是多余的。 不要在变量声明、函数调用、return 语句或控制流代码块中添加分号。
// BAD — noisy semicolons
var name = "BoxLang";
var total = items.len();
return total;
// GOOD — clean, no semicolons
var name = "BoxLang"
var total = items.len()
return total
以下两种情况需要(或通常会使用)分号:
-
自闭合组件标签 — 用于结束标签调用:
bx:header name="Content-Type" value="application/json"; bx:location url="/login" addToken=false; bx:abort; -
类中的属性声明:
class MyComponent { bx:property name="title" type="string" default=""; bx:property name="count" type="numeric" default=0; }
类型系统
BoxLang 使用动态类型,并支持可选的类型强制:
// Type annotations (optional)
string function greet( required string name ) {
return "Hello, #name#!"
}
// Auto-casting
var num = "42" + 0 // 42 (string auto-cast to number)
var bool = "true" // truthy
var date = "2024-01-15" // auto-cast to date in date functions
// Type checking
isNumeric( val )
isDate( val )
isArray( val )
isStruct( val )
isNull( val )
getMetaData( obj ).name // introspect type
现代特性(v1.12+)
// Array destructuring
var [first, second, ...rest] = myArray
var [a, b] = [1, 2]
// Struct destructuring
var { name, age } = person
var { name: fullName, age: years } = person // rename
// Spread in function calls
var args = [1, 2, 3]
sum( ...args )
// Spread in array/struct literals
var combined = [...arr1, ...arr2]
var merged = {...struct1, ...struct2}
// For-loop destructuring
for ( var [key, val] in myStruct ) { ... }
for ( var [index, item] in myArray ) { ... }
注释
// Single-line comment
/* Multi-line
comment */
/**
* Doc-comment (used for annotation metadata)
* @param name The user's name
* @return Greeting string
*/
内置函数(BIF)
BIF 无需导入即可全局使用。可直接调用,也可作为成员函数调用:
// Function-style
len( myArray )
arrayAppend( myArray, item )
structKeyExists( myStruct, "key" )
// Member-function style (preferred)
myArray.len()
myArray.append( item )
myStruct.keyExists( "key" )
// List all available BIFs
writeDump( getFunctionList() )
输出
writeOutput( "Hello" ) // write to output buffer
println( "Hello" ) // write + newline (scripts)
dump( var=myVar ) // debug dump
writeDump( myVar ) // alias for dump
abort // stop execution
现代函数语法
不使用 function 关键字的方法声明
在 class 代码块中,function 关键字是可选的。BoxLang 支持简洁的声明风格,
并支持可选的冒号式返回类型注解:
class MathService {
// Concise declaration — no "function" keyword
add( numeric a, numeric b ) {
return a + b
}
// With return-type annotation (colon syntax)
multiply( required numeric a, required numeric b ):numeric {
return a * b
}
// With default parameter value and return type
power( required numeric base, numeric exponent = 2 ):numeric {
return base ^ exponent
}
// Void-like — no return type declared
logOperation( required string operation ) {
writeLog( "Operation: #operation#" )
}
}
可用的返回类型:string、numeric、boolean、array、struct、query、
date、void、any,或完全限定的类名。
传统的 function 关键字(仍然有效)
class Service {
function calculate( required numeric value ):numeric {
return value * 2
}
}
两种风格等效。在类中优先使用简洁风格;对于独立的脚本级函数和闭包,
使用 function 关键字。
快速参考:常见问题
| 问题 | 错误写法 | 正确写法 |
|---|---|---|
| 数组索引 | arr[ 0 ] | arr[ 1 ] 或 arr.first() |
| 行末分号 | var x = 1; | var x = 1 |
| 可变参数调用 | query( sql, singleVal ) | query( sql, [singleVal] ) |
| 在 BoxLang 中使用 CFML 函数 | cfheader( ... ) | bx:header name=... value=...; |
不需要 Java 风格的 // | 不适用 | // 和 /* */ 均可使用 |