本格派エンジニアの工具箱

第24回 クロージャや拡張メソッドでJava開発をサポートする「Eclipse Xtend」

この記事を読むのに必要な時間:およそ 3.5 分

まずはお約束のHelloWorld

前回は,Eclipseプロジェクトによって開発された新プログラミング言語Eclipse Xtendの概要と,Eclipseへのインストール方法を解説しました。今回は,Xtendの特徴的な機能のうちのいくつかを紹介します。

まずはその前に,お約束のHelloWorldプログラムを作ってみましょう。Xtendを使う場合には,通常のJavaアプリケーションと同じようにプロジェクトを作成し,ライブラリとして関連するjarファイルを追加します。jarファイルはEclipseのプラグインフォルダ(EclipseがC:\eclipseにインストールされている場合には,C:\eclipse\plugins)にあります。基本的な機能であれば「org.eclipse.xtext.xtend2.lib_xxxx.jar」「org.eclipse.xtext.xbase.lib_xxxx.jar」⁠xxxxはバージョン番号)があればOKです。

※注:Eclipse Xtend 2,2がリリースされました。コンストラクタやstaticメソッドは2.2以降でしかサポートされないので,2.1以前をインストールしている場合は必ずアップデートしておきましょう。

プログラムのソースファイルも,Javaと同様にsrcフォルダにパッケージを作成して配置します。新規ファイル作成ウィザードで[Xtend]-[Xtend Class]を選択すれば,クラスファイルの雛形を作成することができます。ファイルの拡張子は.xtendです。

図1 新規ファイル作成ウィザードでXtend用クラスの雛形が作成できる

図1 新規ファイル作成ウィザードでXtend用クラスの雛形が作成できる

HelloWorld.xtendの例を以下に示します。

HelloWorld.xtend

package jp.gihyo.toolbox.xtend

class HelloWorld {
        def String sayHello(String name) {
                var hello =  "Hello "+name+"!"
                hello
        }

        def static void main(String[] args) {
                val HelloWorld hello = new HelloWorld()
                println(hello.sayHello("Gihyo"))
        }
}

Javaとの大きな違いは,行末にセミコロンが付かない点です。メソッドの定義には「def」を,変数宣言には「var」または「val」を使用します。valの場合は変更負荷の変数になります。変数の型宣言は省略することができますが,これは型推論によってコンパイル時に自動で型付けされるだけで,Xtendはあくまでも静的型付けの言語です。メソッドの戻り値は最後に評価した式の値になるので,⁠return」キーワードは省略することができます。

main()メソッドはJavaと同様にstatic修飾子を付けて作成します。実行方法もJavaプログラムと同様で,ファイル名を右クリックして[実行]-[Javaアプリケーション]を選択すれば実行できます。

このプログラムはJavaコードにコンパイルされ,xtend-genフォルダ以下に「HelloWorld.java」が作成されます。これは通常のJavaファイルとまったく変わらないものなので,Javaコードから呼び出すこともできます。

クロージャ

Xtendではクロージャを使うことができます。クロージャを作成するための文法は『[ 引数リスト | 処理内容 ]』の形で定義し,apply()メソッドによって実行します。次のコードは,文字列を引数として受け取り,その内容を表示するクロージャの例です。apply()に渡された文字列(この場合は「Hello!」⁠が出力されます。

val func = [String s | println(s)]
func.apply("Hello!")           // "Hello!"を出力

クロージャを引数にとるメソッドや,戻り値としてクロージャを返すメソッドを作ることもできます。クロージャの型は『(引数の型のリスト) => 文字列の型』のように記述します。次のmyFunc1()メソッドは,第2引数としてクロージャを受け取るメソッドの例です。第1引数に渡されたリストの各要素に対して,それぞれクロージャの処理を実行します。

def myFunc1(ArrayList<String> names, (String) => String function){
    val result = newArrayList()
    for (n : names) {
        result += function.apply(n)
    }
    return result
}

myFunc1()を実行するコードは次のようになります。namesに対するメソッド呼び出しのように見えますが,実際にはnamesがmyFunc1()の第1引数として扱われます。これは後述する拡張メソッドの機能によるものです。1つ目の呼び出しではtoUpperCase(),2つ目の呼び出しではtoLowerCase()を実行するクロージャを渡しているので,実行すればそれぞれ「[TARO, JIRO]」⁠⁠[taro, jiro]」が出力されます。

var names = new ArrayList<String>() 
names.add("Taro")
names.add("Jiro")
println(names.myFunc1(n | n.toUpperCase()))    // "[TARO, JIRO]"を出力
println(names.myFunc1(n | n.toLowerCase()))    // "[taro, jiro]"を出力

次の例は戻り値としてクロージャを返すメソッドmyFunc2()を定義したものです。引数を文字列にとり,⁠"Hello " + name + "!"』を実行するクロージャが返されます。

def (String) => String myFunc2(){
    return [String name | "Hello " + name + "!"]
}

myFunc2()から返されたクロージャは,通常通りapply()メソッドを呼び出すことで実行できます。

println(myFunc2().apply("Gihyo"))    // "Hello Gihyo!"を出力

拡張メソッド

Xtendには拡張メソッドと呼ばれる機能が用意されています。これは,独自に定義したメソッドをあたかも既存のクラスのメソッドであるかのように呼び出すことができる仕組みです。次のに示す例は,hello()をStringの拡張メソッドとして定義したものです。

def hello(String name) {
    println("Hello " + name + "!")
}

このhello()メソッドは,次のようにあたかも第1引数の型であるStringクラスのメソッドであるかのように呼び出すことができます。

"Gihyo".hello()     // "Hello Gihyo!"を出力

もちろん,これは実際にStringにhelloメソッドが追加されたわけではなく,内部では『this.hello("Gihyo")』の呼び出しとして扱われます。前述のクロージャの例でも,この拡張メソッドの仕組みを利用して,第1引数の型を呼び出し元のレシーバであるかのように使っています。

著者プロフィール

杉山貴章(すぎやまたかあき)

ONGS Inc.所属のプログラマ兼テクニカルライター。雑誌,書籍,Webメディアで多数の著作をもつ。

著書

コメント

コメントの記入