Help us understand the problem. What is going on with this article?

【Kotlin】 TypeAliasで関数リテラルに名前を付ける

More than 3 years have passed since last update.

Problem

SAM変換

Kotlinにはinterfaceに1つのみ関数が定義されている場合にのみLambda式として表せるSAM変換というものがあります。

例えばAndroidでボタンをクリックした際に呼ばれるOnClickListenerをJavaで書くとこうなります。

Java
button.setOnClickListener(new View.OnClickListener() {
    @Override
    public void onClick(View v) {
       // click 
    }
});

OnClickListenerはViewクラスのinterfaceにonClickというメソッドが1つのみ定義されているので

Java
/**
 * Interface definition for a callback to be invoked when a view is clicked.
 */
public interface OnClickListener {
    /**
     * Called when a view has been clicked.
     *
     * @param v The view that was clicked.
     */
     void onClick(View v);
}

Kotlinでは簡潔にこれだけで書きます。

Kotlin
button.setOnClickListener { 
    // click
}

しかしながら、SAM変換はJavaで定義されたinterfaceのみ対応していてKotlinで書かれているinterfaceは対応していません。
Kotlinは関数リテラルを型に持てるのでプロパティにコールバックを定義してリスナーの形をとることが可能なのですが、型に名前がつかないのでライブラリの様な不特定多数の人が使うものだと、やはり型名を付けたくなります。
プロパティに関数リテラルを定義してコールバックとして用いたやり方を以前このような記事を書いたので、参考にしてみて下さい。
Kotlinでコールバック

Solution

Type Alias

Kotlin1.1からTypeAliasという機能が導入されました。
C++やSwift等の他の言語にも似た機能があるので知っている人も多いと思います。

簡単に説明すると、例えばHogeというIntとStringを引数にとってStringを返すTypeAliasを定義して、
変数hogeの型としてHogeを指定することが出来ます。

Kotlin
typealias Hoge = (Int, String) -> String

val hoge: Hoge? = { i: Int, str: String -> "hoge : $str : $i" }

Callback (interface)

今まではJavaの様にinterfaceを使って書くとこのようになります。

Kotlin
interface CallBackListener {
    fun onHoge(foo: String, bar: Int)
}

// caller
var callback: CallBackListener? = null
callback?.onHoge("foo", 100)

// callee
val callback = object : CallBackListener {
    override fun onHoge(foo: String, bar: Int) {
        print("$foo : $bar")
    }
}

これだと、Javaで書いているのとあまり変わりません…

Callback (関数リテラル)

関数リテラルにして書いてみます。

Kotlin
// caller
var callback: ((foo: String, bar: Int) -> String)? = null
callback?.invoke("foo", 100)

// callee
val callback = { foo: String, bar: Int ->
    print("$str : $i")
}

この形でも、Kotlinっぽく書けなくはありません。
ただ上記でも述べたように、型では無いので引数の型さえ同じなら任意の値を入れることが出来ます。

Callback (TypeAlias)

上2つをTypeAliasで書き直すとこのようになります。
イメージとしてはinterfaceと関数リテラルの良いとこ取りをした感じです。

Kotlin
typealias CallBackListener = (foo: String, bar: Int) -> Unit

// caller
var callback: CallBackListener? = null
callback?.invoke("foo", 100)

// callee
val callback = { foo, bar -> 
    print("$foo : $bar")
}

TypeAliasを使うとコールバックに型名を付ける事が出来るので、型安全かつKotlinっぽく書くことが出来ました。

2017年1月現在はまだKotlin1.1はbeta版ですが、近いうちに正式リリースされる予定なので是非使ってみて下さい。

※ 指摘がありましたので、2017/1/24にタイトル、内容を一部変更致しました。

Why not register and get more from Qiita?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
Comments
No comments
Sign up for free and join this conversation.
If you already have a Qiita account
Why do not you register as a user and use Qiita more conveniently?
You need to log in to use this function. Qiita can be used more conveniently after logging in.
You seem to be reading articles frequently this month. Qiita can be used more conveniently after logging in.
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
ユーザーは見つかりませんでした