No Programming, No Life

プログラミング関連の話題や雑記

JavaからGroovyへ移行する簡単なステップ

この記事は From Java to Groovy in a few easy steps | Groovy Zone の和訳です。思いっきり意訳です、あしからず。

GroovyとJavaは、いとこみたいに近い存在で、構文がとても似ているからJava開発者にとってGroovyを覚えることはとても簡単です。Javaプログラムのほとんどはそのまま、有効なGroovyプログラムなんです!でも、Groovyを学ぶようになるとそのうちにGroovyに用意されている気の利くデフォルト値やGStringなどショートカット記法を使うようになっていくでしょう。

この記事では最初にJavaプログラムを取り上げ、それをGroovyに置き換えてゆくプロセスをご紹介します。まずは少々馬鹿げているかもしれませんが、複雑なHello Worldプログラムから始めることにしましょう。後の方の記事ではより高度な例をご紹介します。これは、会議の席で見たGroovyとJavaの構文がとても近いことを紹介するスライドの何枚かからインスピレーションを得ました。アンドレスさんとポールさんのアイデアに感謝します。

まずはこんな感じのJavaプログラムを用意しましょう。

public class HelloWorld {
    private String name;

    public void setName(String name) {
        this.name = name;
    }

    public String getName() {
        return name;
    }

    public String greet() {
        return "Hello " + name;
    }
    
    public static void main(String[] args) {
        HelloWorld helloWorld = new HelloWorld();
        helloWorld.setName("Groovy");
        System.out.println( helloWorld.greet() );
    }
}

さて、このハローワールドクラスは、ゲッター/セッターとそれに紐付くprivateフィールド、それからgreet()メソッドがありますね。greet()メソッドは悪名高いハローワールド文字列を返します。それに親しみや憎しみを感じたものですね*1。そして、main()メソッドがクラスをインスタンス化して、挨拶を出力します。

さてGroovy版を見てみましょう。

public class HelloWorld {
    private String name;

    public void setName(String name) {
        this.name = name;
    }

    public String getName() {
        return name;
    }

    public String greet() {
        return "Hello " + name;
    }
    
    public static void main(String[] args) {
        HelloWorld helloWorld = new HelloWorld();
        helloWorld.setName("Groovy");
        System.out.println( helloWorld.greet() );
    }
}

そうです、まったく同じプログラムです!馬鹿にしてるわけじゃなくて、Java版とGroovy版はまったく同じだってことです。でもこのプログラムはまだGroovyプログラムとはおせじにも呼べない代物です。もっと読みやすくて簡潔に記述できますよ。それじゃあ、単純なGroovy化*2ステップを適用していってみましょう。まず最初のステップはセミコロンの削除とpublicキーワードの削除です。Groovyではクラスやメソッドはデフォルトでpubilcなので明示的に指定する必要がないんです。ということで、こんな感じになります。

class HelloWorld {
    private String name

    void setName(String name) {
        this.name = name
    }

    String getName() {
        return name
    }

    String greet() {
        return "Hello " + name
    }
    
    static void main(String[] args) {
        HelloWorld helloWorld = new HelloWorld()
        helloWorld.setName("Groovy")
        System.out.println( helloWorld.greet() )
    }
}

あとは何ができるでしょうか。ここでGroovyの便利な文字列、GStringを使ってみましょう。なにその機能、おいしいの?GStringって何なの?他の言語では "補間文字列" とかって呼ばれているやつです。Javaで言うところの通常の文字列のようにダブルクォーテーションで囲んだ文字列では、プレースホルダーが使えます。${someVariable} みたいな感じで。文字列が出力される際に置換されます。なので、手動で文字列結合とかする必要がないんです。じゃあ、さっきのgreet()メソッドはどうなるかって?

String greet() {
    return "Hello ${name}"
}

さらにもう一つ。returnキーワードは省略できます、省略した場合最後に評価された値が返却値となります。これで、greet()メソッドはさらにほんのちょっと短くなりますね。ここまでの内容をまとめてみましょう。プログラムは以下のような感じになりますね。

class HelloWorld {
    private String name

    void setName(String name) {
        this.name = name
    }

    String getName() { name }

    String greet() { "Hello ${name}" }
    
    static void main(String[] args) {
        HelloWorld helloWorld = new HelloWorld()
        helloWorld.setName("Groovy")
        System.out.println( helloWorld.greet() )
    }
}

getName()メソドとgreet()メソッドはワンライナーですっきりさせました。さて、次はどうしましょうか。Groovyはプロパティをサポートしています。*3だから、ゲッター/セッターとそれに紐付くprivateフィールドはプロパティ化できます。Groovyでは特に可視性を指定せずに単にフィールド宣言するだけでOKです。nameプロパティは単純にStringです。privateフィールドと、それに紐付くゲッター/セッターはGroovyが自動で用意してくれます。setName()呼び出しは、helloWorld.name = "Groovy" のように使えるようになります。getName()は、単にhelloWorld.name となります。これは既存のJavaクラスについても同様で、たとえば、HogeクラスがgetName()とかsetName()を持っているなら、hoge.name で呼び出せます。*4さて、どうなったかな?

class HelloWorld {
    String name

    String greet() { "Hello ${name}" }
    
    static void main(String[] args) {
        HelloWorld helloWorld = new HelloWorld()
        helloWorld.name = "Groovy"
        System.out.println( helloWorld.greet() )
    }
}

Groovyにはよく使うメソッドのお手軽なショートカットが用意されています。System.out.println() は println() だけで使えます。GroovyはJDKで提供されているクラスにもユーティリティメソッドを追加していたりします。その名もGDKです。トップレベルステートメントではメソッド呼び出しの括弧を省略することができます。

class HelloWorld {
    String name

    String greet() { "Hello ${name}" }
    
    static void main(String[] args) {
        HelloWorld helloWorld = new HelloWorld()
        helloWorld.name = "Groovy"
        println helloWorld.greet()
    }
}

そういえばここまで、全てのメソッドや変数宣言の時に、型指定していましたね。Groovyは動的型付けもサポートしていますので、もしお望みなら、全部型を取っ払ってしまうこともできます。

class HelloWorld {
    def name

    def greet() { "Hello ${name}" }
    
    static main(args) {
        def helloWorld = new HelloWorld()
        helloWorld.name = "Groovy"
        println helloWorld.greet()
    }
}

Stringをdefキーワードに変換しました。main()メソッドのvoidキーワードも要りません。引数のString配列も無くしてしまいましょう。

Groovyはオブジェクト指向言語(数値なども含めて全てがオブジェクト)であり、Javaと同じプログラミングモデルをサポートしています。さらにGroovyはスクリプト言語でもあるので、Javaのようにクラス定義を強制されない自由な書き方が可能です。このチュートリアルの最後のステップは、完全にメインメソッドを取り除くことです。

class HelloWorld {
    def name
    def greet() { "Hello ${name}" }
}    

def helloWorld = new HelloWorld()
helloWorld.name = "Groovy"
println helloWorld.greet()

スクリプトは本当に手軽にちょっとした文の集まりでプログラミングしていくことができます。あなたもHelloWorldクラスをGroovy化したみたいに、いろんなクラスをGroovy化することができるんです。

これで退屈なJavaプログラムはとてもグルービーなものとなります。ここで学んだ単純なGroovy化のステップを適用していくことで、Groovy言語で用意されている数多くの便利な機能の活用方法の一端に触れることができました。プログラムは、はるかに簡潔で読みやすく、Javaと同等のことが実現できました。読みやすさはコードメンテナンス時にとても重要になります。特に自分で書いたコードじゃない場合などは、実際にそのコードを書くよりもそのコードを読んで理解するのに多くの時間を費やす必要があります。

Enjoy!

*1:なぜ親しみや憎しみを感じたのか、なぜハローワールド文字列が悪名高いのか、私には理解できませんでした…あちらの方のフィーリングが何かあるんだろうなぁ。

*2:元記事では、Groovyfy:グルービファイと表現されていました。カッコイイ言い方なので流行らせたい!

*3:Java7だか8だかで正式サポートされる可能性があります。

*4:hogeはHogeクラスのインスタンスと仮定。