$30 off During Our Annual Pro Sale. View Details »
Speaker Deck
Features
Speaker Deck
PRO
Sign in
Sign up for free
Search
Search
一歩進んだ拡張関数の活用とその濫用で後悔した話 #m3kt
Search
Taro Nagasawa
August 25, 2017
Programming
1
7.4k
一歩進んだ拡張関数の活用とその濫用で後悔した話 #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
1k
Kotlin 最新動向2022 #tfcon #techfeed
ntaro
1
2.3k
#Ubie 狂気の認知施策と選考設計
ntaro
13
13k
UbieにおけるサーバサイドKotlin活用事例
ntaro
1
1.2k
KotlinでSpring 完全理解ガイド #jsug
ntaro
6
3.5k
Kotlinでサーバサイドを始めよう!
ntaro
1
1k
Androidからサーバーサイドまで!プログラミング言語 Kotlinの魅力 #devboost
ntaro
5
2.9k
Kotlin Contracts #m3kt
ntaro
4
4.2k
How_to_Test_Server-side_Kotlin.pdf
ntaro
1
530
Other Decks in Programming
See All in Programming
Full-Cycle Reactivity in Angular: SignalStore mit Signal Forms und Resources
manfredsteyer
PRO
0
140
リリース時」テストから「デイリー実行」へ!開発マネージャが取り組んだ、レガシー自動テストのモダン化戦略
goataka
0
130
React Native New Architecture 移行実践報告
taminif
1
160
Rubyで鍛える仕組み化プロヂュース力
muryoimpl
0
140
【Streamlit x Snowflake】データ基盤からアプリ開発・AI活用まで、すべてをSnowflake内で実現
ayumu_yamaguchi
1
120
ID管理機能開発の裏側 高速にSaaS連携を実現したチームのAI活用編
atzzcokek
0
230
AIコーディングエージェント(skywork)
kondai24
0
180
Findy AI+の開発、運用におけるMCP活用事例
starfish719
0
1.2k
FluorTracer / RayTracingCamp11
kugimasa
0
230
AIエンジニアリングのご紹介 / Introduction to AI Engineering
rkaga
8
2.9k
開発に寄りそう自動テストの実現
goyoki
2
1k
Your Architecture as a Crime Scene?Forensic Analysis
manfredsteyer
PRO
0
100
Featured
See All Featured
RailsConf 2023
tenderlove
30
1.3k
No one is an island. Learnings from fostering a developers community.
thoeni
21
3.6k
Automating Front-end Workflow
addyosmani
1371
200k
ReactJS: Keep Simple. Everything can be a component!
pedronauck
666
130k
Designing for humans not robots
tammielis
254
26k
Connecting the Dots Between Site Speed, User Experience & Your Business [WebExpo 2025]
tammyeverts
10
730
How GitHub (no longer) Works
holman
316
140k
Why You Should Never Use an ORM
jnunemaker
PRO
61
9.6k
The World Runs on Bad Software
bkeepers
PRO
72
12k
StorybookのUI Testing Handbookを読んだ
zakiyama
31
6.5k
Fantastic passwords and where to find them - at NoRuKo
philnash
52
3.5k
Exploring the Power of Turbo Streams & Action Cable | RailsConf2023
kevinliebholz
37
6.2k
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