Slide 1

Slide 1 text

Copyright © Sansan, Inc. All rights reserved. > みんな⼤好き拡張関数 Jumpei Yamamoto 2017.6.29 第6回 Kotlin勉強会 in Sansan #kotlin_sansan

Slide 2

Slide 2 text

Copyright © Sansan, Inc. All rights reserved. > ⾃⼰紹介 - ⼭本純平 - twitter: @boohbah - github: https://github.com/yamamotoj

Slide 3

Slide 3 text

Copyright © Sansan, Inc. All rights reserved. 3

Slide 4

Slide 4 text

Copyright © Sansan, Inc. All rights reserved. 4 DSOC事業部でデータ分析 ビールは⼀⽇2杯まで

Slide 5

Slide 5 text

Copyright © Sansan, Inc. All rights reserved. > 拡張関数

Slide 6

Slide 6 text

Copyright © Sansan, Inc. All rights reserved. > 便利! fun String.lastChar() = get(length - 1) print(“Kotlin”.lastChar()) >> n

Slide 7

Slide 7 text

Copyright © Sansan, Inc. All rights reserved. > propertyアクセス val String.isNumeric get() = StringUtils.isNumeric(this) “30”.isNumeric // -> True

Slide 8

Slide 8 text

Copyright © Sansan, Inc. All rights reserved. > いくつかの疑問がありますね? - どういう仕組み? - 元のクラスとメソッドが被ったら? - 元のクラスが継承している場合は? - 標準的なクラスに、どんどんメソッドを追加 していったらメンテナンスが⼤変そう

Slide 9

Slide 9 text

Copyright © Sansan, Inc. All rights reserved. > どういう仕組み?

Slide 10

Slide 10 text

Copyright © Sansan, Inc. All rights reserved. > どういう仕組み? (StringUtils.kt) fun String.lastChar() = get(length - 1) // Java͔Βݺͼग़͢ StringUtilsKt.lastChat(“Java”);

Slide 11

Slide 11 text

Copyright © Sansan, Inc. All rights reserved. > どういう仕組み? (StringUtils.kt) fun String.lastChar() = get(length - 1) // Java͔Βݺͼग़͢ StringUtilsKt.lastChat(“Java”); 内部的にはthisを引数にした static methodの呼び出し

Slide 12

Slide 12 text

Copyright © Sansan, Inc. All rights reserved. > どういう仕組み? (StringUtils.kt) fun String.lastChar() = get(length - 1) // Java͔Βݺͼग़͢ StringUtilsKt.lastChat(“Java”); ↑Ktまで含めたクラス名

Slide 13

Slide 13 text

Copyright © Sansan, Inc. All rights reserved. > 元のメソッドと被ったら?

Slide 14

Slide 14 text

Copyright © Sansan, Inc. All rights reserved. > 元のメソッドと被ったら? class Universe{ fun answer() = 42 } fun Universe.answer() = 100

Slide 15

Slide 15 text

Copyright © Sansan, Inc. All rights reserved. > 元のメソッドと被ったら? class Universe{ fun answer() = 42 } fun Universe.answer() = 100 Universe().answer() // -> 42

Slide 16

Slide 16 text

Copyright © Sansan, Inc. All rights reserved. > 元のメソッドと被ったら? class Universe{ fun answer() = 42 } fun Universe.answer() = 100 Universe().answer() // -> 42 必ず、定義元のクラスのメンバーが実⾏される 拡張関数が実⾏されることはない

Slide 17

Slide 17 text

Copyright © Sansan, Inc. All rights reserved. > 他の拡張メソッドと名前が被る場合 // Extension1.kt package hoge.extension1 fun SomeClass.hello() = println(“hello”) ————— // Extension2.kt package hoge.extension2 fun SomeClass.hello() = println(“͜Μʹͪ͸”) 違うパッケージなら定義可能

Slide 18

Slide 18 text

Copyright © Sansan, Inc. All rights reserved. > 他の拡張メソッドと名前が被る場合 // Extension1.kt package hoge.extension1 fun SomeClass.hello() = println(“hello”) ————— // Extension2.kt package hoge.extension2 fun SomeClass.hello() = println(“͜Μʹͪ͸”) import hoge.extension1.hello SomeClass().hello() // -> hello

Slide 19

Slide 19 text

Copyright © Sansan, Inc. All rights reserved. > 他の拡張メソッドと名前が被る場合 // Extension1.kt package hoge.extension1 fun SomeClass.hello() = println(“hello”) ————— // Extension2.kt package hoge.extension2 fun SomeClass.hello() = println(“͜Μʹͪ͸”) import hoge.extension2.hello SomeClass().hello() // -> ͜Μʹͪ͸

Slide 20

Slide 20 text

Copyright © Sansan, Inc. All rights reserved. > 元のクラスが継承されてい たら?

Slide 21

Slide 21 text

Copyright © Sansan, Inc. All rights reserved. > 元のクラスが継承されている場合 open class ParentClass class ChildClass: ParentClass() fun ParentClass.greeting() = println("hello!") ParentClass().greeting() >> hello! ChildClass().greeting() >> hello!

Slide 22

Slide 22 text

Copyright © Sansan, Inc. All rights reserved. > 元のクラスが継承されている場合 open class ParentClass class ChildClass: ParentClass() fun ParentClass.greeting() = println("hello!") fun ChildClass.greeting() = println(“good bye!”) ParentClass().greeting() >> hello! ChildClass().greeting() >> good bye!

Slide 23

Slide 23 text

Copyright © Sansan, Inc. All rights reserved. > 元のクラスが継承されている場合 open class ParentClass class ChildClass: ParentClass() fun ParentClass.greeting() = println("hello!") fun ChildClass.greeting() = println(“good bye!”) fun printGreeting(obj: ParentClass) { obj.greeting() }

Slide 24

Slide 24 text

Copyright © Sansan, Inc. All rights reserved. > 元のクラスが継承されている場合 open class ParentClass class ChildClass: ParentClass() fun ParentClass.greeting() = println("hello!") fun ChildClass.greeting() = println(“good bye!”) fun printGreeting(obj: ParentClass) { obj.greeting() } printGreeting(ChildClass()) >> hello!

Slide 25

Slide 25 text

Copyright © Sansan, Inc. All rights reserved. > 元のクラスが継承されている場合 open class ParentClass class ChildClass: ParentClass() fun ParentClass.greeting() = println("hello!") fun ChildClass.greeting() = println(“good bye!”) fun printGreeting(obj: ParentClass) { obj.greeting() } printGreeting(ChildClass()) >> hello! このクラスに対して静的に解決される

Slide 26

Slide 26 text

Copyright © Sansan, Inc. All rights reserved. > 標準的なクラスに、どんどん メソッドを追加していったら メンテナンスが⼤変そう

Slide 27

Slide 27 text

Copyright © Sansan, Inc. All rights reserved. > 拡張関数の問題点1 - どこにでも定義できる - → どこに書いたかわからなくなる

Slide 28

Slide 28 text

Copyright © Sansan, Inc. All rights reserved. > 拡張関数の問題点2 - 同じ名前の拡張関数でもimportで切り替え可能 - → コードを読んでいるときにはどの拡張関数を呼んでい るかはわかりにくい - → 継承しているときなどの挙動も複雑になる

Slide 29

Slide 29 text

Copyright © Sansan, Inc. All rights reserved. > 拡張関数の問題点3 - 元クラスのメンバと拡張関数の名前がかぶった場合には元 クラスのメンバのメソッドが必ず呼ばれる。 - → 定義時には名前がかぶっていなくても、元クラスの バージョンアップにより、拡張関数が上書きされるリス ク

Slide 30

Slide 30 text

Copyright © Sansan, Inc. All rights reserved. > 拡張関数定義のための > ガイドライン

Slide 31

Slide 31 text

Copyright © Sansan, Inc. All rights reserved. > ガイドライン1 (com.xxx.utils.StringUtils.kt) @file:JvmName(“StringUtils”) package com.xxx.utils fun String.lastChar() = get(length - 1) ֦ுؔ਺Λఆٛ͢ΔΫϥεͷ৔ॴɺ ϑΝΠϧ໊ͷίϯϕϯγϣϯΛ͖ΊΔɻ

Slide 32

Slide 32 text

Copyright © Sansan, Inc. All rights reserved. > ガイドライン1 (com.xxx.utils.StringUtils.kt) @file:JvmName(“StringUtils”) package com.xxx.utils fun String.lastChar() = get(length - 1) ֦ுؔ਺Λఆٛ͢ΔΫϥεͷ৔ॴɺ ϑΝΠϧ໊ͷίϯϕϯγϣϯΛ͖ΊΔɻ たとえばJavaの慣習にしたがい、 StringならStringUtilsというファイル名 またJavaからも使⽤する場合は@file:JvmNameを指定

Slide 33

Slide 33 text

Copyright © Sansan, Inc. All rights reserved. > ガイドライン2 ʢͨͱ͑ύοέʔδ͕͕ͪͬͨͱͯ͠΋ʣ ಉ໊͡લͷ֦ுؔ਺Λఆٛ͠ͳ͍ ͋·Γʹ൚༻తͳ໊લ΋ؾΛ͚ͭͨ΄͏͕Α͍

Slide 34

Slide 34 text

Copyright © Sansan, Inc. All rights reserved. > ガイドライン3 Ή΍Έʹ֦ுؔ਺Λఆٛ͠ͳ͍ɻ ಛఆͷίϯςΩετʹґଘ͢ΔΑ͏ͳ֦ுؔ਺͸ආ͚Δ

Slide 35

Slide 35 text

Copyright © Sansan, Inc. All rights reserved. > でもさ、

Slide 36

Slide 36 text

Copyright © Sansan, Inc. All rights reserved. > せっかくの拡張関数 val date = Date().before(30.minutes) val size = 30.GB もっとがんがん使いたい! とかやりたい!!

Slide 37

Slide 37 text

Copyright © Sansan, Inc. All rights reserved. > 拡張関数の定義できる箇所 - publicな関数 - クラス内 - 関数内に定義する拡張関数 - interfaceに定義する

Slide 38

Slide 38 text

Copyright © Sansan, Inc. All rights reserved. > 拡張関数の定義できる箇所 - publicな関数 - クラス内 - 関数内に定義する拡張関数 - interfaceに定義する

Slide 39

Slide 39 text

Copyright © Sansan, Inc. All rights reserved. > クラス内の拡張関数 class MyClass{ val Int.seconds get() = this * 1000 val Int.minutes get() = this.seconds * 60 fun calc(){ val s = 20.seconds } } 利⽤できるスコープはそのクラスの中

Slide 40

Slide 40 text

Copyright © Sansan, Inc. All rights reserved. > クラス内の拡張関数 open class MyClass{ val Int.seconds get() = this * 1000 val Int.minutes get() = this.seconds * 60 } class SubClass: MyClass(){ fun calc(){ val s = 20.seconds } } サブクラス内でも使⽤可能

Slide 41

Slide 41 text

Copyright © Sansan, Inc. All rights reserved. > 拡張関数の定義できる箇所 - publicな関数 - クラス内 - 関数内に定義する拡張関数 - interfaceに定義する

Slide 42

Slide 42 text

Copyright © Sansan, Inc. All rights reserved. > 関数内で拡張関数を定義 fun updateView(view: View){ fun Boolean.toVisibility() = if (this) View.VISIBLE else View.GONE
 view.visibility = true.toVisibility() } 関数内でのみ使⽤可能

Slide 43

Slide 43 text

Copyright © Sansan, Inc. All rights reserved. > 関数内で拡張関数を定義 fun updateView(view: View){ fun Boolean.toVisibility() = if (this) View.VISIBLE else View.GONE
 view.visibility = true.toVisibility() } 関数内でのみ使⽤可能

Slide 44

Slide 44 text

Copyright © Sansan, Inc. All rights reserved. > 拡張関数の定義できる箇所 - publicな関数 - クラス内 - 関数内に定義する拡張関数 - interfaceに定義する

Slide 45

Slide 45 text

Copyright © Sansan, Inc. All rights reserved. > interface内で拡張関数を定義 interface MyInterface{ val Long.seconds get() = this * 1000 val Long.minutes get() = this.seconds * 60 }

Slide 46

Slide 46 text

Copyright © Sansan, Inc. All rights reserved. > interface内で拡張関数を定義 interface MyInterface{ val Int.seconds get() = this * 1000 val Int.minutes get() = this.seconds * 60 } class MyClass : MyInterface{ fun calc(){ val s = 20.seconds } } interfaceを実装したクラス内で使⽤可能

Slide 47

Slide 47 text

Copyright © Sansan, Inc. All rights reserved. > interface内で拡張関数を定義 interface MyInterface{ val Int.seconds get() = this * 1000 val Int.minutes get() = this.seconds * 60 } fun calcTime(f:MyInterface.() -> Int):Int = object : MyInterface{}.f()

Slide 48

Slide 48 text

Copyright © Sansan, Inc. All rights reserved. > interface内で拡張関数を定義 interface MyInterface{ val Int.seconds get() = this * 1000 val Int.minutes get() = this.seconds * 60 } fun calcTime(f:MyInterface.() -> Int):Int = object : MyInterface{}.f() レシーバー付き関数リテラル

Slide 49

Slide 49 text

Copyright © Sansan, Inc. All rights reserved. > interface内で拡張関数を定義 interface MyInterface{ val Int.seconds get() = this * 1000 val Int.minutes get() = this.seconds * 60 } fun calcTime(f:MyInterface.() -> Int):Int = object : MyInterface{}.f() fun calc(){ calcTime{ 30.minutes } }

Slide 50

Slide 50 text

Copyright © Sansan, Inc. All rights reserved. > interface内で拡張関数を定義 interface MyInterface{ val Int.seconds get() = this * 1000 val Int.minutes get() = this.seconds * 60 } fun calcTime(f:MyInterface.() -> Int):Int = object : MyInterface{}.f() fun calc(){ calcTime{ 30.minutes } } このラムダ式の中だけで拡張関数を使うことができる

Slide 51

Slide 51 text

Copyright © Sansan, Inc. All rights reserved. > まとめ - publicな拡張関数の利⽤するときは - 拡張関数を定義するクラスの場所、ファイル名のコンベ ンションをきめる。 - 同じ名前の拡張関数を定義しない - むやみに拡張関数を定義しない。 - クラス内、関数内、インターフェイス内の定義を使い、適 切なスコープで拡張関数の仕様が可能

Slide 52

Slide 52 text

$PQZSJHIU˜4BOTBO *OD"MMSJHIUTSFTFSWFE  4BOTBO͸Ұॹʹ৽͍͠Ձ஋Λ࡞͍ͬͯ͘ ஥ؒΛ͕͍ͯ͞͠·͢ɻ 3VCZ 3VCZPO3BJMT ʢ8FCΞϓϦέʔγϣϯʣ $ɼ"41/&5.7$ ʢ8FCΞϓϦέʔγϣϯʣ J04"OESPJEΞϓϦ   ݸਓ޲໊͚ࢗ؅ཧΞϓϦʮ&JHIUʯ   ໊ࢗσʔλԽ෼ࢄॲཧγεςϜ   ๏ਓ޲໊͚ࢗ؅ཧαʔϏεʮ4BOTBOʯ   ๏ਓ޲໊͚ࢗ؅ཧαʔϏε ʮ4BOTBOʯ   ݸਓ޲໊͚ࢗ؅ཧΞϓϦʮ&JHIUʯ ΤϯδχΞืूத 4BOTBO࠾༻ ݕࡧ SFDSVJU!TBOTBODPN·Ͱ ͓ؾܰʹ͝࿈བྷ͍ͩ͘͞ɻ ڵຯͷ͋Δํ͸

Slide 53

Slide 53 text

Copyright © Sansan, Inc. All rights reserved. > Enjoy Kotlin! 53