明日からちょっと
KotlinでAndroidが書きたくなる(かもしれない?)
SAM変換と拡張関数
 
室星亮太
2014/7/4(金))第2回)かわいいKotlin勉強会)#jkug
突然ですが質問です!
Kotlin書いたことある方?
「Kotlin'書いたことない」
ってイベントページに書いたけれど
本当は書いたことある方?
Androidアプリ
開発したことある方?
ありがとうございました!
お前だれよ
• 名前":"室星亮太
• 仕事":"Androidアプリ開発(Java)、Unityゲーム開発(C#)
• Twi6er":"@RyotaMurohoshi
• 投稿先":"h6p://qiita.com/RyotaMurohoshi
• 興味":"Kotlin,"Groovy,"C#,"Unity,"SonyのWearable"Device
AndroidアプリをJavaで開発していて、
「なんでこんなに冗長なコードが必要なんだ!」
ってイラっとすることはありませんか?
私はあります!
C#を業務で使い始めたりしたり、
Groovyをほんのちょっと勉強したら、
「なんでJava、〇〇できないのー!」
って、イラッとなりました
そこでKotlinですね!
明日からちょっと
KotlinでAndroidが書きたくなる(かもしれない?)
SAM変換と拡張関数
と題して今日はLTします!
いらっとする冗長なコード1
button.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
Log.v(TAG, "clicked");
}
});
どんなAndroid入門書にも載っている、アクティビティでよくみるコード
Kotlinで書いたやつと比べると、冗長なのが一目瞭然!
Kotlinだとすっきり
button.setOnClickListener { Log.v(TAG, "clicked") }
Java、Kotlinそれぞれのクリックリスナー
Kotlin
button.setOnClickListener { Log.v(TAG, "clicked") }
Java
button.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
Log.v(TAG, "clicked!");
}
});
行数が短くなったことではなく、
「冗長な部分は書く必要がなく
本質的なことだけ書けばよくなった」
というのがミソ
Javaのコードをもう一度見てみましょう
button.setOnClickListener(new View.OnClickListener() { // new以降本質じゃない
@Override // <- 本質じゃない
public void onClick(View v) { // <- 本質じゃない
Log.v(TAG, "clicked!");
} // <- 本質じゃない
}); // <- 本質じゃない
Javaは本質じゃない部分が多い。Kotlinは本質のみを記述すればいい
Kotlinはなぜあんな記述ができるのか?
Single'Abstract'Method'Conversions
SAM変換
h"p://blog.jetbrains.com/kotlin/2013/08/kotlin;m6;is;here/
SAM$インターフェース
一つの(Single))抽象(Abstract))メソッド(Method)
をもつインターフェース
• Runnable):)void)run()
• View.OnClickListener):)void)onClick(View)v)
• Response.Listener<T>):)void)onResponse(T)response))
など、他にもたくさん
SAM変換
関数リテラル!">!SAMインターフェース!な変換
Kotlinでは、SAMインターフェースを引数にとるメソッドで、
引数の型と順序そして返値型が一致する関数リテラルを渡すと
インターフェースに変換してくれる
SAMインターフェースが必要な所も、関数リテラルでスッキリ!
SAM変換できる例
button.setOnClickListener( { (v : View): Unit -> Log.v(TAG, "clicked") })
button.setOnClickListener( { v -> Log.v(TAG, "clicked")})
button.setOnClickListener{ v -> Log.v(TAG, "clicked") }
button.setOnClickListener{ Log.v(TAG, "clicked") }
val listener : (View) -> Unit = {v -> Log.v(TAG, "clicked") }
//or
val listener : (View) -> Unit = { Log.v(TAG, "click") }
//or
val listener = { (v : View) : Unit -> Log.v(TAG, "clicked") }
button.setOnClickListener(listener)
SAM変換できない例
// 下記はコンパイルエラー
// Type mismatch
val listener : View.OnClickListener = { (v : View) : Unit -> Log.v(TAG, "clicked") }
// 下記は実行時エラー
// java.lang.ClassCastException
val listener = { (v : View) : Unit -> Log.v(TAG, "clicked") } as View.OnClickListener
引数に関数リテラルを渡さないといけない
SAM変換で無駄コードを無くせますね!
もう一例
VolleyのStringRequest
VolleyのStringRequest0Java版
StringRequest request = new StringRequest(
"https://www.google.co.jp/",
new Response.Listener<String>() {
@Override
public void onResponse(String response) {
Toast.makeText(getApplicationContext(), response, Toast.LENGTH_LONG).show();
}
},
new Response.ErrorListener() {
@Override
public void onErrorResponse(VolleyError volleyError) {
Toast.makeText(getApplicationContext(), "onErrorResponse", Toast.LENGTH_LONG).show();
}
}
);
無駄な部分が多く、縦に長い...
VolleyのStringRequest0Kotlin版
val request = StringRequest(
url = "https://www.google.co.jp/",
listener = { response ->
Toast.makeText(this, response, Toast.LENGTH_LONG).show()
},
errorListener = { volleyError ->
Toast.makeText(this, "onErrorResponse", Toast.LENGTH_LONG).show()
}
);
型パラーメータがあるインターフェースもSAM変換可能
名前付き引数で可読性アップ
ちょっとGroovyもみてみましょう
GroovyもSAM変換が使えるようです。
button.setOnClickListener { v -> Toas.makeText(this, "click", Toast.LENGTH_LONG).show() }
Groovy&2.2からas演算子がいらなくなったそうです。
h"p://groovy.codehaus.org/Groovy+2.2+release+notes
ちょっとGroovyもみてみましょう
SAMインターフェースだけでなく、複数メソッドを持つインターフェースも
Map+クロージャー+as演算子でこんな感じに!
viewPager.setOnPageChangeListener ([
onPageScrollStateChanged: { state -> Log.v(TAG, state) },
onPageScrolled : { position, positionOffset, positionOffsetPixels -> /*略*/ },
onPageSelected : { position -> Log.v(TAG, position) }
] as ViewPager.OnPageChangeListener)
h"p://groovy.codehaus.org/Groovy+way+to+implement+interfaces
実はAndroidStudioとIntelliJだと
一つだけメソッドを実装した匿名クラスがいい感じに!(SAM型も)
button.setOnClickListener((v) -> {Log.v(TAG, "clicked");});
IDE上ではいい感じで折り畳まれてますが、
githubとかでコードリビューするときは、
ほら畳まれないし...
h"p://qiita.com/RyotaMurohoshi/items/0ce799c747d91756131a
以上
SAM変換でした。
いらっとする冗長なコード2
ImageView imageView = (ImageView)findViewById(R.id.image_view);
どんなAndroid入門書にも載っている、アクティビティでよくみるコード
けれど「ImageView」ってなんで2回書く必要あるん?型推論してくれてもいいじゃん!
とりあえず継承します?
Ac#vityを継承したクラスBaseAc#vityにて、こんなメソッド定義すれば、
public <T extends View> T findById(int viewId) {
return (T)findViewById(viewId);
}
BaseAc'vityを更に継承したクラスでは、こんな感じでキャスト不要になりますね
ImageView imageView = findById(R.id.image_view);
けど継承って
• 継承むずい
• 「継承が許されるのは小学生までだよね」、だと...
• Ac$vityのサブクラスいくつかあるけど、全部に更にサブクラス
作ってメソッド定義する?
ちょっとないかな...
さてどうしましょう?
• 冗長な記述が頻発したり、よく使う処理がある
• Ac$vityとかに、メソッドを追加したい!
• けど継承したくない)or)できない
Extension)Funcitons
拡張関数
h"p://confluence.jetbrains.com/display/Kotlin/Extension+func=ons
Extension)Funcitons(拡張関数)とは
• 継承せずにメソッドを追加できる"
• 継承禁止なクラスにもメソッドを追加できる
• privateなメンバにアクセスはできない
• メソッドをオーバーライドはできない
Extension)Func-onsの例)定義側
extensions.kt内にて
package com.mrstar.extensions
import android.app.Activity
import android.view.View
fun <T : View> Activity.findById (id : Int) : T = findViewById(id) as T
Extension)Func-onsの例)利用側
package com.mrstar.android_with_kotlin
// 略
import com.mrstar.extensions.findById // <- 注目
public class MainActivity() : FragmentActivity() { // <- 注目
protected override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
val textView: ImageView = findById (R.id.image_view) // <- 注目
}
}
• fun$ClassName.methodName(hoge$:$Hoge)$:$Fuga"みたいな感じで
メソッドを定義します
• 定義した拡張メソッドをimportします
• そうすると普通のメソッドのように使えます
• この例だとAc+vityのサブクラスだけでなく、
FragmentAc+vity、Ac+onBarAc+vity、他のサブクラスでも使え
ます(継承で定義するなら、各クラスにメソッド定義が必要)
Extension)Func-onsを使えば...
• 継承しなくてもメソッドを追加できますね
• たくさんのクラスにメソッドを追加しなくてもいいですね
• 自作のメソッドで冗長な記述をスッキリできますね!
ちょっとC#もみてみましょう
C#にも拡張関数と同じような拡張メソッドがあります。
// 定義側
public static class StringExtensions
{
public static string Decorate(this string str, string symbol) {
return string.Format("{0}{1}{2}", symbol, str, symbol);
}
}
//利用側
string decoratedMessage = "Hello!".Decorate("===")
こんな感じで、sta$cなクラスにsta$cなメソッドとして拡張メソッドを定義します。
ちょっとC#もみてみましょう
C#は
• Javaのpackageアクセスのような、メンバを同じ名前空間だけに
公開するという制限がない
• privateでsta.cな入れ子のクラスは作れる
• が↑なクラスでは拡張メソッドを定義できない
このクラスにだけ、この名前空間でだけ使えるという拡張メソッドを定義できない!
一方Kotlinでは
• packageにその名前空間とそのサブ名前空間限定で
• クラス内にprivateアクセスレベルで
• 関数内にローカル関数内として、ローカルスコープで
Kotlinだと拡張関数を定義できる!
以上
拡張関数でした。
質問ありますか?
ご清聴ありがとうございました!
 
SAM変換について(Qiita)
h"p://qiita.com/RyotaMurohoshi

明日からちょっと KotlinでAndroidが書きたくなる(かもしれない?)SAM変換と拡張関数