24
20

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 5 years have passed since last update.

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

Last updated at Posted at 2017-01-23

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にタイトル、内容を一部変更致しました。

24
20
2

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
24
20

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?