saba1024のブログ

どうでも良い思いついた事とかプログラミング関係のメモとか書いていきます。

Groovyの不思議なメソッド「call」

概要

通常のクラスであれば、インスタンスをnewで生成して、メソッドを呼び出します。

class Hoge {
    def test(String message) {
        "${message} in Hoge#test"
    }
}
def hoge = new Hoge()
assert hoge.test("test") == "test in Hoge#test"

Groovyのクロージャにも同様のものが有りますが、インスタンスメソッドにもcallという特殊な名前のものを定義することが出来ます。
このcallというメソッドを定義すると、メソッド名を省略してcallメソッドを実行できます。

使い方

実際にサンプルを見てみます。先ほどのHogeクラスにcallメソッドを追加したのが以下です。

class Hoge {
    def test(String message) {
        "${message} in Hoge#test"
    }
    
    // これを追加
    def call(String message) {
        "${message} in Hoge#call"
    }
}
def hoge = new Hoge()
assert hoge.test("test") == "test in Hoge#test"

// 当然普通に呼べる
assert hoge.call("test") == "test in Hoge#call"

// しかし!callメソッドはメソッド名を省略できる!
assert hoge("test") == "test in Hoge#call"

このようにcallメソッドを定義することで、インスタンスが格納されている変数自身がまるでメソッドのように振る舞うことが出来ます。
普段のコーディングで使いドコロがあるか、と言われると微妙ですが、DSLを自作する際などには威力を発揮するのではないでしょうか?

以下、自分の確認用に作成したMapで渡された値を元に簡単なHTMLを生成するサンプルです。

class MyHTMLBuilder {
    def call(Map htmlParts) {
        """
        <html>
        <head>${htmlParts.head.collect{k,v -> "<${k}>${v}</${k}>"}.join("")}</head>
        <body>${htmlParts.body.collect{k,v -> "<${k}>${v}</${k}>"}.join("")}</body>
        </html>
        """
    }
}

def html = new MyHTMLBuilder()
def map = ['head': ['title': 'hogehoge'], 'body':['h1': 'これはTitle','p':'Groovy良いよね!']]
println html(map)

実行結果は以下のようになります。(見やすいようにインデントしています)

<html>
    <head>
        <title>hogehoge</title>
    </head>
    <body>
        <h1>これはTitle</h1>
        <p>Groovy良いよね!</p>
    </body>
</html>