Null Safe
์๋ฐ์ ๋ค๋ฅด๊ฒ ์ฝํ๋ฆฐ์ Null์๋ํด ๊ต์ฅํ ๊น๋ค๋กญ๋ค.
์๋ฐ๋ ํจ์๊ฐ ๊ฐ์ด Null์ธ์ง ๋ณด์ฅ์ ํ์ง ๋ชป๋ค. @Nullable, @NotNull ๋ฑ์ ์ด๋ ธํ ์ด์ ์ผ๋ก ์ฒ๋ฆฌ๋ฅผ ํ๊ธฐ๋ ํ์ง๋ง ์ ์ง๋ณด์๋ ๊ฐ๋ ์ฑ ์ธก๋ฉด์์ ์ ๊ณต๋๋ ๋ถ๋ถ์ด๊ณ ์ปดํ์ผ๋ฌ๊ฐ ์ค์ ๋ก ์๋ฌ๋ฅผ ๋ฑ์ด์ฃผ์ง๋ ๋ชปํ๋ค. ํ์ง๋ง ์ฝํ๋ฆฐ์์๋ ์ปดํ์ผ๋ฌ๊ฐ ์๋ฌ๋ฅผ ์ก์์ค๋ค.
์ฝํ๋ฆฐ์๋ ์์ ํ ํธ์ถ(safe call) ?. ์ฐ์ฐ์์ ์๋น์ค ์คํผ๋ ์ดํฐ๋ผ๊ณ ํ๋ ?: ์ฐ์ฐ์๊ฐ ์กด์ฌํ๋ค. (์๋น์ค ํ๋ ์ฌ๋ฆฌ์ ๋จธ๋ฆฌ๋ชจ์์ ๋ฎ์๋คํด์ ์๋น์ค๋ค...)
์ด ? ๊ฐ ๋ถ์๊ฒฝ์ฐ์๋ง ์ปดํ์ผ๋ฌ๊ฐ Null์ ํ์ฉํ๋ค.
? ๊ฐ ๋ถ์ง์์, Nullable ํ์ง ์๋ค๊ณ ๋์ด์๋ ๊ณณ์ Nullable ํ ๊ฐ์ ํ ๋นํด ์ฃผ๋ ค๊ณ ํ๋ฉด ์ปดํ์ผ ์๋ฌ๊ฐ ๋ฐ์ํ๋ค.
์ด๋ฐ์์ผ๋ก ์ปดํ์ผ๋ฌ๊ฐ ์๋ฌ๋ฅผ ์ก์์ฃผ๊ธฐ ๋๋ฌธ์ ๋ฐํ์์์ ๋ฐ์ํ ์ ์๋ NPE ๋ฅผ ๋ฐฉ์งํ ์ ์๋ค.
var str : String = "Hello World"
str = null
//error
var str_1: String? = "Hello World"
str_1 = null
//Nullable
์ด๋ ๊ฒ ์ฝ๋์์ฑ๋จ์์ ์ปดํ์ผ๋ฌ๊ฐ ์ก์์ฃผ๊ธฐ ๋๋ฌธ์ ํ์ ์ ํ ๋๋ ๋ค๋ฅธ์ฌ๋์ด ์ง ๋ถ๋ถ์ ์ฝ๋์์ ๊ฐ์ ๋ฐ์์ผ ํ ๋ ์ฝํ๋ฆฐ ์์๋ ๋ฐ์์ผ ํ ๊ฐ์ด null ์ด ์ฌ ์ ์๋ ๊ฐ์ธ์ง null ์ด ์ฌ ์ ์๋ ๊ฐ์ธ์ง ๋ช ์์ ์ผ๋ก ์ ์ ์๊ธฐ ๋๋ฌธ์ ๊ตณ์ด null ์ฒดํฌ๋ฅผ ํ๋ ์ฝ๋๋ฅผ ์ถ๊ฐํด ์ค ํ์๊ฐ ์์ด์ง๋ค.
var nullable: String? = "Kotlin"
fun nullSafe(str: String?): String? {
return str
}
Nullable ํ๊ฒ ์ ์ธ์ ํ๋ค๋ฉด ๊ทธ ๊ฐ์ฒด์ ์ ๊ทผ์ ํ ๋๋ ?. ์ผ๋ก ์ ๊ทผ์ ํด์ผํ๋ค. safe-call ์ฐ์ฐ์๋ ์ด ์ฐ์ฐ์๋ฅผ ์ฌ์ฉํ๋ ๊ฐ์ฒด๊ฐ null ๊ฐ์ด ์๋ ๊ฒฝ์ฐ์ ์ฐ์ฐ์ ๋ค์ ๋ฌธ์ฅ์ ์ํํ๋ค. null ๊ฐ์ผ ๊ฒฝ์ฐ์๋ ๋ค์ ๋ฌธ์ฅ์ ์ํํ์ง ์๊ณ null ๊ฐ์ ๋ฐํํ๋ค. -> ์ฆ, null ์ธ ๊ฐ์ฒด์ property ๋ฅผ ์ฐธ์กฐํ๊ฑฐ๋, ํจ์๋ฅผ ํธ์ถํ๋ ์ผ์ ๋ฐฉ์งํ ์ ์๋ค.
var str: String? = "Hello World!"
str.length
//error
str?.length
//correct
// bar๊ฐ null์ด ์๋ ๊ฒฝ์ฐ์๋ง ํด๋น ๊ฐ์ ๋์
, ๊ทธ๋ ์ง ์์ ๊ฒฝ์ฐ null์ foo์ ๋์
ํ๋ค.
val foo = bar?.baz
//foo๊ฐ null์ด ์๋ ๊ฒฝ์ฐ์๋ง bar() ํธ์ถ
foo?.bar()
//java
public class Contact {
@NotNull
String name;
@Nullable
Address address;
}
class Address {
@NotNull
String line1;
@Nullable
String line2;
}
// Address.line1์ null๊ฐ์ ํ์ฉํ์ง ์์ง๋ง,
// address๊ฐ null์ธ ๊ฒฝ์ฐ null์ ๋ฐํํ๊ฒ ๋๋ฏ๋ก ๊ฐ line์ ํ์
์ null๊ฐ์ ํ์ฉํด์ผ ํ๋ค.
val line: String? = Contact().address?.line1
// ์ฃผ์๊ฐ ์๊ฑฐ๋ line2๊ฐ ์์ ๊ฒฝ์ฐ ๊ธฐ๋ณธ๊ฐ์ธ "No Address"๋ฅผ ๋ฆฌํด
val line: String = Contact().address?.line2 ?: "No Address"
str!!.length
// ์ ๋ Null์ด ์์๊ฒฝ์ฐ์ ๊ฐ์ ํธ์ถํ๋ ๋ฐฉ๋ฒ
// ์ฃผ์ํด์ผ๋ ์ ์ด Nullable๋ก ์ ์ธํ ๋ณ์์ !!๋ก ์ ๊ทผ์ ํ๋ฉด ์ปดํ์ผ์๋ ๋ฌธ์ ๊ฐ ์์๊ฒ์ด๋ค.
// ํ์ง๋ง ๋ฐํ์์์ ์ด Nullableํ ๊ฐ์๋ค๊ฐ Null์ด ๋ค์ด๊ฐ๋๋ฐ Null์ด ์๋๋ผ๊ณ !!๋ก ์ ๊ทผ์ ํ๋ฉด
// ๋ฐํ์์ค์ NPE ๋ฟ๋๋ค.
// ๊ณ ๋ก !!๋ ๋ด๊ฐ ๊ตณ์ด Null์ด ์ ๋ ์ค์ง ์์๊ฒ์ด๋ค ๋ผ๊ณ ๋ณด์ฅํ ์ ์๋ ๋ถ๋ถ์๋ง ์ฌ์ฉ์ ํด์ผํ๋ค.
val foo: Foo? = nullable...
// ๊ฐ foo๋ null๊ฐ์ ํฌํจํ์ง ์์์ ๋ณด์ฆ.
val nonNullFoo: Foo = foo!!
// ๊ฐ foo๊ฐ null๊ฐ์ด ์๋์ ๋ณด์ฆํ๋ฉด์ bar()ํจ์ ํธ์ถ
foo!!.bar()
// ๊ฐ foo๊ฐ null์ด ์๋์ ๋ณด์ฆํ๋ฉด์ baz ํ๋กํผํฐ ์ ๊ทผ
foo!!.baz
Nullableํ ๊ฐ์ ์ ๊ทผ์ ํ ๋ Null์ผ ๊ฒฝ์ฐ์๋ Default๊ฐ์ ์ฃผ๊ณ ์ถ์๋ ์๋น์ค ์คํผ๋ ์ดํฐ๋ฅผ ์ฌ์ฉํ๋ค.
if else ๋ก ํ๋ณํด๋ ๋์ง๋ง ์๋น์ค ์คํผ๋ ์ดํฐ๋ฅผ ์ฌ์ฉํ๋ ํธ์ด ๊ฐ๋ ์ฑ์ธก๋ฉด์์ ํจ์ฌ ์ข๋ค. ์๋ฐ์ ์ผํญ์ฐ์ฐ์์ ๋น์ทํ๊ฒ ์๊ฒผ๋ค. ์ฐธ๊ณ ๋ก ์ฝํ๋ฆฐ์๋ ์ผํญ์ฐ์ฐ์๊ฐ ์๋ค.
var str: String? = "Hello World!"
str?.length ?: -1
// ์์ ๊ฐ์ด Null์ด ์๋๋ผ๋ฉด ๊ทธ๋ฅ Nullable์ length ๊ฐ์ ๊ฐ์ ธ์ค๊ณ ,
// Null์ด๋ผ๋ฉด Default๋ก ์ค์ ํด๋ ๊ฐ์ ๊ฐ์ ธ์จ๋ค. (?: ์ ๋ค์ ๊ฐ.)
// ์ด๋ฐ์์ผ๋ก ๋์ฒ๋ฆฌ๋ฅผ ์ฝ๊ฒ ํ ์ ์๋ค.
fun generateMapWithAddress(address: String) : Image? {
// ๊ฒ์๊ฒฐ๊ณผ๊ฐ ์์๊ฒฝ์ฐ Exception ๋ฐ์
val postal = findPostalCode(address) ?: throw IllegalStateException()
}
as? ์ฐ์ฐ์. ์ง์๋์ง ์๋ ์๋ฃํ์ผ๋ก ๋ณํ์ ์๋ํ๋ ๊ฒฝ์ฐ ์์ธ๊ฐ ๋ฐ์ํ๋ค.
val foo: String = "foo"
//java.lang.ClassCastException ๋ฐ์
val bar: Int = foo as Int
//์๋ฐ์์๋ ์ง์๋์ง ์๋ ์๋ฃํ์ผ๋ก ๋ณํ์ ์๋ํ ๊ฐ๋ฅ์ฑ์ด ์๋ ๋ถ๋ถ์
//try-catch ๋ธ๋ก์ผ๋ก ๊ฐ์ธ ์ฒ๋ฆฌํด์ผ ํ์ง๋ง,
//์ฝํ๋ฆฐ์์ as? (์์ ํ๋ณํ) ์ฐ์ฐ์๋ฅผ ์ฌ์ฉํ์ฌ ์ด ๋ฌธ์ ๋ฅผ ๊ฐํธํ๊ฒ ํด๊ฒฐํ ์ ์๋ค.
//as? ์ฐ์ฐ์๋ ํ๋ณํ์ด ์คํจํ ๊ฒฝ์ฐ ์์ธ๋ฅผ ๋ฐ์์ํค๋ ๋์ ์ null๊ฐ์ ๋ฐํํ๋ค.
//๋ฐ๋ผ์ ๋ณํ๋๋ ๊ฐ์ ํตํด ๋ณํ๊ฒฐ๊ณผ๋ฅผ ๋ฐ๋ก ํ์ธํ ์ ์๋ค.
//null๊ฐ์ด ์ฌ ์ ์์ผ๋ฏ๋ก ๋ฐ๋์ชฝ์ ์๋ฃํ์ Nullable๋ก ํด์ฃผ์ด์ผ ํ๋ค.
val foo: String = "foo"
//bar๊ฐ null์ ํ์ฉํ๋๋ก Int?๋ก ์ ์ํ๋ค.
//ํ๋ณํ์ ์คํจํ๋ฏ๋ก bar์๋ null์ด ํ ๋น๋๋ค.
val bar: Int? = foo as? Int
//์คํจ์ ๊ฒฝ์ฐ null์ ๋ฐํํ๊ธฐ ๋๋ฌธ์ ์๋น์ค ์ฐ์ฐ์์ ํจ๊ป ์ฌ์ฉํ ์ ์๋ค.
val bar: Int = foo as? Int ?: 0 //ํ๋ณํ ์คํจ์ ๊ฒฝ์ฐ ๊ธฐ๋ณธ๊ฐ์ 0์ผ๋ก ์ง์ .
์๋ฐ๋ก ์์ฑ๋ ํด๋์ค๋ ๊ธฐ๋ณธ์ ์ผ๋ก null ๊ฐ์ ํ์ฉํ๋๋ก ์ฒ๋ฆฌ๋๋ฉฐ, ์ฝํ๋ฆฐ์์ platform type์ด๋ผ ๋ถ๋ฅธ๋ค. ํ๋ซํผ ํ์ ์ Type!๊ณผ ๊ฐ์ ํํ๋ก ํ์๋๋ค. ํ๋ซํผ ํ์ ์ ์ฝํ๋ฆฐ์์ ์๋ฐ๋ก ์์ฑํ ํด๋์ค๋ฅผ ์ฌ์ฉํ ๋์ ์๋์ผ๋ก ์ง์ ๋๋ ํ์ ์ผ๋ก, ์ด ํ์ ์ ๊ฐ๋ฐ์๊ฐ ์ง์ ์ฌ์ฉํ ์ ์๋ค.
//java
class Person {
String name;
public String getName() {
return name;
}
}
//kotlin
val person: Person = ...
// n1์ null๊ฐ์ ํ์ฉํ์ง ์๋๋ค.
val n1: String = person.name
// n2๋ null๊ฐ์ ํ์ฉํ๋ค.
val n2: String? = person.name
// ์ด๋ฐ ํน์ง ๋๋ฌธ์ ํ๋ซํผํ์
๊ฐ์ฒด๋ฅผ ์ฌ์ฉํ ๋๋ ํญ์ ๊ฐ์ฒด์ null๊ฐ ์ฌ๋ถ๋ฅผ ํ์ธํด์ผ๋ง ํ๋ค. -> NPE
// ์ฝํ๋ฆฐ์์๋ ์ด๋ฅผ ํด๊ฒฐํ๊ธฐ ์ํด ์๋ฐ์ annotation์ ์ธ์ํด ๊ฐ์ฒด์ null ํ์ฉ์ฌ๋ถ๋ฅผ ํ๋จํ๋ค.
//java
class Person {
@Nullable
String name; //์ด๋ฐ ์์ผ๋ก ์ด๋
ธํ
์ด์
์ ๋ถ์ฌ์ฃผ๋ฉด nameํ๋๋ ์ฝํ๋ฆฐ์์ nullableํ ํ๋กํผํฐ(String?)์ผ๋ก ์ธ์๋๋ค.
public String getName() {
return name;
}
}
//kotlin
//์คํจ
val n1: String = person.name
//์ฑ๊ณต
val n2: String? = person.name
Last updated