boxlang-java-integration について
BoxLangコードからJavaのクラス、ライブラリ、フレームワークを直接使う方法をまとめたスキルです。インポート、型変換、JARの読み込み、例外処理、JavaのJSR-223スクリプトAPIによる実行を扱います。
主な機能
- 01Javaクラスの生成とインポート
- 02静的メソッドとフィールドの呼び出し
- 03BoxLang・Java間の自動型変換
- 04アプリやモジュール設定でのJAR読み込み
- 05JSR-223エンジンとしてのBoxLang利用
活用シーン
- 01BoxLangアプリでJavaライブラリを再利用
- 02BoxLangクロージャをJavaインターフェースに渡す
- 03BoxLangでJava例外を処理
- 04JavaアプリにBoxLangスクリプトを組み込む
インストール
コマンドをコピーしてターミナルで実行します。
npx skills add https:/ / github.com/ ortus-boxlang/ skills --skill boxlang-java-integrationインストール後、Claude Code で/boxlang-java-integrationを入力して読み込まれたか確認します。
詳細
BoxLang Java 統合
BoxLang は、開発者と AI エージェントが連携してアプリケーションを構築、モダナイズ、実行するための、AI ネイティブなソフトウェア生産性プラットフォームです。
概要
BoxLang は Java バイトコードにコンパイルされ、JRE 21 以降で実行されます。Java との相互運用性は 100%で、あらゆる Java ライブラリ、クラス、フレームワークを BoxLang コードから直接利用できます。ほとんどの場合、BoxLang と Java の型変換は自動的に行われます。
Java オブジェクトの作成
createObject() よりも new java:fully.qualified.ClassName() を優先してください。どちらも使用できますが、new java: 構文のほうが簡潔で、BoxLang らしい書き方です。
// PREFERRED — new java: syntax
var list = new java:java.util.ArrayList()
var map = new java:java.util.HashMap()
var sb = new java:java.lang.StringBuilder()
// Also valid — createObject (verbose, legacy style)
var list = createObject( "java", "java.util.ArrayList" ).init()
var map = createObject( "java", "java.util.HashMap" ).init()
// With constructor args
var list = new java:java.util.ArrayList( 100 ) // initialCapacity
var sb = new java:java.lang.StringBuilder( "Hello" )
ヒント: ファイル内でクラスを頻繁に使う場合は、上部で
import java:を使用すると、短い名前で参照できます(以下を参照)。
静的メソッドとフィールド
ベストプラクティス:クラスをインポートしてから、静的メンバーを直接呼び出します。
呼び出すたびに createObject() を繰り返すのは避けてください。毎回新しいラッパーが作成されます。
// PREFERRED — import once, call freely
import java:java.util.UUID
import java:java.lang.Math
import java:java.lang.System
var id = UUID.randomUUID().toString()
var pi = Math.PI
var sqrt2 = Math.sqrt( 2 )
var abs = Math.abs( -42 )
var sysProps = System.getProperties()
// AVOID — verbose, allocates a wrapper on every call
var id = createObject( "java", "java.util.UUID" ).randomUUID()
var pi = createObject( "java", "java.lang.Math" ).PI
Java オブジェクトでは名前付き引数はサポートされていません
重要: BoxLang では、名前付き引数を使って Java メソッドを呼び出すことはできません。 Java メソッドを呼び出すときは、必ず位置引数を使用してください。
// WRONG — will throw "Methods on Java objects cannot be called with named arguments"
emitter.send( data="hello", event="msg" )
// CORRECT — positional only
emitter.send( "hello", "msg" )
Java クラスのインポート
クラス、テンプレート、またはスクリプトの先頭でインポートすると、短い名前でクラスを直接参照できます。これは、静的呼び出しやインスタンス化を繰り返す場合に推奨される方法です。
// Standard library classes
import java:java.util.ArrayList
import java:java.util.HashMap
import java:java.util.concurrent.ConcurrentHashMap
import java:java.time.LocalDateTime
import java:java.time.format.DateTimeFormatter
// After import, use the short class name directly
var list = new ArrayList()
var map = new ConcurrentHashMap()
var now = LocalDateTime.now() // static method call
var fmt = DateTimeFormatter.ofPattern( "yyyy-MM-dd HH:mm:ss" )
var str = now.format( fmt )
モジュールスコープのクラス(@moduleAlias)
クラスがシステムクラスローダーではなく BoxLang モジュールのクラスローダー内にある場合は、正しいローダーを経由するように @moduleAlias を付けます。
// Import a class from the "bxinsights" module classloader
import java:ortus.boxlang.insights.InsightsStorage@bxinsights
import java:ortus.boxlang.insights.InsightsContext@bxinsights
// Now use the short name directly — no createObject needed
var storage = InsightsStorage.getInstance()
var events = InsightsContext.drainSince( lastId )
@moduleAliasがない場合、ランタイムはシステムクラスローダーを検索するため、モジュール内でのみ利用可能なクラスを見つけられずに失敗します。
従来の方法(引き続き使用できますが、繰り返し使う場合は避けてください)
// Verbose — ok for a single one-off call
var props = createObject( "java", "java.lang.System" ).getProperties()
Java オブジェクトの操作
// ArrayList
var list = new java.util.ArrayList()
list.add( "apple" )
list.add( "banana" )
list.add( "cherry" )
list.size() // 3
list.get( 0 ) // "apple"
list.contains( "banana" ) // true
list.remove( 0 )
list.toArray() // returns Java Object[]
// HashMap
var map = new java.util.HashMap()
map.put( "name", "Ada Lovelace" )
map.put( "email", "[email protected]" )
map.get( "name" ) // "Ada Lovelace"
map.containsKey( "age" ) // false
map.keySet() // Java Set of keys
// StringBuilder
var sb = new java.lang.StringBuilder()
sb.append( "Hello" )
sb.append( ", " )
sb.append( "World!" )
sb.toString() // "Hello, World!"
自動型変換
BoxLang は、BoxLang の型と Java の型を自動的に変換します。
| BoxLang の型 | Java の型 |
|---|---|
| String | java.lang.String |
| 数値(整数) | int、long、java.lang.Integer |
| 数値(浮動小数点) | double、java.lang.Double |
| Boolean | boolean、java.lang.Boolean |
| Array | java.util.List または Object[] |
| Struct | java.util.Map |
| Date | java.util.Date、java.time.LocalDateTime |
| Closure/Lambda | Java の関数型インターフェース(自動的にプロキシ化) |
// BoxLang array → Java List
var bxArray = [1, 2, 3]
var javaList = createObject( "java", "java.util.ArrayList" ).init( bxArray )
// Java List → BoxLang array (automatic when assigned)
var backToArray = javaList.toArray()
クロージャを Java の関数型インターフェースとして渡す
BoxLang のクロージャは、Java の関数型インターフェースに自動的にプロキシ化されます。
import java.util.Arrays
import java.util.Comparator
// Sort a Java list using a BoxLang lambda as Comparator
var names = new java.util.ArrayList()
names.add( "Charlie" )
names.add( "Alice" )
names.add( "Bob" )
names.sort( (a, b) -> a.compareTo( b ) ) // BoxLang lambda → Comparator
names // ["Alice", "Bob", "Charlie"]
// Runnable
var thread = new java.lang.Thread( () -> {
println( "Running in Java thread!" )
})
thread.start()
thread.join()
// Java Stream API with BoxLang lambdas
import java.util.stream.Collectors
var nums = new java.util.ArrayList()
[1,2,3,4,5,6].each( (n) -> nums.add(n) )
var evenNums = nums.stream()
.filter( (n) -> n % 2 == 0 )
.map( (n) -> n * n )
.collect( Collectors.toList() )
Java 例外の処理
try {
var conn = DriverManager.getConnection( url, user, pass )
} catch ( "java.sql.SQLException" e ) {
writeOutput( "SQL error: #e.message#" )
} catch ( "java.lang.ClassNotFoundException" e ) {
writeOutput( "Driver not found: #e.message#" )
} catch ( any e ) {
writeOutput( "Java error: #e.type# — #e.message#" )
}
Java ライブラリ(JAR)の追加
Application.bx を使用する場合
class {
this.javaSettings = {
loadPaths : [ "lib/", "lib/vendor.jar" ],
loadColdFusionClassPath : false,
reloadOnChange : false
}
}
モジュールの libs/ フォルダーを使用する場合
JAR をモジュールの libs/ ディレクトリに配置すると、そのモジュール内でのみ使用できます。
my-module/
├── ModuleConfig.bx
├── libs/
│ ├── my-library-1.2.3.jar
│ └── dependency.jar
Maven を使用する場合(Gradle ベースのモジュール)
// build.gradle
dependencies {
implementation "com.example:my-library:1.2.3"
}
Java Time API
import java.time.LocalDate
import java.time.LocalDateTime
import java.time.ZonedDateTime
import java.time.ZoneId
import java.time.format.DateTimeFormatter
import java.time.temporal.ChronoUnit
var now = LocalDateTime.now()
var today = LocalDate.now()
var utcNow = ZonedDateTime.now( ZoneId.of( "UTC" ) )
// Format
var fmt = DateTimeFormatter.ofPattern( "yyyy-MM-dd HH:mm:ss" )
var str = now.format( fmt )
// Parse
var parsed = LocalDate.parse( "2024-01-15" )
// Arithmetic
var nextWeek = now.plus( 7, ChronoUnit.DAYS )
var daysBetween = ChronoUnit.DAYS.between( today, LocalDate.of(2025,1,1) )
JSR-223:Java スクリプティングエンジンとしての BoxLang
Java アプリケーション内から BoxLang を使用します。
// Java code
import javax.script.ScriptEngineManager;
import javax.script.ScriptEngine;
ScriptEngineManager manager = new ScriptEngineManager();
ScriptEngine engine = manager.getEngineByName("BoxLang");
engine.put("greeting", "Hello from Java!");
engine.eval("println( greeting )");
Object result = engine.eval("return 1 + 2");
System.out.println(result); // 3
リフレクションとイントロスペクション
// Get Java class from a BoxLang object
var obj = new java.util.ArrayList()
var clazz = obj.getClass()
var name = clazz.getName() // "java.util.ArrayList"
// List methods
var methods = clazz.getMethods()
for ( var method in methods ) {
writeOutput( method.getName() & "<br>" )
}
// Check instanceof
if ( isInstanceOf( obj, "java.util.List" ) ) {
// ...
}
BoxLang でよく使われる Java ライブラリ
// Apache Commons Lang
import org.apache.commons.lang3.StringUtils
StringUtils.isBlank( " " ) // true
StringUtils.capitalize( "hello" ) // "Hello"
// Google Guava
import com.google.common.collect.ImmutableList
var list = ImmutableList.of( "a", "b", "c" )
// Jackson JSON (if bundled)
import com.fasterxml.jackson.databind.ObjectMapper
var mapper = new ObjectMapper()
var json = mapper.writeValueAsString( myStruct )
var parsed = mapper.readValue( json, createObject("java","java.util.Map").getClass() )