μμ΄ν 19. knowledge(μλμ μΈ μ 보)λ₯Ό λ°λ³΅νμ¬ μ¬μ©νμ§ λ§λΌ
νμκ° μκ°νλ νλ‘κ·Έλλ°μ κ°μ₯ ν° κ·μΉμ?
- νλ‘μ νΈμμ μ΄λ―Έ μλ μ½λλ₯Ό 볡μ¬ν΄μ λΆμ¬λ£κ³ μλ€λ©΄, 무μΈκ°κ° μλͺ»λ κ²μ΄λ€.
νλ‘κ·Έλ¨μμ μ€μν knowledge λ κ°μ§?
- λ‘μ§: νλ‘κ·Έλ¨μ΄ μ΄λ€ μμΌλ‘ λμνλμ§μ νλ‘κ·Έλ¨μ΄ μ΄λ»κ² 보μ΄λμ§
- κ³΅ν΅ μκ³ λ¦¬μ¦: μνλ λμμ νκΈ° μν μκ³ λ¦¬μ¦
λΉμ¦λμ€ λ‘μ§μ μκ°μ΄ μ§λλ©΄μ κ³μ λ³νμ§λ§, κ³΅ν΅ μκ³ λ¦¬μ¦μ ν λ² μ μλ ν ν¬κ² λ³νμ§ μλλ€.
class Student {
fun isPassing(): Boolean = ...
fun qualifiesForScholarship(): Boolean = ...
private fun calculatePointsFromPassedCousre(): Int { ... }
}
μ Student ν΄λμ€λ μλ‘ λ€λ₯Έ λ λΆμμμ μ¬μ©μ€μΈ ν΄λμ€μΈλ°, μꡬμ¬νμ΄ λ³κ²½λμ΄ κΈ°μ‘΄ ν¨μλ€μ΄ μμ λλ€λ©΄, κ΄λ ¨ μλ κΈ°λ₯μμ μ΄μκ° λ°μν μ μλ€. (ννν λ¨μ ν μ€νΈκ° μ΄λμ λ 컀λ²λ κ°λ₯ν κ² κ°λ€.)
StudentIsPassingValidator, StudentQualifiesForScholarshipValidator λ±μ ν΄λμ€λ₯Ό ꡬλΆν΄μ νμ©ν μ μκ² λ€.
λν μ½νλ¦°μ νμ₯ ν¨μλ₯Ό νμ©νλ©΄, λ ν¨μλ Student ν΄λμ€ μλμ λλ©΄μ κ° λΆμκ° κ΄λ¦¬νλ μλ‘ λ€λ₯Έ λͺ¨λμ νμΌμ λ°°μΉν μ μλ€.
// A λͺ¨λ
fun Student.qualifiesForScholarship(): Boolean {
...
}
// B λͺ¨λ
fun Student.isPassing(): Boolean = {
...
}
ν¬νΌ ν¨μλ private ν¨μλ‘ λ§λ€μ§ μκ³ λ€μκ³Ό κ°μ΄ λ§λλ κ²μ΄ μΌλ°μ μ΄λ€.
- λ λΆμμμ λͺ¨λ μ¬μ©νλ μΌλ°μ μΈ public ν¨μλ‘ ν¬νΌ ν¨μλ₯Ό λ§λ λ€. λν 곡ν΅μ μΈ λΆλΆμ λ λΆμμμ λͺ¨λ μ¬μ©νλ―λ‘, μ΄λ₯Ό ν¨λΆλ‘ μμ ν μ μλλ‘ κ·μ½μ μ νλ€.
- ν¬νΌ ν¨μλ₯Ό κ°κ°μ λΆμ λͺ¨λμ λ°λΌ 2κ° λ§λ λ€.
λ¨μΌ μ± μ μμΉ(SRP)μ μ°λ¦¬μκ² λ κ°μ§ μ¬μ€μ μλ €μ€λ€.
- μλ‘ λ€λ₯Έ κ³³(λΆμ)μμ μ¬μ©νλ knowledgeλ λ 립μ μΌλ‘ λ³κ²½ν κ°λ₯μ±μ΄ λ§λ€. λ°λΌμ λΉμ·ν μ²λ¦¬λ₯Ό νλλΌλ μμ ν λ€λ₯Έ knowledgeλ‘ μ·¨κΈνλ κ²μ΄ μ’λ€.
- λ€λ₯Έ knowledgeλ λΆλ¦¬ν΄ λλ κ²μ΄ μ’λ€. κ·Έλ μ§ μμΌλ©΄ μ¬μ¬μ©νλ €λ μ νΉμ΄ λ°μν μ μλ€.
μμ΄ν 20. μΌλ°μ μΈ μκ³ λ¦¬μ¦μ λ°λ³΅ν΄μ ꡬννμ§ λ§λΌ
// Bad
override fun saveCallResult(item: SourceResponse) {
var sourceList = ArrayList<SourceEntity>()
item.sources.forEach {
var sourceEntity = SourceEntity()
sourceEntity.id = it.id
sourceEntity.category = it.category
...
}
db.insertSources(sourceList)
}
// Good
override fun saveCallResult(item: SourceResponse) {
var sourceEntries = item.sources.map(::sourceToEntry)
db.insertSources(sourceEntries)
}
private fun sourceToEntry(source: Source) = SourceEntity()
.apply {
id = source.id
category = source.category
...
}
λͺ¨λ ν¨μλ ν μ€νΈκ° λμ΄μΌ νκ³ , κΈ°μ΅λμ΄μΌ νλ©°, μ μ§λ³΄μκ° λμ΄μΌ νλ€. -> λΉμ© κ³ λ €
νμ₯ ν¨μκ° κ°μ§λ μ₯μ λ€?
- ν¨μλ μνλ₯Ό μ μ§νμ§ μμΌλ―λ‘ νμλ₯Ό λνλ΄κΈ° μ’λ€.
- ν±λ 벨 ν¨μμ λΉκ΅ν΄μ νμ₯ ν¨μλ ꡬ체μ μΈ νμ μ΄ μλ κ°μ²΄μλ§ μ¬μ©μ μ νν μ μμΌλ―λ‘ μ’λ€.
- μμ ν κ°μ²΄λ₯Ό μκ·λ¨ΌνΈλ‘ μ λ¬λ°μ μ¬μ©νλ κ²λ³΄λ€ νμ₯ 리μλ²λ‘ μ¬μ©νλ κ²μ΄ κ°λ μ± μΈ‘λ©΄μμ λ μ’λ€.
- IDEμ μλμμ± λμμ λ°μ μ½κ² μ°Ύμ μ μλ€.
// (1) Stringμ΄λΌλ μνλ₯Ό κ°μ§ κ°μ²΄μ isEmail()μ΄λΌλ μμν νμλ§ μΆκ°ν ννμ
λλ€.
fun String.isEmail(): Boolean = this.contains("@") && this.contains(".")
val email = "hello@example.com"
println(email.isEmail())
// (2) List<Int> νμ
μλ§ μ μ©λλ―λ‘ νμ
μ΄ λͺ
νν κ°μ²΄μλ§ νΈμΆ κ°λ₯ν¨.
fun List<Int>.sumOfEven(): Int = this.filter { it % 2 == 0 }.sum()
// (3) removeSpaces(raw)λ³΄λ€ raw.removeSpaces()κ° μ§κ΄μ
fun String.removeSpaces(): String = this.replace(" ", "")
val raw = "hello world"
println(raw.removeSpaces())
λ΄κ° ꡬννλ €λ κΈ°λ₯μ΄ μ΄λ―Έ μ€νμμ€, λΌμ΄λΈλ¬λ¦¬ λ±μ μ‘΄μ¬νμ§ μλμ§ μ μ°Ύμ보λ κ²λ μ€μνλ€. νΉν guava, apache λ±μ μ€νμμ€μλ κ΅μ₯ν μ μ©ν λΌμ΄λΈλ¬λ¦¬λ€μ΄ κ΅μ₯ν λ§κΈ°μ νμμ μ μ±κ²¨λ³΄λ κ²μ΄ μ€μν κ² κ°λ€!
μμ΄ν 21. μΌλ°μ μΈ νλ‘νΌν° ν¨ν΄μ νλ‘νΌν° μμμΌλ‘ λ§λ€μ΄λΌ
μ½νλ¦°μ μ½λ μ¬μ¬μ©κ³Ό κ΄λ ¨νμ¬ νλ‘νΌν° μμ(=νλ‘νΌν° λΈλ¦¬κ²μ΄νΈ)μ΄λΌλ μλ‘μ΄ κΈ°λ₯μ μ 곡νλ€.
λνμ μΌλ‘ μ§μ° νλ‘νΌν°κ° μλλ°, lazy νλ‘νΌν°λ μ²μ μ¬μ©νλ μμ²μ΄ λ€μ΄μ¬ λ μ΄κΈ°νλλ νλ‘νΌν°λ₯Ό μλ―Ένλ€.
val value by lazy { createValue() }
μμ κ°μ νλ‘νΌν° μμμ μ¬μ©νλ©΄, λ³νκ° μμ λ μ΄λ₯Ό κ°μ§νλ Obserable ν¨ν΄μ μ½κ² λ§λ€ μ μλ€.
// λͺ©λ‘μ μΆλ ₯νλ 리μ€νΈ μ΄λν°κ° μλ€λ©΄, λ΄λΆ λ°μ΄ν°κ° λ³κ²½λ λλ§λ€ λ³κ²½λ λ΄μ©μ λ€μ μΆλ ₯νκ±°λ λ‘κ·Έλ‘ λ¨κΈ°λ κ²½μ°
val items: List<Item> by Delegates.observable(listOf()) { _, _, _ -> notifyDataChanged() }
// ### λ·°, 리μμ€ λ°μΈλ©, μμ‘΄μ± μ£Όμ
, λ°μ΄ν° λ°μΈλ©
// μλλ‘μ΄λμμ λ·°μ 리μμ€ λ°μΈλ©
private val button: Button by bindView(R.id.button)
private val textSize by bindDimension(R.dimen.font_size)
// Kotlinμμμ μ’
μμ± μ£Όμ
private val presenter: MainPresenter by inject()
private val repository: NetworkRepository by inject()
// λ°μ΄ν° λ°μΈλ©
private val port by bindConfiguration("port")
μ½νλ¦°μ stdlibμμ λ€μκ³Ό κ°μ νλ‘νΌν° λΈλ¦¬κ²μ΄ν°λ₯Ό μμ λμ.
- lazy
- Delegates.obserable
- Delegates.vetoable
- Delegates.notNull
Delegates λ΄λΆ μ½λ
/**
* Standard property delegates.
*/
public object Delegates {
/**
* Returns a property delegate for a read/write property with a non-`null` value that is initialized not during
* object construction time but at a later time. Trying to read the property before the initial value has been
* assigned results in an exception.
*
* @sample samples.properties.Delegates.notNullDelegate
*/
public fun <T : Any> notNull(): ReadWriteProperty<Any?, T> = NotNullVar()
/**
* Returns a property delegate for a read/write property that calls a specified callback function when changed.
* @param initialValue the initial value of the property.
* @param onChange the callback which is called after the change of the property is made. The value of the property
* has already been changed when this callback is invoked.
*
* @sample samples.properties.Delegates.observableDelegate
*/
public inline fun <T> observable(initialValue: T, crossinline onChange: (property: KProperty<*>, oldValue: T, newValue: T) -> Unit):
ReadWriteProperty<Any?, T> =
object : ObservableProperty<T>(initialValue) {
override fun afterChange(property: KProperty<*>, oldValue: T, newValue: T) = onChange(property, oldValue, newValue)
}
/**
* Returns a property delegate for a read/write property that calls a specified callback function when changed,
* allowing the callback to veto the modification.
* @param initialValue the initial value of the property.
* @param onChange the callback which is called before a change to the property value is attempted.
* The value of the property hasn't been changed yet, when this callback is invoked.
* If the callback returns `true` the value of the property is being set to the new value,
* and if the callback returns `false` the new value is discarded and the property remains its old value.
*
* @sample samples.properties.Delegates.vetoableDelegate
* @sample samples.properties.Delegates.throwVetoableDelegate
*/
public inline fun <T> vetoable(initialValue: T, crossinline onChange: (property: KProperty<*>, oldValue: T, newValue: T) -> Boolean):
ReadWriteProperty<Any?, T> =
object : ObservableProperty<T>(initialValue) {
override fun beforeChange(property: KProperty<*>, oldValue: T, newValue: T): Boolean = onChange(property, oldValue, newValue)
}
}
class User {
var name: String by Delegates.observable("initial") { prop, old, new ->
println("Property ${prop.name} changed from $old to $new")
}
}
fun main() {
val user = User()
user.name = "Ju"
user.name = "Hyun"
}
// Property name changed from initial to Ju
// Property name changed from Ju to Hyun
μμ΄ν 22. μΌλ°μ μΈ μκ³ λ¦¬μ¦μ ꡬνν λ μ λ€λ¦μ μ¬μ©νλΌ
νμ μκ·λ¨ΌνΈλ₯Ό μ¬μ©νλ©΄ ν¨μμ νμ μ μ λ¬ν μ μλλ°, μ΄λ₯Ό μ λ€λ¦ ν¨μλΌκ³ λΆλ₯Έλ€.
- μ»΄νμΌλ¬κ° μ νν νμ μΆμΈ‘
μ λ€λ¦ μ ν
- νμ νλΌλ―Έν°μ μ€μν κΈ°λ₯ μ€ νλλ ꡬ체μ μΈ νμ μ μλΈνμ λ§ μ¬μ©νκ² νμ μ μ ννλ κ²
νμ (T)μ μ νμ΄ κ±Έλ¦¬λ―λ‘, λ΄λΆμμ ν΄λΉ νμ μ΄ μ 곡νλ λ©μλλ₯Ό μ¬μ©ν μ μλ€.
mapNotNull vs map
fun main() {
val items: List<String> = listOf("1", "2", "a", "4", "b")
val mappedList = items.map { it.toIntOrNull() }
println("Result of map: $mappedList") // [1, 2, null, 4, null]
val mappedNotNullList = items.mapNotNull { it.toIntOrNull() }
println("Result of mapNotNull: $mappedNotNullList") // [1, 2, 4]
}
μμ΄ν 23. νμ νλΌλ―Έν°μ μλμμ νΌνλΌ
μλμμ μ§μ λ³μλ νλΌλ―Έν°κ° λμΌν μ΄λ¦μ κ°μ§ μΈλΆ μ€μ½νμ λ³μλ₯Ό κ°λ¦¬λ νμμ΄λ€.
class Forest<T> {
fun addTree(tree: T) {
// T νμ
μ λ무λ₯Ό μΆκ°
}
}
μ μ½λλ νμ μμ μ±μ΄ λΆμ‘±νλ°, μ΄λ€ νμ μ treeλ λ€ λ°μ μ μκΈ° λλ¬Έμ΄λ€.
class Forest2<T : Tree> {
fun addTree(tree: T) {
// Tree νμ νμ
μ T νμ
μ λλ¬΄λ§ μΆκ°
}
}
fun main() {
val forest = Forest<Birch>()
forest.addTree(Birch()) // κ°λ₯
forest.addTree(Spruce()) // μ»΄νμΌ μ€λ₯(type mismatch)
}
λ°λΌμ addTree() ν¨μμμ ν΄λμ€ νμ μ νλΌλ―Έν°μΈ Tλ₯Ό μ¬μ©νλλ‘ λ³κ²½ν΄μΌ νλ€.
μμ΄ν 24. μ λ€λ¦ νμ κ³Ό variance νμ μλ₯Ό νμ©νλΌ
class Cup<T>
μ μ½λμμ νμ νλΌλ―Έν° Tλ variance νμ μ(out λλ in)κ° μμΌλ―λ‘, κΈ°λ³Έμ μΌλ‘ invariant(λΆκ³΅λ³μ±)μ΄λ€.
invariantλ μ λ€λ¦ νμ μΌλ‘ λ§λ€μ΄μ§λ νμ λ€μ΄ μλ‘ κ΄λ ¨μ±μ΄ μλ€λ μλ―Έ
out: 곡λ³μ±(covariant)
- μλ° μ λ€λ¦ -> ? extends T
class Cup<out T>
open class Dog
class Puppy : Dog()
fun main() {
val cupDog: Cup<Dog> = Cup<Puppy>() // OK
val cupPuppy: Cup<Puppy> = Cup<Dog>() // μ€λ₯
}
"Aκ° Bμ μλΈ νμ μΌ λ, Cup<A>λ Cup<B>μ μλΈνμ "
in: λ°κ³΅λ³μ±(contravariant)
- μλ° μ λ€λ¦ -> ? super T
class Cup<in T>
open class Dog
class Puppy : Dog()
fun main() {
val cupPuppy: Cup<Puppy> = Cup<Dog>() // OK
val cupDog: Cup<Dog> = Cup<Puppy>() // μ€λ₯
}
"Aκ° Bμ μλΈ νμ μΌ λ, Cup<A>λ Cup<B>μ μμ νμ "
'Kotlin' μΉ΄ν κ³ λ¦¬μ λ€λ₯Έ κΈ
μ΄νν°λΈ μ½νλ¦° 2μ₯ - κ°λ μ± (2) | 2025.06.14 |
---|---|
μ΄νν°λΈ μ½νλ¦° - 1μ₯ μμ μ± (0) | 2025.06.03 |
Kotlin Collection (1) - (forEach, onEach, filter, filterNot, map, mapNotNull, flatMap, fold, reduce, sum, sumOf) (0) | 2025.02.09 |
Kotlin for iterator (1) | 2025.01.17 |
Kotlin - Scope functions (let, with, run, apply, also) (0) | 2024.12.15 |
λκΈ