Xtend
30 分   クッキング
Xtend
30 分   クッキング
IT'S JUST Java
Xtend Extends Java
愛   は   関   ジャバ にある。
Xtend
30 分   クッキング
Java ソースへ変換
拡張メソッド
ローカル型推論
ステートメントレス(全てが式)
強力な Switch 式
ラムダ
演算子上書き
テンプレート式
プロパティ
Xtend news!
    JavaOne 2012

Xtend at JavaOne
           10/2 10:00
 Sebastian's プレゼンテーション
            on Xtend
Xtend
    30 分     クッキング




愛    は   X    tend にある。
           関ジャバの提供でお送りします。
先生のレシピ
                                                        先生のレシピ

                      @s_kozake 先生                s_kozake せんせい

                       Xtend 研究家・トラブル火消士。
                       某大学卒業後、某大手 SIer 企業での下積み ( 常駐 ) を経て
                       自社のビックプロジェクトに従事するに至る。
                       2 人の娘さんの父親でもあり、過酷な育児の日々の中、
                       「 No ドラクエ No ライフ」を嫁に提案中。

著プレゼン / LT (http://www.slideshare.net/s_kozake)

「 Play! Together 」( Play Framework 勉強会 in 関西)
「 About Jobs 」(やきに駆動 秋の Java 祭典スペシャル in 大阪~こりん星)
「 MyBatis で流れるようなメソッドチェーン」(やきに駆動 秋の Java 祭典スペシャル in 大阪~こりん星)
「システムアーキテクトになる前に覚えておきたい技術とか色々」(鹿駆動勉強会)
「たのしい関数型」(第 1 回 関数型言語勉強会 大阪)

著ブログ (http://ameblo.jp/kozake)

「はじめての変態」(変態アドベントカレンダー 2011 )
「変態アドベントカレンダー 2011 の QR コード作りました」(変態アドベントカレンダー 2011 )
「 cron4j のご紹介」( Java Advent Calendar 2011 )
「 Play Excel !」( Play! framework Advent Calendar 2011 )
「おめでたい話と悩みごと」(変態アドベントカレンダー 2011 )
本日のレシピ『 Xtend 』
What's Xtend?

Java ランタイムに完全な互換性をもつ、
ラムダを備えた Java 互換言語

Xtext という DSL ライブラリおよびエディタ
生成のためのプラグインスイートをベース
Xtend のコンセプト

  Python や Ruby と同等の表現力
  互換性問題のない Java

  Java の構文とコンセプトをできるだけ再利用

 Java アプリケーションを読みやすく表現力高く
Xtend は Java のソースファイルを生成する
              Java バイトコード
              0:   aload_0
              1:   invokespecial
              4:   return
              0:   getstatic
              3:   ldc #3;
              5:   invokevirtual
              8:   return




              Java ソースコード                                        Java バイトコード
              import org.eclipse.xtext.xbase.lib.InputOutput;    0:   aload_0
                                                                 1:   invokespecial
              public class HelloWorld {                          4:   return
                public static void main(final String[] args) {   0:   getstatic
                  InputOutput.<String>println("Hello World");    3:   ldc #3;
                }                                                5:   invokevirtual
              }
                                                                 8:   return




要するに、 Java にとっての「 CoffeeScript 」
Xtend のコンパイル方法
・ Eclipse ビルダ経由
・ Maven プラグイン

コンパイラは Eclipse とは完全に独立
Xtend の機能
拡張メソッド
ローカル型推論
ステートメントレス(全てが式)
強力な Switch 式
ラムダ
演算子上書き
テンプレート式
プロパティ
Why Xtend?
IT'S JUST Java
   Java 技術者の習得コストが少ない
   =技術者確保が容易
   Java の既存資産が活用できる

   静的型付けであり、 Java と同等の性能
Why Xtend?
DSL
  拡張メソッド、演算子上書き、
  テンプレート式など、内部 DSL を作成する
  便利機能がそろっている
下ごしらえ( Installation )
Xtend
                     <1 人分 >
Eclipse Juno ---------------------------1 Download
Xtend-2.3.1 ----------------------------1 Download
Windows7 -----------------------------1 ライセンス
コーヒー ------------------------------5 ~ 10 本

嫁の理解 ------------------------------2 人日
やる気 --------------------------------- 少々
Help → Install New Software
下の URL を入力し、 Xtend-2.3.1 を選択。
http://download.eclipse.org/modeling/tmf/xtext/updates/composite/releases/
Next
Check 「 I accept.. 」 → Finish
Download.... → restart
New → Java Project
Input Project name → Next
Add Library
Select 「 Xtend Library 」→ Next
Finish
試食( Demo )
Demo ソース「 Hello World 」
HelloWorld.xtend
package demo

class HelloWorld {
  def static void main(String... args) {
    println('Hello World!')
  }
}



                                  HelloWorld.java
                                  package demo;

                                  import org.eclipse.xtext.xbase.lib.InputOutput;

                                  @SuppressWarnings("all")
                                  public class HelloWorld {
                                    public static void main(final String... args) {
                                      InputOutput.<String>println("Hello World!");
                                    }
                                  }
Demo ソース「 FizzBuzz 」
FizzBuzz.xtend                                        FizzBuzz2.xtend
package demo                                          package demo

class FizzBuzz {                                      class FizzBuzz2 {

    def static void main(String... args) {                def static void main(String... args) {

    }
      new FizzBuzz().run
                                             More         }
                                                            new FizzBuzz2().run;

                                             Simple
    def void run() {                                      def void run() {
      var i = 1                                             (1..100).forEach [ println(it.fizzbuzz) ]
      while (i <= 100) {                                  }
        println(fizzbuzz(i))
        i = i + 1                                         def fizzbuzz(int i) {
      }                                                     switch i {
    }                                                         case (i % 15 == 0) : 'fizzbuzz'
                                                              case (i % 3 == 0) : 'fizz'
    def String fizzbuzz(int i) {                              case (i % 5 == 0) : 'buzz'
      if (i % 15 == 0) {                                      default : i.toString
        return 'fizzbuzz'                                   }
      } else if (i % 3 == 0) {                            }
        return 'fizz'                                 }
      } else if (i % 5 == 0) {
        return 'buzz'
      } else {
        return String::valueOf(i)
      }
    }
}
Demo テストソース「 FizzBuzz2Test 」
FizzBuzz2Test.xtend
package demo

import static org.junit.Assert.*;
import static org.hamcrest.CoreMatchers.*;

import org.junit.Test

class FizzBuzz2Test {

    extension FizzBuzz2 test = new FizzBuzz2()

    @Test
    def testFizzBuzz() {

        assertThat   (1.fizzbuzz, is('1'))
        assertThat   (3.fizzbuzz, is('fizz'))
        assertThat   (5.fizzbuzz, is('buzz'))
        assertThat   (15.fizzbuzz, is('fizzbuzz'))
    }
}
調理(機能紹介)
Xtend の機能
拡張メソッド
ローカル型推論
ステートメントレス(全てが式)
強力な Switch 式
ラムダ
演算子上書き
テンプレート式
プロパティ
拡張メソッド
既存の型に変更なしに新しいメソッドを追加できます。

Xtend の名前はこの機能からきています。

機能自体は単純な仕組み
拡張メソッドの最初のパラメータを渡す代わりに
そのメンバの 1 つであるかのようにメソッドが呼ばれます。

 15.fizzbuzz // call fizzbuzz(15)
拡張メソッド(利点)
ネストされたメソッド呼び出し(コマンドクエリー IF )でなく
チェインされたメソッド呼び出し(流れるような IF )が可能
可読性が増します。
JsonUtil::writeJson(obj)
                でなく

obj.writeJson

特定のコンテキストやアプリケーション層に固有メソッド
を追加できます。
拡張メソッド(組み込みライブラリ)
デフォルトで既存クラスの拡張メソッドが用意されています。
・   ObjectExtensions
・   IterableExtensions
・   MapExtensions
・   ListExtensions
・   CollectionExtensions
・   BooleanExtensions
・   IntegerExtensions
・   FunctionExtensions


    val obj = newArrayList(1,2,3)
    println(obj.map[it * 2])     // [2, 4, 6]
    println(obj.getClass.getName)   // java.util.ArrayList
拡張メソッド(ローカル Extension インポート)
クラス内または親クラスの可視性のある非 static メソッドは
自動的に拡張メソッドとして使えます。

   def void run() {
     println(15.fizzbuzz)
   }

   def fizzbuzz(int i) {
     switch i {
       case (i % 15 == 0) : 'fizzbuzz'
       case (i % 3 == 0) : 'fizz'
       case (i % 5 == 0) : 'buzz'
       default : i.toString
     }
   }
拡張メソッド( Extension インポート)
ヘルパークラスなどが用意する static メソッドは、
import static の後ろに extension キーワードを配置
することで、全ての static メソッドを拡張メソッドとして
import できます。
                                                   package demo

                                                   import static org.junit.Assert.*;
                                       static      import static org.hamcrest.CoreMatchers.*;
package demo                           Import      import static extension demo.JsonUtil.*;
                                       extension
class JsonUtil {                                   import org.junit.Test
  def static writeJson(Object obj) {
    println(obj.toString)                          class TestJsonUtil {
  }
}                                                      @Test
                                                       def void testWriteJson() {

                                                           val obj = 'test'
                                                           obj.writeJson
                                                       }
                                                   }
拡張メソッド( Extension フィールド)
フィールド定義に extension キーワードを追加すると、
そのインスタンスのメソッドは拡張メソッドとなります。
class FizzBuzz2Test {

  extension FizzBuzz2 test = new FizzBuzz2()

  @Test
  def testFizzBuzz() {

    assertThat (1.fizzbuzz, is('1'))
       :
  }
}


static な extension import と比べて、フィールド定義による
extension の利点は、実装にバインドされないことです。
factory や依存性注入、 setter メソッドにより外部から
実装を簡単に交換できます。
Xtend の機能
拡張メソッド
ローカル型推論
ステートメントレス(全てが式)
強力な Switch 式
ラムダ
演算子上書き
テンプレート式
プロパティ
ローカル型推論
変数の型は右の式から推論されます。
また、メソッドの戻り値もメソッド本体から推測されます。
                          package demo;

                          import java.util.Date;
 package demo
                          @SuppressWarnings("all")
 import java.util.Date
                          public class DemoClass {
                            private final int a = 1;
 class DemoClass {
   val a = 1
                              private String b = "test";
   var b = 'test'
   public var c = 2
                              public int c = 2;
     def hoge() {
                              public String hoge() {
       val d = new Date
                                String _xblockexpression = null;
       'hoge'
                                {
     }
                                  Date _date = new Date();
 }
                                  final Date d = _date;
                                  _xblockexpression = ("hoge");
                                }
                                return _xblockexpression;
                              }
                          }
Xtend の機能
拡張メソッド
ローカル型推論
ステートメントレス(全てが式)
強力な Switch 式
ラムダ
演算子上書き
テンプレート式
プロパティ
ステートメントレス(全てが式)
Xtend はステートメントを持っていません。
全てが式で戻り値を持っています。
全ての式を紹介する時間がないので、いくつかの式を
紹介します。
ステートメントレス( if 式)
if (p) e1 else e2

if は p の結果により e1 か e2 を返す式です。

val a = if (true) 1 else 2
println(a) // 1


else をオプションで省略可能です。

val a = if   (false) 1
val b = if   (false) 'a'
println(a)   // 0
println(b)   // null
ステートメントレス( for 式)
for (T1 variable : arrayOrIterable) expression

for は void を返す式です。
variable の型は arrayOrIterable の型から型推論される
ので省略可能です。

val arr = newArrayList(1,2,3,4,5)
for (i : arr) {
  println(i)
}
ステートメントレス( try-catch 式)
 try-catch も式です。以下のようなコードを書くことで
 今まで必要だったローカル変数を省略できます。

 val s = 'a'
 val n = try {
   Integer::parseInt(s)
 } catch (NumberFormatException ex) {
   -1
 }
 println(n) // -1
ステートメントレス(その他)
ローカル変数 it を使用すると、 it に代入された
インスタンスのフィールドやメソッドがローカルのように
扱えます。

 package demo

 class Hoge {
   @Property
   String fuga
 }

 class DemoClass {

     def static void main(String... args) {
       val it = new Hoge
       fuga = 'test'
       println(fuga) // test
     }
 }
ステートメントレス(その他)
?. によるメソッド呼び出しで、 null 安全な呼び出し
が可能です。

 var hoge = new Hoge   Hoge _hoge = new Hoge();
 hoge?.hello           Hoge hoge = _hoge;
                       if (hoge!=null) hoge.hello();
 hoge = null           hoge = null;
 hoge?.hello           if (hoge!=null) hoge.hello();
ステートメントレス(その他)
Static アクセスは . でなく :: でアクセスします。

 val n = Integer::parseInt('1')
 println(n)
Xtend の機能
拡張メソッド
ローカル型推論
ステートメントレス(全てが式)
強力な Switch 式
ラムダ
演算子上書き
テンプレート式
プロパティ
強力な Switch 式
 Switch 式は Java の switch 文とはかなり異なっています。
  ・下への落下がない
  ・特定の値に限定されず、任意のオブジェクトが使用可能

 val s = 'ho'
 val ret = switch s + 'ge' {
   case s.length > 5 : 'so long'
   case 'hoge' : 'equal hoge'
   default : 'not'
 }
 println(ret) // equal hoge


 まず、 switch の式が評価されます。
 式の型が boolean 型なら、 true で case にマッチします。
 boolean 型以外なら、 Object.equals(Object) が比較に使用されます。
 case にマッチしたら、コロンの後の式が評価され、式全体の結果となります。
強力な Switch 式
 ケースガードに加え、型ガードもサポートしています。

 def length(Object x) {
   switch x {
     String case x.length > 0 : x.length // length is define String
     List<?> : x.size // size is define for List
     default : -1
   }
 }


 switch の値が型と一致しているのみ、ケースと比較します。
 型と一致しない場合、 case の式は評価されません。
 両方が一致した場合、コロンの後の式が評価されます。
 switch の値が変数の場合、自動的にその型にキャストされます。

 Java の instance of やキャストに比べて読みやすく型安全です。
Xtend の機能
拡張メソッド
ローカル型推論
ステートメントレス(全てが式)
強力な Switch 式
ラムダ
演算子上書き
テンプレート式
プロパティ
ラムダ
Xtend は SAM(Single Abstract Method) Type の
インタフェースをラムダ式として表せます。
 public interface Hello {    class DemoClass {
     void hello(String s);
 }                               def static void main(String... args) {
                                   helloWorld([String s | println(s)])
                                 }

                                 def static void helloWorld(Hello o) {
                                   o.hello('hello world!')
                                 }
                             }




上記のように角括弧で囲んだ部分がラムダ式です。
この構文は、 Smalltalk に影響を受けたらしいです。
| の区切りでパラメータ宣言と式が書かれています。
ラムダ


 public interface Hello {    class DemoClass {
     void hello(String s);
 }                               def static void main(String... args) {
                                   helloWorld([String s | println(s)])
                                 }

                                 def static void helloWorld(Hello o) {
                                   o.hello('hello world!')
                                 }
                             }




パラメータの型はインタフェースの型から推測できるので、省略可能です。
ラムダ


 public interface Hello {    class DemoClass {
     void hello(String s);
 }                               def static void main(String... args) {
                                   helloWorld([println(it)])
                                 }

                                 def static void helloWorld(Hello o) {
                                   o.hello('hello world!')
                                 }
                             }




パラメータが 1 つの場合、パラメータ宣言が省略可能です。
省略した場合、パラメータは it という名前の変数にバインドされます。
ラムダ


 public interface Hello {   class DemoClass {
     void hello();
 }                              def static void main(String... args) {
                                  helloWorld([| println('hello world!')])
                                }

                                def static void helloWorld(Hello o) {
                                  o.hello()
                                }
                            }




パラメータないメソッドは上記のように書けます。
| の記述がいることに注意してください。
ラムダ


 public interface Hello {    class DemoClass {
     void hello(String s);
 }                               def static void main(String... args) {
                                   helloWorld(3) [
                                     println(it)
                                   ]
                                 }

                                 def static void helloWorld(int n, Hello o) {
                                   (1..n).forEach [ o.hello('hello world!') ]
                                 }
                             }


メソッド一番右側のパラメータがラムダ式の場合、パラメータリストの後に
渡すことができます。
ラムダ


 public interface Hello {    class DemoClass {
     void hello(String s);
 }                               def static void main(String... args) {
                                   val name = 'duke'
                                   helloWorld [ println(it + ' ' + name) ]
                                 }

                                 def static void helloWorld(Hello o) {
                                   o.hello('hello world!')
                                 }
                             }




Java 同様、ラムダ式の外部の final 変数参照することが可能です。
ラムダ
SAM Type がなくても、下記のように書けます。

 val f = [int n | n**3 ]
 println(f.apply(2)) // 8.0




 final Function1<Integer,Double> _function = new Function1<Integer,Double>() {
   public Double apply(final Integer n) {
     double _power = Math.pow(n, 3);
     return _power;
   }
 };
 final Function1<Integer,Double> f = _function;
 Double _apply = f.apply(Integer.valueOf(2));
 InputOutput.<Double>println(_apply);



戻り値が void の場合、 Procedures 、それ以外は Functions となります。
ラムダ
Xtend は関数型の簡略構文もサポートしています。

 val (int) => double f = [int n | n**3 ]
 println(f.apply(2)) // 8.0




                                  =
 val Function1<Integer,Double> f = [int n | n**3 ]
 println(f.apply(2)) // 8.0
Xtend の機能
拡張メソッド
ローカル型推論
ステートメントレス(全てが式)
強力な Switch 式
ラムダ
演算子上書き
テンプレート式
プロパティ
中置演算子と演算子のオーバーロード
Java とは対照的に、演算子は特定の型の操作に
限定されません。
任意の型の演算子を再定義し、対応するメソッドシグネチャ
を実装することで、
演算子からメソッドへのマッピングが可能です。
例えば、ランタイムライブラリには、 BigDecimal の演算子
を定義するクラス BIgDecimalExtensions が含まれています。

val a = 1BD
val b = 2BD

println(a.getClass.getName) // java.math.BigDecimal
println(b.getClass.getName) // java.math.BigDecimal

println (a + b) // 3
中置演算子と演算子のオーバーロード
演算子のオーバーロードが可能です。
package demo

@Data
class DemoClass {

    String hoge

    def DemoClass operator_add(DemoClass obj) {
      new DemoClass(this.hoge + obj.hoge)
    }

    override toString() {
      hoge
    }

    def static void main(String... args) {
      val a = new DemoClass('hoge')
      val b = new DemoClass('fuga')

        println(a += b) // hogefuga
    }
}
中置演算子と演算子のオーバーロード
使用可能な演算子とそれに対応するメソッドシグネチャの
完全なリストです。
また、下の表は昇順で演算子の優先順位を定義しています。
e1 += e2 e1.operator_add(e2)
e1 || e2 e1.operator_or(e2)
e1 && e2 e1.operator_and(e2)
e1 == e2 e1.operator_equals(e2)               e1 <> e2 e1.operator_diamond(e2)
e1 != e2 e1.operator_notEquals(e2)            e1 ?: e2 e1.operator_elvis(e2)
e1 < e2 e1.operator_lessThan(e2)              e1 <=> e2 e1.operator_spaceship(e2)
e1 > e2 e1.operator_greaterThan(e2)           e1 + e2 e1.operator_plus(e2)
e1 <= e2 e1.operator_lessEqualsThan(e2)       e1 – e2 e1.operator_minus(e2)
e1 >= e2 e1.operator_greaterEqualsThan(e2)    e1 * e2 e1.operator_multiply(e2)
e1 -> e2 e1.operator_mappedTo(e2)             e1 / e2 e1.operator_divide(e2)
e1 .. e2 e1.operator_upTo(e2)                 e1 % e2 e1.operator_modulo(e2)
e1 => e2 e1.operator_doubleArrow(e2)          e1 ** e2 e1.operator_power(e2)
e1 << e2 e1.operator_doubleLessThan(e2)       ! e1 e1.operator_not()
e1 >> e2 e1.operator_doubleGreaterThan(e2)    - e1 e1.operator_minus()
e1 <<< e2 e1.operator_tripleLessThan(e2)
e1 >>> e2 e1.operator_tripleGreaterThan(e2)
Xtend の機能
拡張メソッド
ローカル型推論
ステートメントレス(全てが式)
強力な Switch 式
ラムダ
演算子上書き
テンプレート式
プロパティ
ごめんなさい
手がまわりませんでした (><)
Xtend の機能
拡張メソッド
ローカル型推論
ステートメントレス(全てが式)
強力な Switch 式
ラムダ
演算子上書き
テンプレート式
プロパティ
プロパティ
フィールド変数を @Property アノーテーション定義することで
setter / getter を自動生成してくれます。
                    @SuppressWarnings("all")
                    public class DemoClass {
                      private String _hoge;
class DemoClass {
                        public String getHoge() {
    @Property             return this._hoge;
    String hoge         }
}
                        public void setHoge(final String hoge) {
                          this._hoge = hoge;
                        }
                    }


フィールドが指定されたコンテキストから
アクセスできない場合、 setter が使用されます。
これが @Property アノーテーションがローカルフィールドを
_myProperty にリネームする理由です。
プロパティ
また、クラス定義に @Data アノーテーション定義することで
値オブジェクトとしてクラスを定義できます。
                     import org.eclipse.xtend.lib.Data;
                     import org.eclipse.xtext.xbase.lib.util.ToStringHelper;

                     @Data
                     @SuppressWarnings("all")
                     public class DemoClass {
                       private final String _hoge;
 @Data
                         public String getHoge() {
 class DemoClass {         return this._hoge;
                         }
     String hoge         public DemoClass(final String hoge) {
 }                         super();
                           this._hoge = hoge;
                         }

                         @Override
                         public int hashCode() {
                             :
                         @Override
                         public boolean equals(final Object obj) {
                             :
                         @Override
                         public String toString() {
                             :
                     }
まとめ
Xtend Extends Java
モダンな言語機能を備えている。
  ・ラムダ
  ・型推論
  ・強力な Switch 式
  ・ etc..
既存機能を拡張できる

  ・拡張メソッド
IT'S JUST Java
既存のリソースを活用できる
 ・豊富な OSS
 ・ IDE サポート
 ・様々な経験知
 ・ etc..

なによりも・・・


     Java である
30 分 Xtend クッキング




     Thank you a lot

     愛     は       Java エンジニア にある。

                   関ジャバの提供でお送りしました。

Xtend30分クッキング