Chapter 3 ์ฌ์ฌ์ฉ์ฑ
์ฌ์ฌ์ฉ์ฑ์ ๊ทธ๋งํผ์ ํ์ด ์๋ ๋งํผ ๊ต์ฅํ ์ํํ ์๋ ์๋ค.
A ์ B์์ ๊ณตํต ๋ถ๋ถ์ ์ถ์ถํ๋ค๋ฉด, ์ดํ์ ๊ณตํต ๋ถ๋ถ์ ์์ ํ ์ผ์ด ์์๋ ํ๊บผ๋ฒ์ ์์ ํ ์ ์๋ค. ํ์ง๋ง A๋ฅผ ๋์์ผ๋ก ์์ ํ๊ฒ์ด B์์ ๋ฌธ์ ๊ฐ ๋ ์ ์๊ณ , ๊ทธ ๋ฐ๋๋ก B๋ฅผ ๋์์ผ๋ก ์์ ํ ๊ฒ์ด A์์ ๋ฌธ์ ๊ฐ ๋ ์ ์๋ค.
์ฌ์ฌ์ฉ์ฑ์ ๊ณ ๋ คํ๋ ์ผ์ ์๊ฐ๋ณด๋ค ์ด๋ ต๊ณ , ๋ค์ํ ์ค๋ฅ๋ฅผ ๋ฐ์์ํฌ ์ ์๋ค.
Item 19 knowledge ๋ฅผ ๋ฐ๋ณตํ์ฌ ์ฌ์ฉํ์ง ๋ง๋ผ
ํ๋ก์ ํธ์์ ์ด๋ฏธ ์๋ ์ฝ๋๋ฅผ ๋ณต์ฌ/๋ถ์ฌ๋ฃ๊ธฐ ํ๊ณ ์๋ค๋ฉด, ๋ฌด์ธ๊ฐ๊ฐ ์๋ชป๋ ๊ฒ์ด๋ค.
knowledge
ํ๋ก๊ทธ๋๋ฐ์์๋ knowledge๋ ๋์ ์๋ฏธ๋ก '์๋์ ์ธ ์ ๋ณด'๋ฅผ ๋ปํจ
์ฐ๋ฆฌ๊ฐ ์ฌ์ฉํ๋ ํ๋ก๊ทธ๋จ์์ knowledge ๋ฅผ ๋ฝ๋๋ค๋ฉด ๋ค์ ๋๊ฐ์ง๋ก ๋๋ ์ ์์
- ๋ก์ง(logic): ํ๋ก๊ทธ๋จ์ด ์ด๋ ํ ์์ผ๋ก ๋์ํ๋์ง ํ๋ก๊ทธ๋จ์ด ์ด๋ป๊ฒ ๋ณด์ด๋์ง
- ๊ณตํต ์๊ณ ๋ฆฌ์ฆ(common algorithm): ์ํ๋ ๋์์ ํ๊ธฐ ์ํ ์๊ณ ๋ฆฌ์ฆ
์ ๋์ ๊ฐ์ฅ ํฐ ์ฐจ์ด์ ์ ์๊ฐ์ ๋ฐ๋ฅธ ๋ณํ์ด๋ค.
๋น์ง๋์ค ๋ก์ง์ ์๊ฐ์ด ์ง๋๋ฉด์ ๊ณ์ ๋ณํ์ง๋ง, ๊ณตํต ์๊ณ ๋ฆฌ์ฆ์ ํ๋ฒ ์ ์๋ ์ดํ์๋ ํฌ๊ฒ ๋ณํ์ง ์๋๋ค.
๋ชจ๋ ๊ฒ์ ๋ณํํ๋ค
ํ๋ก๊ทธ๋จ์์ ์ ์ผํ๊ฒ ์ ์ง๋๋ ๊ฒ์ '๋ณํํ๋ค๋ ์์ฑ' ์ด๋ค.
์ค๋๋ ์ ๋๋ถ๋ถ์ ํ๋ก์ ํธ๋ ๋ช ๋ฌ๋ง๋ค ์๊ตฌ ์ฌํญ๊ณผ ๋ด๋ถ์ ์ธ ๊ตฌ์กฐ๋ฅผ ๊ณ์ํด์ ๋ณ๊ฒฝํ๋ค.
๋ชจ๋ ๊ฒ์ ๋ณํํ๊ณ ์ฐ๋ฆฌ๋ ์ด์ ๋๋นํด์ผ ํ๋ค. ๋ณํํ ๋ ๊ฐ์ฅ ํฐ ์ ์ knowledge๊ฐ ๋ฐ๋ณต๋์ด ์๋ ๋ถ๋ถ์ด๋ค.
๋ฒํผ ๋์์ธ์ ๋ณ๊ฒฝ ๊ฑด
ํ๋ก์ ํธ ์ฌ๋ฌ ๊ณณ์์ ์ฌ์ฉ๋๊ณ ์๋ ๋ฒ์ฉ์ ์ธ ๋ฒํผ์ด ์๋ค. ๊ทธ๋ํฝ ๋์์ด๋๊ฐ ์ด ๋ฒํผ ๋ชจ์์ผ๋ก ๋ณ๊ฒฝํด์ผ ํ๋ค๊ณ ๊ฒฐ์ ํ์๋ค. ์ด ๋ฒํผ๋ชจ์์ด ์ ์ฉ๋ ๋ชจ๋ ๋ถ๋ถ์ ํ๋ํ๋ ์์ ํด์ผ ํ๋ค. ๋ํ ์ ์ฉ ๊ณผ์ ์ ์ค์ํ ๋ถ๋ถ์ ์ฐพ๊ธฐ ์ํด ํ ์คํฐ์๊ฒ ํ ์คํธ๋ฅผ ์์ฒญํด์ผ ํ๋ค.
๋ฐ์ดํฐ๋ฒ ์ด์ค ํ ์ด๋ธ ์ด๋ฆ ๋ณ๊ฒฝ ๊ฑด
๋ฐ์ดํฐ๋ฒ ์ด์ค๋ฅผ ์ฌ์ฉํ๋ ํ๋ก์ ํธ์์ ํ ์ด๋ธ ์ด๋ฆ์ ๋ณ๊ฒฝํด์ผ ํ๋ค ์ด ํ ์ด๋ธ์ ์์กดํ๊ณ ์๋ ๋ชจ๋ SQL๊ตฌ๋ฌธ์ ๋ณ๊ฒฝ์ด ํ์ํ๋ค ํน์๋ ํ ๋ถ์์์ ์ด๋ฅผ ๋์น๊ณ ์๋ค๋ฉด ๋งค์ฐ ์ํํ ์ ์๋ค.
knowledge ๋ฐ๋ณต์ด ์ผ๋ง๋ ์ํํ๊ณ ๋ฌธ์ ๊ฐ ์๋์ง ์ ์ ์๋ค. knowledge ๋ฐ๋ณต์ ํ์ฅ์ฑ(scalable)์ ๋ง๊ณ , ์ฝ๊ฒ ๊นจ์ง๊ฒ(fragile) ๋ง๋ ๋ค.
๊ฐ๋ฐ์๋ knowledge ๋ฐ ๋ณต์ ์ค์ผ ์ ์๋ ๋๊ตฌ์ ๊ธฐ๋ฅ๋ค์ ํ์ฉํด์ผ ํ๋ค.
ORM(Object Relational Mapping), DAO(Data Access Object) ๋ฅผ ํ์ฉํ๋ ๋ฐฉ๋ฒ์ด ์๋ค.
์ฌ๋ฌ ์ถ์ํ๋ฅผ ํํํ ์ ์๋ ์๋ง์ ์๋ฃจ์ ์ด ์์ผ๋ฉฐ ์ด๋ฅผ ํ์ฉํ๋ฉด ๋ฐ๋ณต์ ์ค์ผ ์ ์๋ค.
์ธ์ ์ฝ๋๋ฅผ ๋ฐ๋ณตํด๋ ๋ ๊น?
knowledge ๋ฅผ ์ถ์ถํ์ฌ ๋ฐ๋ณต์ ์ ๊ฑฐํ๋ ๊ฒฝ์ฐ, ์ฌ๋ฐ๋ฅด์ง(์ ์คํ์ง) ๋ชปํ ์ถ์ถ์ ๋ณ๊ฒฝ์ ๋ ์ด๋ ต๊ฒ ๋ง๋ ๋ค.
๋น์ง๋์ค ๊ท์น์ด ๋ค๋ฅธ ๊ณณ(source) ์์ ์๋์ง ํ์ธํ๋ ๋ฐฉ๋ฒ์ด ์๋ค. ๋ค๋ฅธ๊ณณ์์ ์๋ค๋ฉด, ๋ ๋ฆฝ์ ์ผ๋ก ๋ณ๊ฒฝ๋ ๊ฐ๋ฅ์ฑ์ด ๋๋ค.
๋จ์ผ ์ฑ ์ ์์น (SRP: Single Responsibility Principle)์ ํ์ฉํ๋ฉด ์๋ชป๋ ์ฝ๋ ์ถ์ถ๋ก ๋ถํฐ ๋ณดํธํ ์ ์๋ค.
๋จ์ผ ์ฑ ์ ์์น
๋จ์ผ ์ฑ ์ ์์น์ด๋ 'ํด๋์ค๋ฅผ ๋ณ๊ฒฝํ๋ ์ด์ ๋ ๋จ ํ๊ฐ์ง์ฌ์ผ ํ๋ค(A class should have only one reason to change)'
๋จ์ผ ์ฑ ์์ด ์๋ ค์ฃผ๋ ๋๊ฐ์ง ์ฌ์ค์ ๋ค์๊ณผ ๊ฐ๋ค.
- ์๋ก ๋ค๋ฅธ ๊ณณ์์ ์ฌ์ฉํ๋ knowledge๋ ๋
๋ฆฝ์ ์ผ๋ก ๋ณ๊ฒฝํ ๊ฐ๋ฅ์ฑ์ด ๋ง๋ค.
๋น์ทํ ์ฒ๋ฆฌ๋ฅผ ํ๋๋ผ๋, ์์ ํ ๋ค๋ฅธ knowledge๋ก ์ทจ๊ธํ๋ ๊ฒ์ด ์ข๋ค. - ๋ค๋ฅธ knowledge๋ก ๋ถ๋ฆฌํด๋๋ ๊ฒ์ด ์ข๋ค.
๊ทธ๋ ์ง ์์ผ๋ฉด, ์ฌ์ฌ์ฉํด์ ์๋๋ ๋ถ๋ถ์ ์ฌ์ฌ์ฉํ๋ ค๋ ์ ํน(์ํ)์ด ๋ฐ์ํ ์ ์๋ค.
์ ๋ฆฌ
๋ชจ๋ ๊ฒ์ ๋ณํํ๋ค. ๊ณตํต knowledge๊ฐ ์๋ค๋ฉด, ์ด๋ฅผ ์ถ์ถํด์ ์ด๋ฌํ ๋ณํ์ ๋๋นํด์ผ ํ๋ค.
์ฌ๋ฌ ์์์ ๋น์ทํ ๋ถ๋ถ์ด ์๋ ๊ฒฝ์ฐ, ๋ณ๊ฒฝ์ด ํ์ํ ๋ ์ค์๊ฐ ๋ฐํํ ์ ์๋ ๋ถ๋ถ์ ์ถ์ถํ๋๊ฒ์ด ์ข๋ค.
์๋ํ์ง ์๋ ์์ ์ ํผํ๋ ค๋ฉด, ํน์ ๋ค๋ฅธ๊ณณ์์ ์กฐ์ํ๋ ๋ถ๋ถ์ด ์๋ค๋ฉด ๋ถ๋ฆฌํด์ ์ฌ์ฉํ๋ ๊ฒ์ด ์ข๋ค.
Item 20 ์ผ๋ฐ์ ์ธ ์๊ณ ๋ฆฌ์ฆ์ ๋ฐ๋ณตํด์ ๊ตฌํํ์ง ๋ง๋ผ
์ด๋ฏธ ์๋ ๋ผ์ด๋ธ๋ฌ๋ฆฌ ํน์ ์๊ณ ๋ฆฌ์ฆ์ ํ์ฉํ๋ฉด ๋ค์๊ณผ ๊ฐ์ ์ด์ ์ด ์๋ค.
- ์ฝ๋ ์์ฑ์ด ๋นจ๋ผ์ง๋ค.
ํธ์ถ์ ํ๋ฒ ํ๋๊ฒ์ด ์๊ณ ๋ฆฌ์ฆ์ ๋ง๋๋ ์๊ฐ ๋๋น ๋น ๋ฅด๋ค. - ๊ตฌํ์ ๋ฐ๋ก ํ์ง ์์๋, ํจ์์ ์ด๋ฆ์ ๋ณด๊ณ ๋ฌด์์ ํ๋์ง ํ์คํ๊ฒ ์ ์ ์๋ค.
- ์ง์ ๊ตฌํํ ๋ ๋ฐ์ํ ์ ์๋ ์ค์๋ฅผ ์ค์ผ์ ์๋ค.
- ํ๋ฒ์ ์ต์ ํ๋ก ์ด๋ฅผ ์ฌ์ฉํ๋ ๋ชจ๋ ํจ์์์ ๋์ผํ ์ต์ ํ ํํ์ ๋ฐ์ ์ ์๋ค.
ํ์ค ๋ผ์ด๋ธ๋ฌ๋ฆฌ ์ดํด๋ณด๊ธฐ
override fun saveCallResult(item: SourceResponse) {
var sourceList = ArrayList<SourceEntity>()
item.sources.forEach {
var sourceEntity = SourceEntity()
sourceEntity.id = it.id
sourceEntity.category = it.category
sourceEntity.country = it.country
sourceEntity.description = it.description
sourceEntity.add(sourceEntity)
}
}
์ ํํ๋ณด๋ค๋ ํฉํ ๋ฆฌ ๋ฉ์๋๋ฅผ ํ์ฉํ๊ฑฐ๋, ๊ธฐ๋ณธ ์์ฑ์๋ฅผ ํ์ฉํ๋ ๊ฒ์ด ์ข๋ค.
๊ทธ๋๋ ์จ์ผ ๊ฒ ๋ค๋ฉด .apply
๋ก ํ์ฉํด์ ๋ชจ๋ ๋จ์ผ ๊ฐ์ฒด๋ค์ ํ๋กํผํฐ๋ฅผ ์๋ฌต์ ์ผ๋ก ์ค์ ํ๋ ๊ฒ์ด ์ข๋ค.
override fun saveCallResult(item: SourceResponse) {
val srouceEntries = item.sources.map(::sourceToEntry)
db.insertSources(srouceEntries)
}
private fun sourceToEntry(source: Source) = SourceEntity()
.apply {
id = source.id
category = source.category
country = source.country
description = source.description
}
๋๋ง์ ์ ํธ๋ฆฌํฐ ๊ตฌํํ๊ธฐ
๋์ผํ ๊ฒฐ๊ณผ๋ฅผ ์ป๋ ํจ์๋ฅผ ์ฌ๋ฌ ๋ฒ ๋ง๋๋ ๊ฒ์ ์๋ชป๋ ์ผ์ด๋ค.
๋ชจ๋ ํจ์๋ ํ ์คํธ๋์ด์ผ ํ๊ณ , ๊ธฐ์ต๋์ด์ผ ํ๋ฉฐ, ์ ์ง๋ณด์๋์ด์ผ ํ๋ค.
ํ์์๋ ํจ์๋ฅผ ์ค๋ณตํด์ ๋ง๋ค์ง ์๊ฒ, ๊ธฐ์กด์ ๊ด๋ จ๋ ํจ์๊ฐ ์๋์ง ํ์ํ๋ ๊ณผ์ ์ด ํ์ํ๋ค.
ํ์ฅํจ์ (Extension Function)
ํด๋์ค๋ฅผ ํ์ฅํ๋ฉด์ ์๋ก์ด ํจ์๋ค์ ๋ง์น ์ธ๋ถ ๋ผ์ด๋ธ๋ฌ๋ฆฌ ํด๋์ค๊ฐ ์ ๊ณตํ๋ ์๋ ํจ์์ธ ๊ฒ ๋ง๋ฅ ์ฌ์ฉํ ์ ์๋ค.
- ์์ทจ์ธ ํ์ (receiver type) : ํ์ฅ ํจ์๋ฅผ ์ถ๊ฐํ ํด๋์ค
- ์์ทจ์ธ ๊ฐ์ฒด(receiver object) : ํ์ฅ ํจ์ ๋ด๋ถ๋ฅผ ๊ตฌํํ ๋ receiver type ์ด ๊ฐ์ง๊ณ ์๋ ์ธ์คํด์ค์ ์ ๊ทผํ ๊ฐ์ฒด
fun Iterable<Int>.product() =
fold(1) { acc, i -> acc * i }
ํ์ฅํจ์๋ฅผ ์ด์ฉํ์ฌ ๋ง๋ ์ฝ๋๋ ์ ์ ๋ฐ์ธ๋ฉ์ด ๋๋ค.
ํ์ฅํจ์์ ์ด์ ์ ๋ค์๊ณผ ๊ฐ๋ค.
- ํจ์๋ ์ํ๋ฅผ ์ ์งํ์ง ์์ผ๋ฏ๋ก, ํ์๋ฅผ ๋ํ๋ด๊ธฐ ์ข๋ค.
ํนํ ๋ถ์ ํจ๊ณผ(side-effect) ์๋ ๊ฒฝ์ฐ ๋ ์ข๋ค. - ํ์ฅํจ์๋ ๊ตฌ์ฒด์ ์ธ ํ์ ์ด ์๋ ๊ฐ์ฒด์๋ง ์ฌ์ฉ์ ์ ํํ ์ ์์ผ๋ฏ๋ก ์ข๋ค.
- ์์ ํ ๊ฐ์ฒด๋ฅผ ์๊ท๋จผํธ๋ก ์ ๋ฌ๋ฐ์ ์ฌ์ฉํ๋ ๊ฒ๋ณด๋ค๋ ํ์ฅ ๋ฆฌ์๋ฒ๋ก ์ฌ์ฉํ๋ ๊ฒ์ด ๊ฐ๋ ์ฑ ์ธก๋ฉด์์ ์ข๋ค.
- ํ์ฅ ํจ์๋ ๊ฐ์ฒด์ ์ ์ํ ํจ์๋ณด๋ค ๊ฐ์ฒด๋ฅผ ์ฌ์ฉํ ๋, ์๋ ์์ฑ ๊ธฐ๋ฅ๋ฑ์ผ๋ก ์ ์์ด ์ด๋ฃจ์ด์ง๋ฏ๋ก ์ฝ๊ฒ ์ฐพ์ ์ ์๋ค.
์ ๋ฆฌ
์ผ๋ฐ์ ์ธ ์๊ณ ๋ฆฌ์ฆ์ ๋ฐ๋ณตํด์ ๋ง๋ค์ง ๋ง๋ผ.
ํน์ ์๊ณ ๋ฆฌ์ฆ์ ๋ฐ๋ณตํด์ ์ฌ์ฉํด์ผ ํ๋ ๊ฒฝ์ฐ์๋ ํ๋ก์ ํธ ๋ด๋ถ์ ์ ์ํ๋ผ. ์ด๋ฌํ ์ฝ๋๋ ํ์ฅ ํจ์๋ก ์ ์ํ๋ ๊ฒ์ด ์ข๋ค.
Item 21 ์ผ๋ฐ์ ์ธ ํ๋กํผํฐ ํจํด์ ํ๋กํผํฐ ์์์ผ๋ก ๋ง๋ค์ด๋ผ
์ฝํ๋ฆฐ์์๋ ์ฝ๋ ์ฌ์ฌ์ฉ๊ณผ ๊ด๋ จํด์ ํ๋กํผํฐ ์์๊ธฐ๋ฅ์ ์ ๊ณตํ๋ค.
ํ๋กํผํฐ ์์์ ์ฌ์ฉํ๋ฉด ์ผ๋ฐ์ ์ธ ํ๋กํผํฐ์ ํ์๋ฅผ ์ถ์ถํด์ ์ฌ์ฌ์ฉํ ์ ์๋ค.
ํ๋กํผํฐ ์์์ ํด๋์ค ์์๊ณผ ๋น์ทํ๊ฒ ํ๋กํผํฐ ๊ฐ์ get
/set
์์์ ์ฌ์ฉํ ์ ์๋ค.
by
ํค์๋๋ฅผ ์ด์ฉํ์ฌ ์์ํ ๊ฐ์ฒด๋ฅผ ํ๋กํผํฐ ๋ค์ ๋ช
์ํ๋ค.
import kotlin.reflect.KProperty
class PropertyDelegate(var value: String) {
operator fun getValue(thisRef: Any?, property: KProperty<*>): String {
println("${property.name} get value ${this.value}")
return value
}
operator fun setValue(thisRef: Any?, property: KProperty<*>, newValue: String) {
println("${property.name} set value ${this.value} -> $newValue")
this.value = newValue
}
}
class Person {
var name: String by PropertyDelegate("no name")
val age: String by PropertyDelegate("31")
}
fun main() {
val person = Person()
println("person name is ${person.name}") // person name is no name
person.name = "jin"
println("person name is ${person.name}") // person name is jin
println("person age is ${person.age}") // person age is 31
}
์ฝํ๋ฆฐ์ stdlib
๋ lazy ํ๋กํผํฐ ํจํด์ ์ฝ๊ฒ ๊ตฌํํ ์ ์๊ฒ lazy ํจ์๋ฅผ ์ ๊ณตํ๋ค.
fun getPostListFromServer(id: Int): List<Post> {
println("get big data from server")
return listOf(Post())
}
class Post
class Board(val id: Int) {
val list: List<Post> by lazy { getPostListFromServer(this.id) }
}
fun main() {
val board = Board(1)
println("lazy init board list :: ${board.list}") // lazy init board list :: [Post@a09ee92]
println("board list :: ${board.list}") // board list :: [Post@a09ee92]
}
// get big data from server
// lazy init board list :: [Post@2f4d3709]
// board list :: [Post@2f4d3709]
ํ๋กํผํฐ ์์์ ์ฌ์ฉํ๋ฉด, ์ด์ธ์ ๋ณํ๊ฐ ์์ ๋ ์ด๋ฅผ ๊ฐ์งํ๋ observable ํจํด์ ์ฝ๊ฒ ๋ง๋ค ์ ์๋ค.
var items: List<Item> by
Delegates.observable(listOf()) { _, _, _ ->
notifyDataSetChanged()
}
val key: String? by
Delegates.observable(null) { _, old, new ->
Log.e("key changed from $old to $new")
}
ํน์ ์ผ์ ์กฐ๊ฑด์ ๋ง์กฑํ์ง ๋ชปํ๋ฉด ๊ฐ ํ ๋น์ ์ ํํ๋ ๊ฑฐ๋ถ๊ถ(vetoable)์ ํ์ฌํ ์ ์๋ค.
import kotlin.properties.Delegates
class Person {
// 200์ด ์ด์์ ๊ฐ ์ค๋ฅ๋ก ํ๋จ ๊ฐ ์
ํ
์ ๊ฑฐ๋ถํ๋ค.
var age: Int by Delegates.vetoable(0) { property, oldValue, newValue ->
newValue < 200
}
}
fun main() {
val person = Person()
person.age = 31
println("person age is ${person.age}") // person age is 31
person.age = 9999 // ๊ฑฐ๋ถ๋จ
println("person age is ${person.age}") // person age is 31
}
์ฝํ๋ฆฐ stdlib
์์ ๋ค์๊ณผ ๊ฐ์ ํ๋กํผํฐ ๋ธ๋ฆฌ๊ฒ์ดํฐ๋ฅผ ์์๋๋ฉด ์ข๋ค.
- lazy
- Delegates.observable
- Delegates.vetoable
- Delegates.notNull
์ ๋ฆฌ
ํ๋กํผํฐ ๋ธ๋ฆฌ๊ฒ์ดํธ๋ ํ๋กํผํฐ์ ๊ด๋ จ๋ ๋ค์ํ ์กฐ์์ ํ ์ ์์ผ๋ฉฐ, ์ปจํ ์คํธ์ ๊ด๋ จ๋ ๋๋ถ๋ถ์ ใ ๋๋ค.
์ด๋ฌํ ํน์ง์ ์ด์ฉํด์ ๋ค์ํ ํ๋กํผํฐ์ ๋์์ ์ถ์ถํด์ ์ฌ์ฌ์ฉํ ์ ์๋ค.
์ด๋ฅผ ์ ํ์ฉํ๋ฉด ์ผ๋ฐ์ ์ธ ํจํด์ ์ถ์ถํ๊ฑฐ๋ ๋ ์ข์ API ๋ฅผ ๋ง๋ค ๋ ํ์ฉํ ์ ์๋ค.
์ฐธ๊ณ ์๋ฃ
Item 22 ์ผ๋ฐ์ ์ธ ์๊ณ ๋ฆฌ์ฆ์ ๊ตฌํํ ๋ ์ ๋ค๋ฆญ์ ์ฌ์ฉํ๋ผ
์ ๋ค๋ฆญ ์ ํ
ํ์ ํ๋ผ๋ฏธํฐ์ ์ค์ํ ๊ธฐ๋ฅ ์ค ํ๋๋ ๊ตฌ์ฒด์ ์ธ ํ์ ์ ์๋ธํ์ ๋ง ์ฌ์ฉํ๊ฒ ํ์ ์ ์ ํํ๋ ๊ธฐ๋ฅ์ด๋ค.
fun <T: Compareable<T>> Iterable<T>.sorted(): List<T> {
/*...*/
}
fun <T, C: MutableCollection<in T>> Iterable<T>.toCollection(destination: C): C {
/*...*/
}
class ListAdapter<T: ItemAdapter>(/*...*/) { /*...*/ }
ํ์ ์ ์ ํ์ด ๊ฑธ๋ฆฌ๋ฏ๋ก, ๋ด๋ถ์์ ํด๋น ํ์ ์ด ์ ๊ณตํ๋ ๋ฉ์๋๋ฅผ ์ฌ์ฉํ ์ ์๋ค.
์ ๋ฆฌ
์ฝํ๋ฆฐ ์๋ฃํ ์์คํ ์์ ํ์ ํ๋ผ๋ฏธํฐ๋ ๊ต์ฅํ ์ค์ํ ๋ถ๋ถ์ด๋ค.
์ผ๋ฐ์ ์ผ๋ก ์ด๋ฅผ ์ฌ์ฉํด์ type-safe ์ ๋ค๋ฆญ ์๊ณ ๋ฆฌ์ฆ๊ณผ ์ ๋ค๋ฆญ ๊ฐ์ฒด๋ฅผ ๊ตฌํํ๋ค.
๋ํ ํ์ ํ๋ผ๋ฏธํฐ๋ ๊ตฌ์ฒด ์๋ฃํ(concrete type)์ ์๋ธํ์ ์ ์ ํํ ์ ์๋ค.
Item 23 ํ์ ํ๋ผ๋ฏธํฐ์ ์๋์์ ํผํ๋ผ
์๋ ์ฝ๋ ์ฒ๋ผ ํ๋กํผํฐ์ ํ๋ผ๋ฏธํฐ๊ฐ ๊ฐ์ ์ด๋ฆ์ ๊ฐ์ง ์ ์๋ค.
class Forest(val name: String) {
fun addTree(name: String) {
// ...
}
}
์์ ๊ฐ์ ์ฝ๋์์ ์ธ๋ถ์ ์๋ ํ๋กํผํฐ๊ฐ ์ง์ญ ํ๋ผ๋ฏธํฐ์ ์ํด ๊ฐ๋ ค์ง๊ฒ ๋๋๋ฐ ์ด๋ฅผ ์๋์(shadowing) ์ด๋ผ ๋ถ๋ฅธ๋ค.
์ ๋ฆฌ
ํ์ ํ๋ผ๋ฏธํฐ์ ์๋์์ ์ดํดํ๊ธฐ๋ ์ด๋ ต๊ธฐ ๋๋ฌธ์ ๋ฐ๋์ ํผํด์ผ ํ๋ค.
Item 24 ์ ๋ค๋ฆญ ํ์ ๊ณผ variance ํ์ ์๋ฅผ ํ์ฉํ๋ผ.
๋ค์๊ณผ ๊ฐ์ ์ฝ๋๊ฐ ์๋ค.
class Cup<T>
์ฌ๊ธฐ์ ํ์
ํ๋ผ๋ฏธํฐ T
๋ variance
ํ์ ์(in ํน์ out)๊ฐ ์์ผ๋ฏ๋ก invariant(๋ถ๊ณต๋ณ) ํ์
์ด๋ค.
invariant ํ์ ์ ์ ๋ค๋ฆญ ํ์ ์ผ๋ก ๋ง๋ค์ด์ง๋ ํ์ ๋ค์ด ์๋ก ๊ด๋ จ์ด ์๋ค.
๋ง์ฝ ์ด๋ ํ ๊ด๋ จ์ฑ์ ์ํ๋ค๋ฉด variance ํ์ ์๋ฅผ ๋ถ์ด๋ฉฐ out
์ ํ์
ํ๋ผ๋ฏธํฐ๋ฅผ covariant(๊ณต๋ณ์ฑ) ํ์
์ผ๋ก ๋ง๋ ๋ค.
in
์ ํ์
ํ๋ผ๋ฏธํฐ๋ฅผ contravariant(๋ฐ๊ณต๋ณ)์ผ๋ก ๋ง๋ ๋ค.
PECS ๊ฐ ์ด ์ฑ๊ฒฉ์ ๋ฐ๋ฅธ๋ค.
ํจ์ ํ์
ํ๋ผ๋ฏธํฐ ์ ํ๊ณผ ๋ฆฌํดํ์ ์ ๋ฐ๋ผ ์๋ก ์ด๋ ํ ๊ด๊ณ๋ฅผ ๊ฐ๋๋ค.
์ ๊ทธ๋ฆผ์์ ๊ณ์ธต ๊ตฌ์กฐ์ ์๋๋ก ๊ฐ๋ฉด, ํ์ดํ ์์คํ ๊ณ์ธต์์ ํ๋ผ๋ฏธํฐ ํ์ ์ด ๋ ๋์ ํ์ ์ผ๋ก ์ด๋ํ๊ณ , ๋ฆฌํด ํ์ ์ ๊ณ์ธต ๊ตฌ์กฐ์ ๋ ๋ฎ์ ํ์ ์ผ๋ก ์ด๋ํ๋ค.
์ฝํ๋ฆฐ์ ํจ์ ํ์ ์ ๋ชจ๋ ํ๋ผ๋ฏธํฐ ํ์ ์ contravariant ์ด๋ค.
๋ชจ๋ ๋ฆฌํด ํ์ ์ covariant ์ด๋ค.
variance ํ์ ์์ ์์ ์ฑ
์๋ฐ์ ๋ฐฐ์ด์ covariant ์ด๋ค.
๋ค์ ์ฝ๋๋ ์ปดํ์ผ์ ๋๋ ๋ฐํ์๋ ์๋ฌ๊ฐ ๋๋ค.
Integer[] numbers = {1, 4, 2, 1};
Object[] objects = numbers;
objects[2] = "B" // Runtime Exception
์ฝํ๋ฆฐ์์๋ ์์ ๊ฐ์ ์ฝ๋ ๊ฒฐํจ์ ํด์ํ๊ธฐ ์ํด Array(IntArray, CharArray ๋ฑ)๋ฅผ invariant ๋ก ๋ง๋ค์๋ค.
ํ๋ผ๋ฏธํฐ ํ์ ์ ์์ธกํ ์ ์๋ค๋ฉด, ์ด๋ค ์๋ธํ์ ์ด๋ผ๋ ์ ๋ฌํ ์ ์๋ค.
์๋์ ๊ฐ์ด ์๊ท๋จผํธ๋ฅผ ์ ๋ฌํ ๋, ์๋ฌต์ ์ผ๋ก ์ ์บ์คํ ํ ์ ์๋ค.
open class Dog
class Puppy: Dog()
class Hound: Dog()
fun takeDog(dog: Dog) {}
takeDog(Dog())
takeDog(Puppy())
takeDog(Hound())
์๋์ ๊ฐ์ ์ํฉ์ ์์ ํ์ง ์๋ค.
์บ์คํ ํ ์ค์ง์ ์ธ ๊ฐ์ฒด๊ฐ ๊ทธ๋๋ก ์ ์ง๋๊ณ , ํ์ดํ ์์คํ ์์๋ง ๋ค๋ฅด๊ฒ ์ฒ๋ฆฌ๋๊ธฐ ๋๋ฌธ์ด๋ค.
class Box<out T> {
private var value: T? = null
// ์ฝํ๋ฆฐ์์ ๋ถ๊ฐ๋ฅํ ์ฝ๋
fun set(value: T) {
this.value = value
}
fun get(): T = value ?: error("Value not set")
}
val puppyBox = Box<Puppy>()
val dogBox: Box<Dog> = puppyBox
dogBox.set(Hound()) // Puppy ๋ง ๊ฐ๋ฅ
val dogHouse = Box<Dog>()
val box: Box<Any> = dogHouse
box.set("Some string") // Dog ๋ง ๊ฐ๋ฅ
box.set(42) // Dog ๋ง ๊ฐ๋ฅ
variance ํ์ ์ ์์น
varinace ํ์ ์๋ ํฌ๊ฒ ๋ ์์น์ ์ฌ์ฉํ ์ ์๋ค.
์ฒซ๋ฒ์งธ๋ ์ ์ธ๋ถ๋ก ์ผ๋ฐ์ ์ผ๋ก ์ด ์์น์ ์ฌ์ฉ๋๋ค.
์ด๋ ํด๋์ค์ ์ธํฐํ์ด์ค๊ฐ ์ฌ์ฉ๋๋ ๋ชจ๋ ๊ณณ์ ์ํฅ์ ์ค๋ค.
class Box<out T>(val value: T)
val boxStr: Box<String> = Box("Str")
val boxAny: Box<Any> = boxStr
๋๋ฒ์งธ๋ ํด๋์ค์ ์ดํฐํ์ด์ค๋ฅผ ํ์ฉํ๋ ์์น์ด๋ค.
์ด ์์น์ variance ํ์ ์๋ฅผ ์ฌ์ฉํ๋ฉด ํน์ ํ ๋ณ์์๋ง variance ํ์ ์๊ฐ ์ ์ฉ๋๋ค.
class Box<T>(val value: T)
val boxStr: Box<String> = Box("Str")
val boxAny: Box<out Any> = boxStr
์ ๋ฆฌ
- ํ์ ํ๋ผ๋ฏธํฐ์ ๊ธฐ๋ณธ์ ์ธ ๋์์ invariant ํ์ ์ด๋ค.
- in ํ์ ์๋ ํ์ ํ๋ผ๋ฏธํฐ๋ฅผ covariant ํ๊ฒ ๋ง๋ ๋ค.
- out ํ์ ์๋ ํ์ ํ๋ผ๋ฏธํฐ๋ฅผ contravariant ํ๊ฒ ๋ง๋ ๋ค.
List
์Set
์ ํ์ ํ๋ผ๋ฏธํฐ๋ covariant(out ํ์ ์)์ด๊ณ ,Array
,MutableList
,MutableSet
,MutableMap
ํ์ ํ๋ผ๋ฏธํฐ๋ invariant(๋ฌด๊ณต๋ณ) ์ด๋ค.- ํจ์ ํ์ ์ ํ๋ผ๋ฏธํฐ ํ์ ์ contravariant(in ํ์ ์) ํ์ ์ด๊ณ ๋ฆฌํด ํ์ ์ covariant(out ํ์ ์)ํ์ ์ด๋ค.
Item 25 ๊ณตํต ๋ชจ๋์ ์ถ์ถํด์ ์ฌ๋ฌ ํ๋ซํผ์์ ์ฌ์ฌ์ฉํ๋ผ
TL;DR - ๊ณตํต ๋ชจ๋์ ์ฌ์ฌ์ฉํ์