#3 Nullable Type๊ณผ ํจ์, ํจ์ํ ํ๋ก๊ทธ๋๋ฐ
by JiwonDevNullPointerExecption์ ๊ณผ๊ฑฐ๋ถํฐ ์ง๊ธ๊น์ง ๊ฐ๋ฐ์๋ฅผ ๊ดด๋กญํ๋ ์์ธ์ค ํ๋์ด๋ค.
์ฝํ๋ฆฐ์ ์ด๋ฅผ ์๊ณ ์๊ธฐ์, ์ ์ด์ [ Null์ด ๊ฐ๋ฅํ ํ์ ]๊ณผ [ Null์ด ๋ถ๊ฐ๋ฅํ ๊ธฐ๋ณธํ์ ]์ ๊ตฌ๋ถํด์ ์ฌ์ฉํ๋ค.
๐ญ Nullable Type (?=)
๊ธฐ๋ณธ์ ์ผ๋ก ์ฝํ๋ฆฐ์ ๋ณ์์ null ๋์ ์ ํ์ฉํ์ง ์๋๋ค. ์๋์ ์ฝ๋๋ ์ปดํ์ผ ์ค๋ฅ๋ฅผ ๋ฐ์์ํจ๋ค.
val s1: String = null // compile error: Null can not be a value of a non-null type String
๋ง์ฝ null์ ํ์ฉํ๊ณ ์ถ๋ค๋ฉด, Nullable Type์ ์ฌ์ฉํด์ผํ๋ค.์ด๋ Type? ๋ฌธ๋ฒ์ผ๋ก ์ฌ์ฉํ ์ ์๋ค.
val s2: String? = null // OK
- ์ด๋ฌํ ๋ ํ์ ๊ตฌ๋ถ ๋๋ถ์ null ์ฒดํฌ๊ฐ ํ์ํ์ง ์ฌ๋ถ๋ฅผ ์ฝ๊ฒ ํ์ธํ ์ ์๋ค.
- ๋ง์ฝ Nullable ํ์
์ ์ฌ์ฉํ๋ค๋ฉด, ๋ฐ๋์ null์ ์ฒ๋ฆฌํ๋ ๋ฌธ๋ฒ์ ์ฌ์ฉํด์ผํ๋ค.
์๋ฅผ ๋ค์ด ์๋์ ๊ฐ์ด ?. ๋ฅผ ์ฌ์ฉํ๋ Safe Access๋ฅผ ์ฌ์ฉํ ์ ์๋ค.
foo?.bar() // ๋ง์ฝ foo๊ฐ null์ด๋ผ๋ฉด, ๋ฉ์๋๋ฅผ ์คํํ์ง ์๊ณ null์ ๋ฐํํ๋ค.
foo?.get().bar() // ์ด๊ฒ๋ ๋๊ฐ์ด ์ฒ๋ฆฌ๋๋ค.
- ๋น์ฐํ๊ฑฐ์ง๋ง Safe access๋ก ๊ฐ์ ธ์จ ๊ฐ์ null์ด ๋ค์ด๊ฐ ์ ์๊ธฐ๋๋ฌธ์, Nullable ํ์
( Int? )์ผ๋ก ๋ฐํ๋๋ค.
์ฐธ๊ณ ๋ก Safe access๋ฅผ nullable ํ์ ์ด ์๋ ๊ณณ์ ์ฌ์ฉํ ์๋ ์์ผ๋, ๊ตณ์ด ๊ทธ๋ ๊ฒ ์ฌ์ฉํ ์ด์ ๋ ์๋ค.
- Elvis operator๋ฅผ ์ฌ์ฉํ๋ฉด ๋ง์น Js์ฒ๋ผ null๊ฐ์ ๊ด๋ฆฌํ ์ ์๋ค.
- ์๋ฐ์ฒ๋ผ null์ผ ๋ NPE ์์ธ๋ฅผ ๋ฐ์์ํค๊ณ ์ถ๋ค๋ฉด, ์๋์ ๊ฐ์ด ๋๋ํ 2๊ฐ (!!)๋ฅผ ์ฌ์ฉํ๋ฉด ๋๋ค.
์ด๋ฅผ Not-null assertion์ด๋ผ๊ณ ํ๋ค.
- Nullable ๋ฌธ๋ฒ ( ? ?. ?: !! !!.) ์ ์ฌ์ฉํ ๋, ์๋ฏธ๋ฅผ ๋ช ํํ๊ฒ ์ฌ์ฉํ์. ํท๊ฐ๋ฆด ์ ์๋ค
๐ญ Nullable Type์ ๋์๋ฐฉ์
์ฝํ๋ฆฐ์์์ Nullable Type์ ์๋ฐ์ @NotNull, @Nullable ์ด๋ ธํ ์ด์ ์ ์ด์ฉํด์ ์ฒ๋ฆฌํ๋ค.
์ฆ ์ปดํ์ผ ํ์์์ ๊ฑธ๋ฌ์ง๋๊ฑฐ์ง, ์ค์ ๋ฐ์ดํธ์ฝ๋์์์๋ ๊ทธ๋ฅ ์ผ๋ฐ ๊ฐ์ฒด์ ๋์ผํ๋ค. Optional์ ์ฌ์ฉํ์ง ์๋๋ค.
์ด๋ฅผ ์ธ๊ธํ๋ ์ด์ ๋, ๋ง์น ์ฝํ๋ฆฐ์์๋ Optional.get()์ ์ฌ์ฉํ๋ ๊ฒ์ฒ๋ผ ์ฌ์ฉํด์ผ ํ๊ธฐ ๋๋ฌธ์ด๋ค.
์ฝํ๋ฆฐ์ NullableType์ ์ธ์ด ์ฐจ์์ ํธ์ ๊ธฐ๋ฅ์ด๋ค. ์ปดํ์ผ ์ดํ ๋ฐํ์์๋ ์๋ฌด๋ฐ ์ํฅ์ด ์๋๊ฑธ ์ธ์งํ์.
๐ญ as, as? (Safe Cast)
์ฝํ๋ฆฐ์ ์ ์ ํ์ ์ธ์ด์ด๋ค. ์ปดํ์ผ ์์ ์ ๋ชจ๋ ํ์ ์ด ๊ฒฐ์ ๋๋ค.
ํ์ ์ถ๋ก ์ ์ ๊ณตํด์ค์ ์ง์ ํ์ ์ ์์ฑํ์ง ์์๋ ๋์ง๋ง, ํ์ํ๋ค๋ฉด as ๋ฅผ ์ด์ฉํด์ ๋ช ์์ ์ธ ์บ์คํ ์ ํ ์ ์๋ค.
๋ง์ฝ ํน์ ํ์ ์ ๊ฐ์ ํ๊ณ ์ถ๋ค๋ฉด Safe Casting ๋ฌธ๋ฒ์ธ as? ๋ฅผ ์ฌ์ฉํ ์ ์๋ค. ํด๋น ํ์ ์ด ์๋๋ฉด null์ ๋ฐํํ๋ค.
์ฝํ๋ฆฐ์๋ nullable ํ์ ์ด ๋ณ๋๋ก ์กด์ฌํ๋ค. Safe Casting์ ์ด์ฉํ๋ฉด ์๋์ ๊ฐ์ ๋ฐํ์ ์ค๋ฅ๋ฅผ ๋ฐฉ์งํ ์ ์๋ค.
var s:String = "3"
println(s as Int?) // ์บ์คํ
์๋. (Int?) s โก ๋ฐํ์ ์์ธ ๋ฐ์: ClassCastException
println(s as? Int) // OK ์บ์คํ
์ ์๋ํ์ง ์๊ณ , ๋ฐ๋ก null์ ๋ฐํํจ
println(s as? Int?) // OK ์บ์คํ
์ ์๋ํ์ง ์๊ณ , ๋ฐ๋ก null์ ๋ฐํํจ
๐ญ ์ต๋ช ํจ์, Lambda ๋๋ค, It ํค์๋
JDK8์์ ๋๋ค๋ฅผ ์ ๊ณตํ๋ ๋งํผ, ์ฝํ๋ฆฐ ๋ํ ํจ์ํ ๋ฌธ๋ฒ์ ์ ๊ณตํ๋ค.
์ถ๋ก ์ด ๊ฐ๋ฅํ ๋ถ๋ถ์ ์๋ต์ด ๊ฐ๋ฅํ๋ค.
// 0๋ณด๋ค ํฐ ๊ฐ์ด ํ๋๋ผ๋ ์๋ค๋ฉด true
list.any( { i: Int -> i>0 } )
list.any( { i: Int -> i>0 } )
list.any() { i: Int -> i>0 } ) // 1๏ธโฃ ๋๋ค๊ฐ ๋ง์ง๋ง ์ธ์๋ผ๋ฉด, ์ด๋ ๊ฒ ๊ตฌ๋ถํด์ ์ฌ์ฉ๊ฐ๋ฅ
list.any { i: Int -> i>0 } // 2๏ธโฃ ๋๋ค ์ด์ธ์ ์ธ์๊ฐ ์๋ค๋ฉด, method() ๊ดํธ๋ฅผ ์๋ตํ ์ ์์.
list.any { i -> i>0 } // 3๏ธโฃ ์ฝํ๋ฆฐ์ ํ์
์ถ๋ก ์ด ๊ฐ๋ฅํ๊ธฐ์, ํ์
๋ ์๋ตํด๋๋จ
๋ง์ฝ ํจ์์ ์ ๋ ฅ๊ฐ์ด 1๊ฐ ( x -> x+1) ๋ผ๋ฉด, it ํค์๋๋ฅผ ์ฌ์ฉํ ์ ์๋ค. ๋ง์น ์๋ฐ์ this์ ๋๋์ด ๋น์ทํ๋ค.
list.any { i -> i>0 }
list.any { it>0 }
๋๋ค์์๋ return์ด ์ ๊ณต๋์ง ์๋๋ค. ๋ง์ง๋ง์ผ๋ก ์ฌ์ฉํ ์์ด return ๊ฐ์ผ๋ก ์ฌ์ฉ๋๋ค.
๐งจ ๋ง์ง๋ง์ผ๋ก ์ฌ์ฉํ ํํ์์ด ๋ฐํ๊ฐ์ผ๋ก ์ฌ์ฉ๋๋ค. ์ด๋ฅผ ์กฐ์ฌํ์.
๋ง์ฝ if(~) return ์ ์ฌ์ฉํ๊ณ ์ถ๋ค๋ฉด, return@flatMap ๊ฐ์ ๋ณ๋ช ์ ํ์ฉํ ์ ์๋ค. ์ด๋ ๊ธ ์๋์์ ์ถ๊ฐ๋ก ์ค๋ช ํ๋ค.
list.flatMap {
if (it == 0) listOf<Int>() // ๊ทธ๋ฅ ๋ฒ๋ ค์ง. ๋๋ค ๋ฐํ๊ฐ์ด ์๋ โ
listOf(it, it) // ๋ง์ง๋ง ํํ์. ๋๋ค ๋ฐํ๊ฐ์ด ๋ง์ โญ
}
list.flatMap {
if (it == 0) listOf<Int>() // if-else ๋ง์ง๋ง ํํ์. ๋ฐํ๊ฐ์ด ๋ง์ โญ
else listOf(it, it) // if-else ๋ง์ง๋ง ํํ์. ๋ฐํ๊ฐ ๋ง์ โญ
}
์ฝํ๋ฆฐ์ Python์ด๋ JS์ฒ๋ผ ๊ตฌ์กฐ๋ถํด ํ ๋น(Destructuring declarations)์ ์ง์ํ๋ค.
var (a, b) = Pair(3, 4)
var (_, b) = Pair(3, 4) // ๋ง์ฝ ๋ ์ค ๊ฐ ํ๋๋ง ํ์ํ๋ค๋ฉด, _ ์ ์ฌ์ฉํด์ ๋ฒ๋ฆด ์ ์๋ค.
// data class Person(val name: String, val age: Int)
val person = Person("chacha", 10)
val (name, age) = person
val (name, _) = person
println("$name, $age")
์ด๋ฅผ ๋๋ค์์์๋ ๋๊ฐ์ด ํ์ฉํ ์ ์๋ค.
val map = mapOf<Int, String>(1 to "one", 2 to "two", 3 to "Three")
map.mapValues { item -> "${item.key} : ${item.value}" }
map.mapValues { (key, value) -> "$key : $value" }
์ฐธ๊ณ ๋ก it ํค์๋๋ ๊ฐ ์๋ง๋ค ๋ค๋ฅด๊ฒ ์ฌ์ฉ๋๋ค. ์๋ 3๊ฐ์ ์ฝ๋๋ ๊ฐ์ ๋์์ ํ๋ค.
์ ์์ ์ฒ๋ผ ์ต์ง๋ก It์ ์ฌ์ฉํด์ ํ์ค๋ก ๋ง๋ค์ง ๋ง๊ณ , ์ฑ ์์ ๋๋ ์ฝ๊ธฐ ์ข๊ฒ ๋ฆฌํฉํ ๋ง ํด์ ์ฌ์ฉํ์.
๐ญ ํจ์ํ ๋ผ์ด๋ธ๋ฌ๋ฆฌ ( filter, map, reduce...)
JDK8์์ ์ ๊ณตํ๋ filter, map, sum๋ฑ์ ํจ์ํ ๋ฉ์๋๋ ์ ๋ถ ์ ๊ณตํ๋ค.
๋ฌธ๋ฒ๋ง ์ด์ง ๋ค๋ฅผ ๋ฟ, ์๋ฐ์ ์ฌ์ฉ๋ฒ์ ํฌ๊ฒ ๋ค๋ฅด์ง ์๋ค.
์ฝํ๋ฆฐ ํ์ฅํจ์๋ก ๋ ํ๋ถํ ํจ์๋ค์ ์ ๊ณตํด์ฃผ๋ ํ์ํ๋ฉด ์ฐพ์๋ณด๋๋ก ํ์.
๐ญ ํจ์๋ฅผ ์ผ๊ธ์๋ฏผ์ผ๋ก ์ฌ์ฉํ๊ธฐ
์ฝํ๋ฆฐ์ ํจ์๋ฅผ ์ธ์๋ ๋ฐํ๊ฐ์ผ๋ก ์ฃผ๊ณ ๋ฐ์ ์ ์๋ค. ํจ์ ํ์ ์ python์ฒ๋ผ ์๋์ ๊ฐ์ด ์ ์ํ๋ค.
์ฝํ๋ฆฐ ๋ฌธ๋ฒ์ ์ ์ ํ๊ฒ ํ์ฉํ๋ฉด, ๋ณ์์ ๋๋คํจ์๋ฅผ ํ์ค๋ก ์ ์ํ ์๋ ์๋ค.
val isEven: (Int) -> Boolean = { it % 2 == 0 } // ํ..ํ์ค์ผ?
val result: Boolean = isEven(42) // True
ํจ์๋ฅผ ()๋ก ์คํํ ์ ์์ง๋ง, ์ฝํ๋ฆฐ์๋ run ํค์๋๋ฅผ ์ ๊ณตํด์ค๋ค.
๋ฌผ๋ก ์ฝํ๋ฆฐ์์๋ง ํจ์๋ฅผ ์ด๋ ๊ฒ ์ฌ์ฉํ๋๊ฑฐ๊ณ , ๋ด๋ถ์ ์ผ๋ก๋ ์๋ฐ์ ๋์ผํ๊ฒ ๋์ํ๋ค.
๊ทธ๋์ ํ๋ฒ ์ ์ํ ๋๋คํจ์๋ฅผ ๋ค๋ฅธ ๋ณ์์ ๋ด์ ์ ์๋ค.
๊ทผ๋ฐ ํฐ ์๊ด์๋๊ฒ, ๋ฉ์๋๋ฅผ ํธ์ถํ๋ ํ์์ผ๋ก ์ฌ์ฉํ๋ฉด ๋๋ค.
์๋์ ์ธ๊ธํ ๋ฉ์๋ ๋ ํผ๋ฐ์ค(๋ฉค๋ฒ ์ฐธ์กฐ)๋ฅผ ์ฌ์ฉํ๋ฉด ๋ณต์ฌํ๋ฏ์ด ์ฌ์ฉํ ์ ์๋ค.
๐ญ ํจ์๋ NotNull, Nullable ๋๊ฐ์ ํ์ ์ด ์กด์ฌํ๋ค.
๋น์ฐํ ๋๋คํจ์์ ๋ฐํ ๊ฐ์ผ๋ก Nullable์ ์ฌ์ฉํ ์ ์๋ค.
val f1: () -> Int? = { null } // ๊ฐ๋ฅ
f1() // ์คํ ๊ฐ๋ฅ () -> null ํจ์๊ฐ ์คํ๋จ
ํจ์ ์์ฒด์ Nullable ํ์ ์ ์ฌ์ฉํ๋ฉด [๋๋ค ํจ์] ๋์ [null]์ ๋ด์ ์ ์๋ค.
val f3: ( ()->Int )? = null // OK ๊ฐ๋ฅ
f3() // ์คํ ๋ถ๊ฐ๋ฅ. f3 == null ์
์์ 2๊ฐ์ง๋ฅผ ํผ๋ํด์ ์ฌ์ฉํ๋ค๊ฐ ์ปดํ์ผ ์๋ฌ๋ฅผ ๋์ธ ์ ์๋๋ฐ, { null } ์ ๋๊ฐ์ ๋ฐํํ๋ ๋๋คํจ์์์ ์ธ์งํ์.
val f2 = null ์ ๋๋ค์ ๋ฐํ๊ฐ์ด ์๋๋ผ, ๋ณ์ ์์ฒด์ null์ ๋ฃ๋๊ฑฐ๋ค.
val f1: ()->Int? = {null} // ๊ฐ๋ฅ. ๋๋คํจ์์ ๋ฐํ๊ฐ์ Nullable ํ์
์ธ Int? ์.
val f2: ()->Int? = null // ๋ถ๊ฐ๋ฅ. ์ปดํ์ผ์๋ฌ f2๋ ๊ธฐ๋ณธํ์
์
val f3: ( ()->Int )? = null // ๊ฐ๋ฅ. f3๋ Nullable ํ์
์.
val f4: ( ()->Int )? = {null} // ๋ถ๊ฐ๋ฅ. ์ปดํ์ผ ์๋ฌ ๋๋คํจ์์ ๋ฐํ๊ฐ์ ๊ธฐ๋ณธํ์
Int์.
๐ญ Member Refernces (๋ฉ์๋ ์ฐธ์กฐ)
JDK8์ ๋ฉ์๋ ์ฐธ์กฐ์ ๊ฑฐ์ ๋์ผํ๋ค.
์ฝํ๋ฆฐ์์์ ๋ฉค๋ฒ์ ๊ทผ์ Person.age , Person.name ์ผ๋ก ์ฌ์ฉํ ์ ์์ง๋ง, ์ด๋ ๋ด๋ถ์ ์ผ๋ก Getter, Setter๋ฅผ ์ฌ์ฉํ๋ค.
์์๋ ์ด์ง ์ธ๊ธํ์ง๋ง, ์ฝํ๋ฆฐ์์ ํจ์๋ฅผ ๋ณ์์ฒ๋ผ ์ฌ์ฉํ๋๊ฑฐ์ง, ๋ด๋ถ๋ฅผ ๊น๋ณด๋ฉด ์๋ฐ์ ๋์ผํ๊ฒ ๋์ํ๋ค.
๊ทธ๋์ ํจ์๋ฅผ ๋ณ์๋ก ์ ๋ฌํ ๋์๋ ๋ฉค๋ฒ ์ฐธ์กฐ๋ฅผ ์ด์ฉํด์ฃผ๋ฉด ๋๋ค.
ํจ์๋ฅผ ๋ณ์์ ๋ด์ ์ ์๋ค๋ณด๋, ๊ฐ์ฒด์ Bound / UnBound๋ผ๋ ๊ฐ๋ ์ด ์๊ธด๋ค. ์๋ ์ฝ๋๋ฅผ ์ดํด๋ณด์
class Person(val name: String, val age: Int) {
fun isOlder(limit: Int) = age > limit
}
var jiwon = Person("Jiwon", 29)
// ์๋ ๋ ํจ์๋ ๋์ผํ๋ค. (unBound reference)
var isOld = Person::isOlder
var isOld = (Person, Int) -> { (person, limit) -> person.isOlder(limit) }
isOld(jiwon, 20)
isOld(alice, 21)
๋ง์ฝ jiwon.isOlder() ๋ฅผ ๋ฐ๋ก ํธ์ถํ๊ณ ์ถ๋ค๋ฉด, ์ฆ Bound reference๋ฅผ ์ฌ์ฉํ๊ณ ์ถ๋ค๋ฉด this๋ฅผ ์ฌ์ฉํ๋ฉด ๋๋ค.
// ์๋ ๋ ํจ์๋ ๋์ผํ๋ค. (Bound reference)
var isOld = this::isOlder
var isOld = (Int) -> { (limit) -> this.isOlder(limit) }
isOld(20)
isOld(21)
์ฐธ๊ณ ๋ก this::method ์์ this๋ ์๋ตํ ์ ์๋ค.
fun getAge() = this::isOlder
fun getAge() = ::isOlder
๋ค๋ง ํท๊ฐ๋ฆฌ๋ฉด ์๋๋๊ฒ, this::method๋ฅผ ์ฌ์ฉํ๋ค๊ณ ํด์ ๋ฌด์กฐ๊ฑด bound ํจ์์ธ๊ฑด ์๋๋ค.
์๋์ ๊ฐ์ด ๊ทธ๋ฅ ํ์ฌ ์ค์ฝํ์ ์๋ ํจ์๋ฅผ ์ฌ์ฉํ ๋์๋ this::method๋ฅผ ์ฌ์ฉํ ์ ์๊ธฐ ๋๋ฌธ์ด๋ค.
๐งจ ๋๋คํจ์๋ return ํค์๋๋ฅผ ๊ทธ๋ฅ ์ฌ์ฉํ ์ ์๋ค.
return ์ ์ธ์ ๋ ํจ์(fun ํค์๋)์ ๋ฐํ์ ์๋ฏธํ๋ค. ์๋ ์์ ์ ๊ฐ์ ํจ์ ์ ์กฐ์ฌํ์.
๋ง์ฝ return์ ๊ผญ ๋๋คํจ์์ ์ฌ์ฉํ๊ณ ์ถ๋ค๋ฉด, ์๋์ ๊ฐ์ด ๋ณ๋ช
์ ๋ถ์ฌ์ฃผ๋ฉด ์ฌ์ฉํ ์ ์๋ค.
์ฌ์ค ์ ์ผ ์ข์๊ฑด ํท๊ฐ๋ฆด์ผ ์๊ฒ ๋๋ค์ return์ ์์ฐ๋๊ฑฐ๊ธด ํ๋ค.
๊ฐ์ ๋งฅ๋ฝ์ผ๋ก ๋ฐ๋ณต๋ฌธ ์์์ return@forEach๋ฅผ ํ๋ฉด continue ์ฒ๋ผ ๋ฃจํ๋ฅผ ํ์ถํ ์๋ ์๋ค.
์ฒ์ ๋ณด์ฌ์ค ์์ ์์ return@๋ณ๋ช ์ ํ์ฉํ๋ฉด ์๋์ ๊ฐ๋ค. [ 3, 3, 5, 5 ]
์ ์ฌ์ฉ์ ์ํ์ง๋ง, ์ต๋ช ํจ์๋ฅผ ์ฌ์ฉํ๋ค๋ฉด ๊ตณ์ด ๋ณ๋ช ์ ๋ถ์ฌ์ฃผ์ง ์์๋ fun-return์ด ์ ์์ ์ผ๋ก ๋งค์นญ๋๋ค.
'๐ฑBackend > Kotiln' ์นดํ ๊ณ ๋ฆฌ์ ๋ค๋ฅธ ๊ธ
#2. ์ฝํ๋ฆฐ ๊ธฐ์ด๋ฌธ๋ฒ (0) | 2022.02.12 |
---|---|
#1 ์ฝํ๋ฆฐ(Kotlin) ์ด๋ (0) | 2022.02.12 |
๋ธ๋ก๊ทธ์ ์ ๋ณด
JiwonDev
JiwonDev