Upgrade to Pro — share decks privately, control downloads, hide ads and more …

Androidで使えるKotlinレシピ

kamedon
July 03, 2017

 Androidで使えるKotlinレシピ

クラスメソッドが運営するIT系技術ブログDevelopers.IOのカンファレンスイベントDevelopers.IO 2017にて、セッション「Androidで使えるKotlinレシピ」を発表しました。

#cmdevio2017

kamedon

July 03, 2017
Tweet

More Decks by kamedon

Other Decks in Technology

Transcript

  1. ,PUMJOͷ1SPQFSUJFTBOE'JFMET  wLFZXPSEQSPQFSUZ/BNF<1SPQFSUZ5ZQF>< QSPQFSUZ@JOJUJBMJ[FS>
 <HFUUFS><TFUUFS> wLFZXPSEWBM SFBEPOMZ WBS NVUBCMF 

    w1SPQFSUZ5ZQF<ʜ>
 ͳ͠ /PU/VMM ɺ /VMMBCMF ɺ 1MBUGPSN5ZQF  wWBMIPHF4USJOHl5FTUz wWBSGPP4USJOH OVMM
  2. ॳظԽ࣌ʹΠϯελϯεԽͰ͖Δ  ActivityͷϥΠϑαΠΫϧͳͲʹґଘ͠ͳ͍৔߹ class SampleActivity : AppCompatActivity() {
 
 val

    adapter = ItemAdapter()
 
 override fun onCreate(savedInstanceState: Bundle?) {
 super.onCreate(savedInstanceState)
 setContentView(R.layout.activity_sample)
 val listView = (findViewById(R.id.listView) as ListView)
 listView.adapter = adapter
 }
 }
  3. "DUJWJUZ࢓༷ͷ͍ͤͰॳظԽͰ͖ͳ͍  ίϯύΠϧ͸௨Δ͕ɺNPEʹͳΔ setContentViewҎ߱Ͱͳ͍ͱfindViewById͸औಘͰ͖ͳ͍ class SampleActivity : AppCompatActivity() {
 


    val adapter = ItemAdapter()
 
 val listView = (findViewById(R.id.listView) as ListView)
 
 override fun onCreate(savedInstanceState: Bundle?) {
 super.onCreate(savedInstanceState)
 setContentView(R.layout.activity_sample)
 listView.adapter = adapter
 }
 }
  4. "DUJWJUZϥΠϑαΠΫϧͷ͍ͤͰॳظԽͰ͖ͳ͍  listViewΛࢀর͞ΕΔ·ͰॲཧΛ஗Ԇ͢Δ setContentViewҎ߱Ͱࢀর͞Ε͍ͯΔͨΊ໰୊ͳ͠ class SampleActivity : AppCompatActivity() {
 


    val adapter = ItemAdapter()
 
 val listView by lazy {
 (findViewById(R.id.listView) as ListView)
 }
 
 override fun onCreate(savedInstanceState: Bundle?) {
 super.onCreate(savedInstanceState)
 setContentView(R.layout.activity_sample)
 listView.adapter = adapter
 }
 }
  5. ֎෦͔Βґଘؔ܎Λղܾ͢Δඞཁ͋Γ  class SampleActivity : AppCompatActivity() {
 val binding by

    lazy {
 DataBindingUtil.setContentView<ActivitySampleBinding>(this, R.layout.activity_sample)
 }
 }
 class SampleFragment : Fragment() {
 lateinit var binding: FragmentSampleBinding 
 override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? {
 binding = DataBindingUtil.inflate<FragmentSampleBinding> (inflater, R.layout.fragment_sample, container, false)
 
 }
 }
  6. ֎෦͔Βґଘؔ܎Λղܾ͢Δඞཁ͋Γ  class SampleFragment : Fragment() {
 lateinit var binding:

    FragmentSampleBinding
 override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? {
 binding = DataBindingUtil.inflate<FragmentSampleBinding> (inflater, R.layout.fragment_sample, container, false)
 
 }
 } inflaterͱcontainer͸onCreateViewͰ ࢀরͰ͖ΔΑ͏ʹͳΔͨΊɺby lazyͰॻ͘͜ͱ͕Ͱ͖ͳ͍
  7. ϓϥοτϑΥʔϜܕͱઓ͏  val listView: View = findViewById(R.id.listView)
 val listView: View?

    = findViewById(R.id.listView)  ϓϥοτϑΥʔϜܕ ͷ৔߹͸໌ࣔతʹܕΛॻ͘ wϓϥοτϑΥʔϜܕ͸ɺܕਪ࿦͠ͳ͍΄͏͕٢ w/PU/VMMͩͱࢥ͏ͷͰ͋Ε͹ɺ/PU/VMMʹ͢Δ w/VMMͷॠؒʹ/1&ͰམͪΔͷͰૣظʹόάΛൃݟͰ͖Δ w,PUMJO͸/1&Ͱམͱ͞ͳ͍ٕज़Ͱ͸ͳ͘ɺ/VMMΛద੾ʹѻ͍΍͘͢ ͯ͘͠ΕΔɻҙਤ͠ͳ͍ͷͰ͋Ε͹མͱ͢΂͖ɻ
  8. ࠷ॳ͸ྑ͔͕ͬͨʜ  ػೳ௥Ճ͸͞Ε͍ͯ͘… • ৽ͨʹageΛ௥Ճ: 20ࡀҎ্ • name͕NGϫʔυΛؚ·ͳ͍ class User(val

    name: String) {
 
 fun vaildate(): Boolean {
 return name.length in 1..16 }
 
 }
  9. ຊ౰ͷઓ͍͸͜Ε͔Βͩͥ  • ԿͰΤϥʔʹͳ͔ͬͨɺΤϥʔϝοηʔδ͍ͩͨ͠
 ɹɹɹɹɹɹɹɹɹʘ(^o^)ʗ class User(val name: String, val

    age: Int) {
 
 fun vaildate(): Boolean {
 return name.length in 1..16 &&
 name in listOf("hoge","foo") &&
 age >= 20
 }
 
 }
  10. 7BMJEBUJPOͷ%4-Λ࡞ͬͯɺϦϑΝΫλϦϯά  • Validation֤ཁૉͷ͋Δ΂͖࢟ɺͦ͏Ͱͳ͍࣌ͷϝοηʔδ • A ͸ B Ͱ͋Δɻͦ͏Ͱͳ͍࣌ɺ Error

    Message class User(val name: String, val age: Int) {
 
 fun vaildate(): Boolean {
 return name.length in 1..16 &&
 name in listOf("hoge","foo") &&
 age >= 20
 }
 
 }
  11. ͍͖ͳΓ࠷ऴܕΛͲʔΜ  • User be {validation} not “error message” •

    Validation͕૿͑ͯ΋໰୊ͳ͠ • υϝΠϯͷจ๏ͷ··ͰɺಡΈ΍͍͢ class User(val name: String, val age: Int) {
 val validation = Validation<User> { be { age >= 20 } not "成人のみです"
 be { name.length in 1..16 } not "1-16文字以下で"
 be { name !in listOf("hoge", "foo") } 
 not "NGワード以外で"
 }
 fun vaildate() = validation.validate(this)
 }
  12. ͞ΒʹಡΈ΍͘͢  • ΍Γա͗ײ͕͋Δ class User(val name: String, val age:

    Int) {
 val validation = Validation<User> { of(age) be { it >= 20 } not "成人のみです"
 of(name) be { it.length in 1..16 } not "1-16文字以下で"
 of(name) be { it !in listOf("hoge", "foo") } 
 not "NGワード以外で"
 }
 fun vaildate() = validation.validate(this)
 }
  13. ఆٛͬΆ͘͢ΔPQFSBUPSJOWPLF  class Validation<in T>(val validations: List<Pair<T.() -> Boolean, String>>)

    {
 
 companion object { 
 operator fun <T> invoke(init: ValidationBuilder<T>.() -> Unit): Validation<T> { 
 val builder = ValidationBuilder<T>()
 return builder.apply(init).build()
 }
 } Validation<User> { be { age >= 20 } not “成人のみです” } operator invokeͰinvokeΛলུɺValidation()Ͱ࣮ߦͰ͖Δ ࠷ޙ͕ϥϜμͳͷͰ()লུՄೳ͠ɺ{}ʹͳΔ
  14. ఆٛͬΆ͘͢ΔPQFSBUPSJOWPLF  class Validation<in T>(val validations: List<Pair<T.() -> Boolean, String>>)

    {
 
 companion object { 
 operator fun <T> invoke(init: ValidationBuilder<T>.() -> Unit): Validation<T> { 
 val builder = ValidationBuilder<T>()
 return builder.apply(init).build()
 }
 } Validation<User>.invoke({…}) Validation<User>.invoke{…} Validation<User>{…} operator invokeͰinvokeΛলུɺValidation()Ͱ࣮ߦͰ͖Δ ࠷ޙ͕ϥϜμͳͷͰ()লུՄೳ͠ɺ{}ʹͳΔ
  15. WBS͔ΒWBMʹ͢ΔͨΊʹ  BuilderΛ࡞੒͠ɺ • ఆ͕ٛऴΘΔ·Ͱ͸mutableʹͯ͠Buildޙ͸immutableʹ͢Δ • Builder಺ʹఆٛ༻ͷγϯλοΫεΛด͡ΊΔ class Validation<in T>(val

    validations: List<Pair<T.() -> Boolean, String>>) {
 fun validate(value: T) = validations.filter { !it.first.invoke(value) }.map { it.second }
 }
 class ValidationBuilder<T> { var validations: MutableList<Pair<T.() -> Boolean, String>> = mutableListOf()
 
 fun build(): Validation<T> {
 return Validation(validations)
 }
 }
  16. ϨγʔόʔࢦఆMBNCEB  class ValidationBuilder<T> {
 var validations: MutableList<Pair<T.() -> Boolean,

    String>> = mutableListOf() 
 ɹɹfun be(validate: T.() -> Boolean) = validate } Validation<User> { be { age >= 20 } not “成人のみです” } TͷvalidateΛϥϜμͰ࡞੒͢Δɺ࠷ޙͷϥϜμ͸লུՄೳ T.() -> Boolean) = (T)->Boolean ͷҧ͍͸ʁ
  17. 5 #PPMFBO 5 #PPMFBOͷҧ͍͸ʁ  Validation<User> { be { it.age

    >= 20 } not “成人のみです” //fun be(validate: (T) -> Boolean) = validate be { this.age >= 20 } not “成人のみです” //fun be(validate: T.() -> Boolean) = validate } Ϩγʔόʔࢦఆͷ৔߹this͕TʹͳΔ
  18. ୹͘͢Δ&YUFOTJPOT'VODUJPOTPS1SPQFSUJFT  class ValidationBuilder<T> {
 var validations: MutableList<Pair<T.() -> Boolean,

    String>> = mutableListOf()
 fun be(validate: T.() -> Boolean) = validate infix fun (T.() -> Boolean).not(error: String) {
 validations.add(this to error)
 } } Validation<User> { be { age >= 20 } not “成人のみです” } validateͯ͠ແޮͳ஋ͩͬͨ৔߹Τϥʔϝοηʔδฦ͢Α͏ʹ͢Δ (T.() -> Boolean)ʹ֦ுؔ਺Λ࣮૷͠Pairʹͯ͠add͢Δ
  19. ӳޠͬΆ͍จ๏ʹJOpY  class ValidationBuilder<T> {
 var validations: MutableList<Pair<T.() -> Boolean,

    String>> = mutableListOf()
 fun be(validate: T.() -> Boolean) = validate infix fun (T.() -> Boolean).not(error: String) {
 validations.add(this to error)
 } } Validation<User> { be { age >= 20 } not “成人のみです” } infix༗Γແ͠Ͳ͏ͳΔ͔ݟͯΈ·͠ΐ͏
  20. ӳޠͬΆ͍จ๏ʹJOpY  Validation<User> { be { it.age >= 20 }.not(“成人のみです”)

    //fun (T.() -> Boolean).not(error: String) 
 be { this.age >= 20 } not “成人のみです” //infix fun (T.() -> Boolean).not(error: String) } infixΛ࢖͏͜ͱͰ.() Λͳ͘͠ɺεϖʔε۠੾Γʹ ӳޠͬΆ͘Ͱ͖Δ
  21. ιʔείʔυͩͱ  class FriedRice val friedRice = FriedRice() class BowlOFRice(val

    rice: Rice, val topping: Topping) interface Topping interface Rice class JapaneseRice : Rice class BeefTopping : Topping //オブジェクトを入れる val beefBowl = BowlOFRice(JapaneseRice(), BeefTopping()) ※ઃܭ࣍ୈͰνϟʔϋϯ΋DIԽͰ͖·͢
  22. %*ͱ%*$POUBJOFS  %*$POUBJOFS͸
 %*Λ࣮ݱ͢ΔͨΊͷศརπʔϧͰ͔͠ͳ͍ wઌఔͷઆ໌Ͱ%BHHFSͳͲͰ͖ͯ·͔ͨ͠ʁ class BowlOFRice(val rice: Rice, val

    topping: Topping) //オブジェクトを入れる val beefBowl = BowlOFRice(JapaneseRice(), BeefTopping())
  23. 4FSWJDF-PDBUPS  class FriedRice val friedRice = FriedRice() class BowlOFRice(val

    di: DIContainer){ @Inject lateinit var rice: Rice @Inject lateinit var topping: Topping init{ di.inject() } } //オブジェクトを入れる val beefBowl = BowlOFRice(di) ※ઃܭ࣍ୈͰνϟʔϋϯ΋DIԽͰ͖·͢
  24. ,PUMJOͳΒҧ͏ํ๏͕͋Δ͔΋  %FMFHBUJPOɺ%FMFHBUFE1SPQFSUJFTΛ࢖͑͹΋ͬ ͱ͏·͘Ͱ͖Δ͔΋ class Delegate {
 operator fun getValue(thisRef:

    Any?, property: KProperty<*>): String {
 return "$thisRef, thank you for delegating '${property.name}' to me!"
 }
 
 operator fun setValue(thisRef: Any?, property: KProperty<*>, value: String) {
 println("$value has been assigned to '${property.name} in $thisRef.'")
 }
 } 
 class Example {
 var hoge: String by Delegate()
 }
  25. ,PEFJO͕Ͱ͖Δ͜ͱ  w -B[JMZJOTUBOUJBUFZPVSEFQFOEFODJFTXIFOOFFEFE w 4UPQDBSJOHBCPVUEFQFOEFODZJOJUJBMJ[BUJPOPSEFS w &BTJMZCJOEDMBTTFTPSJOUFSGBDFTUPUIFJSJOTUBODFPS QSPWJEFS w

    &BTJMZEFCVHZPVSEFQFOEFODZCJOEJOHTBOE SFDVSTJPOT SalomonBrys/KodeinͷREADME.mdΑΓ •ඞཁͳ࣌ʹΠϯελϯεԽ •providerʹ
 ΫϥεɺΠϯλʔϑΣʔεɺΠϯελϯεΛઃఆͰ͖ΔΑ
  26. .PEVMFఆٛ  Kodein.Module { bind<型>(Qualifier) with singleton { … }

    bind<型>(Qualifier) with provider { … } bind<型>(Qualifier) with instance ( … ) } • Dagger2ͷModuleͱಉ͡ • KotlinͷػೳΛϑϧʹ࢖͍ͬͯͯจ๏ͰಡΈ΍͍͢ • infixɺϥϜμলུɺϨγʔόʔࢦఆϥϜμ
  27. .PEVMFྫ  object NetworkModule { val module = Kodein.Module {

    bind<OkHttpClient>() with singleton { OkHttpClient.Builder().build() } bind<Converter.Factory>() with singleton { GsonConverterFactory.create() } bind<Retrofit>() with singleton { Retrofit.Builder() .baseUrl(BuildConfig.GITHUB_ENDOPOINT) .addConverterFactory(instance()) .client(instance()).build() } } }
  28. .PEVMFྫ  object NetworkModule { val module = Kodein.Module {

    bind<OkHttpClient>() with singleton { OkHttpClient.Builder().build() } bind<Converter.Factory>() with singleton { GsonConverterFactory.create() } bind<Retrofit>() with singleton { Retrofit.Builder() .baseUrl(BuildConfig.GITHUB_ENDOPOINT) .addConverterFactory(instance()) .client(instance()).build() } } }
  29. ,PEFJOͷఆٛ  • Dagger2ͷComponentͱಉ͡ • ࢖༻͢ΔϞδϡʔϧΛఆٛ͢Δ class App : Application(),

    KodeinAware { override val kodein: Kodein = Kodein { bind<Context>() with instance(this@App) import(NetworkModule.module) import(PresentationModule.module) } }
  30. *OKFDU  class MVPActivity : AppCompatActivity(), GithubView { val binding

    by lazy { DataBindingUtil.setContentView<…>(this, R.layout…) } val injector: KodeinInjector = KodeinInjector() val presenter: GithubPresenter by injector.instance() override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) injector.inject(Kodein { extend(kodein) bind<GithubView>() with instance(this@MVPActivity) }) binding.searchBtn.setOnClickListener { presenter.search(binding.userEdit.text.toString()) } } }
  31. %BHHFSͱ,PEFJO  ,PEFJO %BHHFS ґଘؔ܎Λ LBQUͰੜ੒ ෆཁ ඞཁ BQUͰੜ੒͞Εͨ΋ͷ JOKFDU

    ̋ ˚ )PMEFSΛ࡞ΔͳͲ޻෉͕ඞཁ ґଘؔ܎ͷ Τϥʔ ࣮ߦ࣌ ίϯύΠϧ࣌ είʔϓ ̋ ̋ /BNFE 2VBMJpFS ̋ ̋