Eclipse.org 製の新言語? Xtend 文法(その1)

Xtend Reference Documentation をかいつまんで。

ファイル形式

Xtend ファイルの構成は Java と同じく package、import、class の順番に並ぶ。行末のセミコロンは不要。

package com.acme
import java.util.List
class MyClass {
  String ^Lrst(List<String> elements) {
    return elements.get(0)
  }
}

インポート

スタティックインポートはワイルドカードでのみ指定可能。java.lang は暗黙的にインポートされる。

import static java.util.Collections.*

extension を付けてスタティックインポートすると、既存のクラスにあたかもインポートした関数が元から定義されていたように書くことができる。

import static extension java.util.Collections.*

のように static extension すると、以下のように書ける。

"foo".singletonList

以下のJavaコードと同義となる。

Collections.singletonList("foo");

わー、スゴい。


クラス

class 宣言はデフォルトでpublic宣言となる。Java のデフォルトである package private は存在しない。
ジェネリクスJavaと同じように使え、extends や impliments も java と同じ。

class ArrayList<E> extends AbstractList<E>
    implements List<E>, RandomAccess, Cloneable, java.io.Serializable {
    ...
}

フィールド

Xtend は DIコンテナを利用することを考慮して、

@Inject MyService myService

は、以下の Java コードに展開される。

@Inject
private MyService myService;

フィールドのデフォルトのアクセスレベルは private で、public、protected、private も Javaと同じ。


リテラルとかキャストとか演算子とか

リテラルJava とほぼ同じように使える。
文字列リテラルはシングルクオートで囲むこともできたり、

'Hello World !'
"Hello World !"

リテラルは以下のように書いたりする。

typeof(java.lang.String)


キャストは as キーワードを使う。

my.foo as MyType
(1 + 3  5  (&#1048576; 23))as BigInteger


演算子は大体Javaと同じで、** にてpower(階乗)となる。


代入

以下のような xtend コードがあると、

myObj.myProperty = "foo"

コンパイラは最初に myObj にアクセス可能な myProperty があるかを探し、あればその通りに代入する。(xtend では全てが式なので、戻り値は代入する値となる)

myObj にアクセス可能な myProperty が無ければ、setter を探して、以下の Java コードが生成されることになる。

myObj.setMyProperty("foo")

この場合の戻り値は null になる。


コール

プロパティへのアクセスは以下のように行い、

myObj.myProperty

フィールドがあればその値を返却するし、なければ、getter アクセスとなる。
統一アクセスっぽく、


以下のコードは、シンタックスシュガーとして

if ( myRef != null ) myRef.doStu()

以下のように書くことができる。

myRef?.doStu()

拡張メソッド

インポート句に extension キーワードを付けられたのと同じく、フィールドに extension を指定できる。
例えば以下のクラスがある場合、

class PersonExtensions {
  def getFullName(Person p) {
    p.forename + " "+ p.name
  }
}

拡張メソッド(Extension Methods)として宣言することで、

@Inject extension PersonExtensions

myPerson.getFullName()

既存クラスをあたかも拡張するメソッドのように書くことができる。
さらに短く、以下のように書くこともできる。

myPerson.fullName

関数

デフォルトでpublic。public、protected、private も Javaと同じ。
return 不要で、以下のような形式となる。

def boolean equalsIgnoreCase(String s1, String s2) :
  s1.toLowerCase() == s2.toLowerCase();

オーバーライドする場合は、override キーワードが必要。final 宣言された関数はオーバーライド不可。

override boolean equalsIgnoreCase(String s1,String s2) :
  s1.toLowerCase() == s2.toLowerCase();

そして、チェック例外を強制しないのは今風の流れで、「throws IOException」は不要。

def void throwIOException()  {
  throw new IOException()
}


型推論により戻り値の型を省略可能で、先の例は以下のように書ける。

def equalsIgnoreCase(String s1,String s2) :
  s1.toLowerCase() == s2.toLowerCase();

ジェネリクスについても Java と同様。


ディスパッチ関数

関数に渡す引数の型により、ポリモーフィックに関数を動的選択可能。これには dispatch キーワードが必要で、

def dispatch foo(Number x) { "it's a number" }
def dispatch foo(Integer x) { "it's an int" }

のようにすると、以下の Java コードに展開される。

public String foo(Number x) {
  if (x instanceof Integer) {
    return foo((Integer)x);
  } else if (x instanceof Number) {
    return foo((Number)x);
  } else {
    throw new IllegalArgumentException("Couldn't handle argument x:"+x);
  }
}
protected String foo(Integer x) {
  return "It's an int";
}
protected String foo(Number x) {
  return "It's a number";
}

型階層に基づき、より抽象的な型から if else で判定するコードとなる。


アノテーション

アノテーションJava と同じように使える。

@TypeAnnotation(typeof(String))
class MyClass f
  @FieldAnnotation(children = f@MyAnno(true), @MyAnnot(false)}
  String myField

  @MethodAnnotation(children = f@MyAnno(true), @MyAnnotg
  def String myMethod(@ParameterAnnotation String param) {
    //...
  }
}

クロージャ

クロージャは、引数 | 操作 の形で書く。

myList.find([e|e.name==null])

上記のように引数として渡す場合は [] は省略可能。

myList.find(e|e.name==null)


クロージャの引数の型は型推論されるので、以下は、

arrayList( "Foo", "Bar" ).findFirst( String e | e == "Bar" )

以下のように書ける。

arrayList( "Foo", "Bar" ).findFirst( e | e == "Bar" )


以下のような書き方ができる。

[ | "foo"] //パラメータなし
[ String s | s.toUpperCase()] //引数の型を明示
[ a,b,a | a+b+c ] //型推論

続きは次回。。