SlideShare uses cookies to improve functionality and performance, and to provide you with relevant advertising. If you continue browsing the site, you agree to the use of cookies on this website. See our User Agreement and Privacy Policy.
SlideShare uses cookies to improve functionality and performance, and to provide you with relevant advertising. If you continue browsing the site, you agree to the use of cookies on this website. See our Privacy Policy and User Agreement for details.
Successfully reported this slideshow.
Activate your 14 day free trial to unlock unlimited reading.
21.
処理を共通化する
1. abstract class
2. interfaceのデフォルト実装
3. class delegation
4. property delegation
5. 拡張関数
6. トップレベル関数
22.
abstract class vs. interface
abstract class interface
状態 持てる 持てない
継承元 classとinterface interfaceのみ
多重継承 できない できる
class method default method
final できる できない
protected できる できない
23.
abstract class vs. interface
abstract classが良いケース
● onCreateなどの継承部分で処理を共通化したい
● クラス外から呼ばれたくない
● 子クラスでoverrideされたくない
24.
abstract class vs. interface
interfaceのデフォルト実装が良いケース
● ベースクラスの肥大化を防ぎたい
● 多重継承したい
interfaceのデフォルト実装であっても、
class delegationを使えば状態を持つことができる
25.
class delegation
interfaceの実装を別クラスに委譲することができる
これによりinterfaceの特性を活かしつつ状態を持つことができる
class UserModel() : DefaultImpl by State() { ... }
class UserModel(s: State) : DefaultImpl by s { ... }
26.
状態を持ちつつも、一部の処理は子クラスで実装させたい場合
interface IState {
var state: String
}
interface DefaultImpl : IState {
fun abstract(): String
fun default() { ... }
}
class State : IState {
override var state: String = ""
}
class UserModel : DefaultImpl, IState by State() {
override fun abstract(): String {
return "concrete"
}
}
IState
State
DefaultImpl
UserModel
状態をインタフェースとして切り出し、
状態を実装したクラスを用意する
27.
property delegation
プロパティのgetter(とsetter)を別のクラスに委譲できる
var userId: String by MyClass()
37.
val と var
val はgetterのみで再代入できない(read-only)
var はgetterとsetterがあり再代入できる(mutable)
val a = 0
var b = 0
a = 1 // NG
b = 1 // OK
38.
List と MutableList
List は要素を変更するメソッドがない(read-only)
MutableList は要素を変更するメソッドがある(mutable)
val a = listOf(1, 2, 3)
val b = mutableListOf(1, 2, 3)
a.add(4) // NG
b.add(4) // OK
40.
どうやってMutableを避けるか
1. その var は本当に var である必要があるか?
2. その MutableList は本当に MutableList である必要があるか?
まずは1について考えます
41.
プロパティの優先順位
コンパイル時からずっと不変だ const val
インスタンス生成時に値が決まり不変だ
ある時点を過ぎると値が確定し、不変だ
ある時点を過ぎると値が確定し、可変だ
しかしNullableにはしたくない
val
lazy
lateinit var
var
YES
NO
※companion objectSTART
44.
ある時点を過ぎると値が確定し、不変だ
↓インスタンス生成時は intent が取れないのでクラッシュ
class MainActivity : AppCompatActivity() {
val articleId: String = intent.getStringExtra(ARTICLE_ID)
}
45.
ある時点を過ぎると値が確定し、不変だ
値は変わらないのに var で、しかもNullable
class MainActivity : AppCompatActivity() {
var articleId: String? = null
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
articleId = intent.getStringExtra(ARTICLE_ID)
}
}
46.
ある時点を過ぎると値が確定し、不変だ
lazy で書くと val かつNonNullにできる
class MainActivity : AppCompatActivity() {
val articleId: String by lazy {
intent.getStringExtra(ARTICLE_ID)
}
}
65.
2つのリストを同時に扱う
(例)対応関係のある名前と年齢のリストをそれぞれ出力
val nameList = listOf("Alice", "Bob", "Charlie")
val ageList = listOf(20, 25, 30)
Alice(20), Bob(25), Charlie(30)
66.
2つのリストを同時に扱う
val nameList = listOf("Alice", "Bob", "Charlie")
val ageList = listOf(20, 25, 30)
val size = minOf(nameList.size, ageList.size)
for (i in 0 until size) {
val name = nameList[i]
val age = ageList[i]
println("$name($age)")
}
67.
2つのリストを同時に扱う:zip
val nameList = listOf("Alice", "Bob", "Charlie")
val ageList = listOf(20, 25, 30)
nameList.zip(ageList) { name, age ->
println("$name($age)")
}
70.
変換と除外
val ids = listOf(...)
val fruits = mutableListOf<Fruit>()
for (id in ids) {
for (fruit in Fruit.values()) {
if (fruit.id == id) {
fruits.add(fruit)
}
}
}
return fruits
71.
変換と除外:map,filter
変換と除外をしているので、map と filter を使う
return ids.filter { id ->
Fruit.values().any { it.id == id }
}.map { id ->
Fruit.values().find { it.id == id }!!
}
72.
変換と除外:mapNotNull
変換と除外を同時に行うには、mapNotNull も有効
return ids.mapNotNull { id ->
Fruit.values().find { it.id == id }
}
75.
リストをN個ずつに分割
val idList = 'A'..'Z'
val charList = mutableListOf<MutableList<Char>>()
val count = idList.count()
for (i in 0 until count) {
if (i % 10 == 0) {
charList.add(mutableListOf())
}
charList.last().add(idList.elementAt(i))
}
return charList // [[A,B,……,J],[K,L,……,T],[U,V,……,Z]]
84.
クラスの拡張
クラスの場合は継承によって既存クラスを拡張できる
open class Animal {
fun foo() {}
}
class Dog : Animal() {
fun bar() {}
}
fun zoo() {
val dog = Dog()
dog.foo() // 元の機能も使えて
dog.bar() // 機能を拡張できる
}
94.
Nullable と NonNull
KotlinといえばNullabilityが嬉しい
val nullable: String? = null // OK
val nonNull: String = null // NG
nullable.length // NG
nullable!!.length // OK
nullable?.length // OK
nonNull.length // OK
95.
Nullableをどこで変換するか
APIがNullableで返してくる場合、
UIに渡す途中のどこまでNullableにするか
Infra Domain UI
Nullable Nullable
or
NonNull
or
Exception
Nullable
or
NonNull
or
Exception
112.
処理をまとめる
ローカル変数の方が優先なので注意が必要
(特にプロパティアクセス形式)
※我々のチームでは let と also だけ使うようにしています
var text = "" // この行があると
val button = Button(context).apply {
text = "hello"
}
button.text // "hello"にならない
117.
そのままdata classにすると
検索条件はこんな感じに → data class SearchCondition(
val area: Area?,
val station: Station?,
val location: Location?,
val query: String?,
val menu: Menu?,
val price: Price?
// ...
)
118.
「ありえない状態」ができてしまう
【ビジネスロジック】
● エリア/駅/緯度経度は排他
○ でもいずれか1つは必須
● 料金はメニュー指定時のみ
● etc……
data class SearchCondition(
val area: Area?,
val station: Station?,
val location: Location?,
val query: String?,
val menu: Menu?,
val price: Price?
// ...
)
120.
クラス化するほど関連がある場合
(例)sealed class で「エリアか駅か緯度経度」を表現する
sealed class Place {
class Area
class Station
class Location
}
data class SearchCondition(
// ...
val place: Place
// ...
)
121.
クラス化するほど関連がある場合
(例)data class で「料金はメニュー指定時のみ」を表現する
data class MenuPrice(
val menu: Menu,
val price: Price?
)
data class SearchCondition(
// ...
val menuPrice: MenuPrice?
// ...
)
122.
クラス化するほど関連がない場合
EitherやPairなどの一般的なクラスで表現する
sealed class → Either
data class → Pair
123.
Eitherを定義する
Eitherとは、LeftかRightの値を保持するクラス
sealed class Either<out L, out R> {
data class Left<out L>(val value: L) : Either<L, Nothing>()
data class Right<out R>(val value: R) : Either<Nothing, R>()
}
131.
状態数の分析
1. Kotlinを数式に変換する
2. 数式を展開する
3. ありえない項を削除する
4. 残った項をまとめる
5. 数式をKotlinに戻す
data class Target(
val a: A?,
val b: B?,
val c: C?
)
(A + 1)(B + 1)(C + 1)
132.
状態数の分析
1. Kotlinを数式に変換する
2. 数式を展開する
3. ありえない項を削除する
4. 残った項をまとめる
5. 数式をKotlinに戻す
ABC + AB + BC + CA + A + B + C + 1
(A + 1)(B + 1)(C + 1)
133.
状態数の分析
1. Kotlinを数式に変換する
2. 数式を展開する
3. ありえない項を削除する
4. 残った項をまとめる
5. 数式をKotlinに戻す
AB + A + C + 1
ABC + AB + BC + CA + A + B + C + 1
【ビジネスロジック】
134.
状態数の分析
1. Kotlinを数式に変換する
2. 数式を展開する
3. ありえない項を削除する
4. 残った項をまとめる
5. 数式をKotlinに戻す
A(B + 1) + C + 1
AB + A + C + 1
135.
状態数の分析
1. Kotlinを数式に変換する
2. 数式を展開する
3. ありえない項を削除する
4. 残った項をまとめる
5. 数式をKotlinに戻す
A(B + 1) + C + 1
data class Target(
val abc: Either<Pair<A, B?>, C>?
)
136.
data class Target(
val abc: Either<Pair<A, B?>, C>?
)
137.
状態数の分析
1. Kotlinを数式に変換する
2. 数式を展開する
3. ありえない項を削除する
4. 残った項をまとめる
5. 数式をKotlinに戻す
6. 適宜 sealed class や data class を使って意味付けをする
data class Target(
val abc: Either<Pair<A, B?>, C>?
)
138.
状態数の分析
1. Kotlinを数式に変換する
2. 数式を展開する
3. ありえない項を削除する
4. 残った項をまとめる
5. 数式をKotlinに戻す
6. 適宜 sealed class や data class を使って意味付けをする
139.
まとめ
● クラスの肥大化で「ありえない状態」が生まれやすい
● 「ありえない状態」はバグの温床なので避ける
● 状態数を分析すると厳密に精査できる
● Either や Pair を使って状態を整理しつつ、
sealed class や data class で適宜意味付けをする