ひょんなことからXtendを触ってみたら思っていたより面白そうだったので時間を見つけて触ってみようと思っています。参考資料はXtendのチュートリアルプロジェクトのソースとWebサイトにあるReference Documentationです(Reference Documentationはところどころ間違ってるところがあるけど…)。
Xtendは基本的にはJavaをベースに用途と機能を絞りつつ関数型言語の便利な機能を取り込んだ、みたいな言語になっています。なので機能やコードの見た目はScalaに近い部分もあります。というわけで、JavaやScalaと比べて異なる部分を中心に見ていこうと思います。
まずは基本的なところから。
package com.acme import java.util.List class MyClass { def String first(List<String> elements) { return elements.get(0) } }
これだけ見ると行末にセミコロンがないのとメソッドの定義にdefを使ってること以外はJavaと区別つかないですが(セミコロンは書いてもOK)、いくつか違いがあります。まず、Xtendにはpackage privateというスコープがなく、クラスはすべてJavaのトップレベルクラスになります。なので、privateやprotectedなクラスを定義することはできません(インナークラスを定義することもできません)。extendsやimplementsはJavaと同じように機能します。
なお、Reference Documentationには「abstractやfinalもJavaにそのままマッピングされます」と書いてあるけど書くとコンパイルエラーになってしまうし、エディタでもabstractやfinalはキーワードとしてハイライトされないので対応していないものと思われます。staticなクラスを定義することもできません。
import文も基本的にはJavaと同じですが、以下のようにextensionというキーワードを付けてstaticインポートすることができます。これについては拡張メソッドという機能とあわせて触れようと思うので、ひとまず置いておきます。
import static extension java.util.Collections.*
変数はScalaと同じくvalやvarを使って宣言します。valだと再代入が不可能(Javaソースではfinal)になります。変数の型はJavaと同じく変数名の前に書きます。確か、Scalaは型を省略したときの違和感が少ないように後置記法にしてるらしいですが、XtendはJavaに近い記法のほうがよい、ということでしょうか。
また、メソッドの戻り値は最後に評価した式の値になります。なのでreturnは省略できます。ついでにメソッドの戻り値の型も省略できます。以下の例ではメソッドの戻り値の型を省略していますが、戻り値の型はちゃんとStringになります。
def String first(List<String> elements) { val first = elements.get(0) first }
クラスのフィールドはこんな感じ。
class MyClass { @Inject MyService myService String name = "Naoki" // コンパイルエラー ... }
DIに使うためのもの、と割り切っているみたい。デフォルトはprivateですが、public、protectedも指定可能。フィールドに対してはpublic、protected、private以外のキーワードは指定できないそうな。ちなみに初期化時に値を代入することすらできません。
メソッドは前述の通りdefキーワードを使って定義します。デフォルトではpublicになります。private、protectedも指定可能ですが、フィールド同様アクセス修飾子以外のキーワードを指定することはできません。つまりstaticメソッドを定義することはできないということです。また、メソッドをオーバーライドする場合はdefキーワードの代わりにoverrideキーワードを使用します。
class MyClass2 extends MyClass { override String first(List<String> elements){ if(elements.empty) null else elements.get(0) } }
メソッドの引数はval扱いになります(なのでメソッドの引数に対する再代入は不可)。また、例外はJavaと同じようにthrowキーワードでスローできますが、Xtendでは検査例外でもメソッドのシグネチャにthrowsを記述する必要はありません。というか、そもそもthrowsというキーワードそのものがないのでメソッドのシグネチャにスローされる例外を記述することができません。
長くなってしまったのでとりあえず今日はここまで。続きます。