Upgrade to Pro
— share decks privately, control downloads, hide ads and more …
Speaker Deck
Features
Speaker Deck
PRO
Sign in
Sign up for free
Search
Search
一歩進んだ拡張関数の活用とその濫用で後悔した話 #m3kt
Search
Taro Nagasawa
August 25, 2017
Programming
1
7.3k
一歩進んだ拡張関数の活用とその濫用で後悔した話 #m3kt
どこでもKotlin #1 (
https://m3-engineer.connpass.com/event/63057/)で使用したスライドです
。
Taro Nagasawa
August 25, 2017
Tweet
Share
More Decks by Taro Nagasawa
See All by Taro Nagasawa
Android開発者のための Kotlin Multiplatform入門
ntaro
0
740
Kotlin 最新動向2022 #tfcon #techfeed
ntaro
1
2.2k
#Ubie 狂気の認知施策と選考設計
ntaro
13
13k
UbieにおけるサーバサイドKotlin活用事例
ntaro
1
1.1k
KotlinでSpring 完全理解ガイド #jsug
ntaro
6
3.5k
Kotlinでサーバサイドを始めよう!
ntaro
1
990
Androidからサーバーサイドまで!プログラミング言語 Kotlinの魅力 #devboost
ntaro
5
2.8k
Kotlin Contracts #m3kt
ntaro
4
4.1k
How_to_Test_Server-side_Kotlin.pdf
ntaro
1
510
Other Decks in Programming
See All in Programming
JetBrainsのAI機能の紹介 #jjug
yusuke
0
200
LLMは麻雀を知らなすぎるから俺が教育してやる
po3rin
3
2.1k
QA x AIエコシステム段階構築作戦
osu
0
270
Terraform やるなら公式スタイルガイドを読もう 〜重要項目 10選〜
hiyanger
13
3.1k
CEDEC2025 長期運営ゲームをあと10年続けるための0から始める自動テスト ~4000項目を50%自動化し、月1→毎日実行にした3年間~
akatsukigames_tech
0
140
AIに安心して任せるためにTypeScriptで一意な型を作ろう
arfes0e2b3c
0
370
「リーダーは意思決定する人」って本当?~ 学びを現場で活かす、リーダー4ヶ月目の試行錯誤 ~
marina1017
0
220
自作OSでDOOMを動かしてみた
zakki0925224
1
1.3k
実践!App Intents対応
yuukiw00w
1
280
0から始めるモジュラーモノリス-クリーンなモノリスを目指して
sushi0120
1
280
オホーツクでコミュニティを立ち上げた理由―地方出身プログラマの挑戦 / TechRAMEN 2025 Conference
lemonade_37
2
470
なぜ今、Terraformの本を書いたのか? - 著者陣に聞く!『Terraformではじめる実践IaC』登壇資料
fufuhu
4
600
Featured
See All Featured
個人開発の失敗を避けるイケてる考え方 / tips for indie hackers
panda_program
110
20k
CoffeeScript is Beautiful & I Never Want to Write Plain JavaScript Again
sstephenson
161
15k
Building Applications with DynamoDB
mza
96
6.6k
For a Future-Friendly Web
brad_frost
179
9.9k
Testing 201, or: Great Expectations
jmmastey
45
7.6k
Bash Introduction
62gerente
614
210k
The Power of CSS Pseudo Elements
geoffreycrofte
77
5.9k
"I'm Feeling Lucky" - Building Great Search Experiences for Today's Users (#IAC19)
danielanewman
229
22k
Distributed Sagas: A Protocol for Coordinating Microservices
caitiem20
333
22k
JavaScript: Past, Present, and Future - NDC Porto 2020
reverentgeek
50
5.5k
Dealing with People You Can't Stand - Big Design 2015
cassininazir
367
26k
Raft: Consensus for Rubyists
vanstee
140
7.1k
Transcript
一歩進んだ拡張関数の活用 とその濫用で後悔した話 どこでもKotlin #1 2017-08-24 長澤太郎 @ngsw_taro
自己紹介 • 長澤 太郎(たろーって呼んでね) • @ngsw_taro • エムスリー株式会社 • やすべえとディズニーが大好き!
宣伝 9/29発売! 好評発売中 無料で配布中 goo.gl/5vUT7o 鋭意執筆中! Kotlin in Action
もくじ 1. エクステンションからインタフェースへ 2. partial class的なやつ 3. Double Context Extension
1. エクステンションから インタフェースへ
AndroidではContextを第1引数によく取る class MyView: FrameLayout { // 各コンストラクタは省略 init { //
LayoutInflaterオブジェクトが欲しい LayoutInflater.from(context) .inflate(R.layout.my_view, this) } }
class MyView: FrameLayout { // 各コンストラクタは省略 init { // LayoutInflaterオブジェクトが欲しい
LayoutInflater.from(context) .inflate(R.layout.my_view, this) } } AndroidではContextを第1引数によく取る getContext()でも同じ
そんなときはエクステンション class MyView: FrameLayout { init { // LayoutInflaterオブジェクトが欲しい context.layoutInflater()
.inflate(R.layout.my_view, this) } } fun Context.layoutInflater(): LayoutInflater = LayoutInflater.from(this)
さらに短く class MyView: FrameLayout { init { // LayoutInflaterオブジェクトが欲しい layoutInflater()
.inflate(R.layout.my_view, this) } } fun View.layoutInflater(): LayoutInflater = LayoutInflater.from(context)
さらに短く class MyView: FrameLayout { init { // LayoutInflaterオブジェクトが欲しい layoutInflater()
.inflate(R.layout.my_view, this) } } fun View.layoutInflater(): LayoutInflater = LayoutInflater.from(context) Viewでしか使えない
似ているエクステンションを量産? class MyAdapter: ArrayAdapter<MyItem> { override fun getView(...) { //
LayoutInflaterオブジェクトが欲しい layoutInflater() .inflate(R.layout.item_view, null) } } fun ArrayAdapter<*>.layoutInflater(): LayoutInflater = LayoutInflater.from(context)
インタフェースで共通部分を抽出 interface ContextHolder { fun getContext(): Context } fun ContextHolder.layoutInflater():
LayoutInflater = LayoutInflater.from(getContext())
ContextHolderと拡張関数1個で賄える class MyAdapter: ArrayAdapter<MyItem>, ContextHolder { override fun getView(...) {
layoutInflater().inflate(...) } } class MyView: FrameLayout, ContextHolder { init { layoutInflater().inflate(...) }
2. partial class的なやつ
大きくなりがちなActivity class MainActivity: AppCompatActivity() { var textView: TextView? = null
override fun onCreate(bundle: Bundle?) { super.onCreate(bundle) prepare() setupViews() } private fun prepare() {...} private fun setupViews() {...} }
大きくなりがちなActivity class MainActivity: AppCompatActivity() { var textView: TextView? = null
override fun onCreate(bundle: Bundle?) { super.onCreate(bundle) prepare() setupViews() } private fun prepare() {...} private fun setupViews() {...} } privateなヘルパーメソッド
インタフェース+エクステンションで分割 class MainActivity: AppCompatActivity(), MainActivityHelper { override fun onCreate(bundle: Bundle?)
{ super.onCreate(bundle) prepare() setupViews() } interface MainActivityHelper { fun MainActivity.prepare() {...} fun MainActivity.setupViews() {...} }
インタフェース+エクステンションで分割 class MainActivity: AppCompatActivity(), MainActivityHelper { override fun onCreate(bundle: Bundle?)
{ super.onCreate(bundle) prepare() setupViews() } interface MainActivityHelper { fun MainActivity.prepare() {...} fun MainActivity.setupViews() {...} } ヘルパーメソッドを 拡張関数として定義
公開されているレシーバのAPIが使える interface MainActivityHelper { fun MainAcitivty.prepare() { ... } fun
MainActivity.setupViews() { setContentView(R.layout.activity_main) textView = findViewById(R.id.text_view) } }
3. Double Context Extension
こんな関数呼び出しをしたい! class MyActivity: AppCompatActivity() { ... fun showMessage() { //
myDialog.show(supportFragmentManager, "tag") myDialog.show("tag") } }
class MyActivity: AppCompatActivity() { ... fun showMessage() { // myDialog.show(supportFragmentManager,
"tag") myDialog.show("tag") } } こんな関数呼び出しをしたい! これを渡すのはお決まりだから 省略したい
class MyActivity: AppCompatActivity() { ... fun showMessage() { // myDialog.show(supportFragmentManager,
"tag") myDialog.show("tag") } } こんな関数呼び出しをしたい! DialogFragmentに拡張関数showを生やせば? いや、プロパティsupportFragmentManagerが必要だ... →FragmentManagerに依存
方法1: エクステンション in インタフェース interface DialogFeature { fun getSupportFragmentManager(): FragmentManager
fun DialogFragment.show(tag: String) { show(getSupportFragmentManager(), tag) } } class MyActivity: AppComatActivity(), DialogFeature { ... fun showMessage() { myDialog.show("tag) }
方法1: エクステンション in インタフェース interface DialogFeature { fun getSupportFragmentManager(): FragmentManager
fun DialogFragment.show(tag: String) { show(getSupportFragmentManager(), tag) } } class MyActivity: AppComatActivity(), DialogFeature { ... fun showMessage() { myDialog.show("tag) } インタフェースを 実装したくない!
方法2: Double Context Extension • 2つのコンテキストを持った拡張関数 • 命名 by 私
• 見た目: 型Aの定義中で、型Aに依存しながらも型Bの拡張関 数を生やすことができる class MyActivity: AppCompatActivity() { ... fun showMessage() { myDialog.show("tag") } } この関数は、引数の他に DialogFragmentとFragmentActivity の2つに依存する
タネ明かし • 実際のコード: 「B型の拡張関数」を返す「A型の拡張プロパ ティ」を定義する // 別の適当なファイル val FragmentActivity.show: DialogFragment.(String)->Unit
get() = { tag -> show(supportFragmentManager, tag) }
どう解釈されるのか val FragmentActivity.show: DialogFragment.(String)->Unit get() = { tag -> show(supportFragmentManager,
tag) } class MyActivity: AppCompatActivity() { ... fun showMessage() { myDialog.show("tag") } }
どう解釈されるのか val FragmentActivity.show: DialogFragment.(String)->Unit get() = { tag -> show(supportFragmentManager,
tag) } class MyActivity: AppCompatActivity() { ... fun showMessage() { myDialog.show("tag") } }
どう解釈されるのか val FragmentActivity.show: DialogFragment.(String)->Unit get() = { tag -> show(supportFragmentManager,
tag) } class MyActivity: AppCompatActivity() { ... fun showMessage() { myDialog.show("tag") } } myDialog.(this.show)("tag") と書いても同じ
どう解釈されるのか val FragmentActivity.show: DialogFragment.(String)->Unit get() = { tag -> show(supportFragmentManager,
tag) } class MyActivity: AppCompatActivity() { ... fun showMessage() { myDialog.show("tag") } }
どう解釈されるのか val FragmentActivity.show: DialogFragment.(String)->Unit get() = { tag -> show(supportFragmentManager,
tag) } class MyActivity: AppCompatActivity() { ... fun showMessage() { myDialog.show("tag") } }
どう解釈されるのか val FragmentActivity.show: DialogFragment.(String)->Unit get() = { tag -> show(supportFragmentManager,
tag) } class MyActivity: AppCompatActivity() { ... fun showMessage() { myDialog.show("tag") } }
どう解釈されるのか val FragmentActivity.show: DialogFragment.(String)->Unit get() = { tag -> show(supportFragmentManager,
tag) } class MyActivity: AppCompatActivity() { ... fun showMessage() { myDialog.show("tag") } }
どう解釈されるのか val FragmentActivity.show: DialogFragment.(String)->Unit get() = { tag -> show(supportFragmentManager,
tag) } class MyActivity: AppCompatActivity() { ... fun showMessage() { myDialog.show("tag") } } A.(B)->Cは(A, B)->Cと 見なせる性質を利用して show(myDialog, "tag") or show.invoke(myDialog, "tag")
すごく ない?
いや、 わけわか らん
まとめ • 拡張関数って便利だよね • でも限界がある • インタフェースと組み合わせる • Double Context
Extensionという提案
まとめ • 拡張関数って便利だよね • でも限界がある • インタフェースと組み合わせる • Double Context
Extensionという提案 • 変なことしないで素直なコードを心がけましょう
エムスリーで 僕とKotlin