Null
๊ทธ๋์์ ์ฝํ๋ฆฐ์ ๋ฌธ๋ฒ ์ค ๋์ ๋ฒ์์ ๊ฒ๋ค์ ์ดํด๋ณด์๋ค. ์ด์ ๊ฐ์ฅ ์ค์ํ ๋ถ๋ถ์ ๋ฐฐ์๋ณด์: ๋ฐ๋ก ํ์ ์์คํ ์ด๋ค. ์ฝํ๋ฆฐ์์๋ ์๋กญ๊ฒ ๋ง๋ค์ด์ง ๊ธฐ๋ฅ, ์ฆ nullable ํ์ ๊ณผ read-only collection ๋ฑ์ ์ง์ํ๋ค.
Nullability
์ฝํ๋ฆฐ ํ์
์์คํ
์์๋ ์๋ฐ์์ ํํ ๋ณผ ์ ์๋ java.lang.NullPointerException๋ฅผ ํผํ ์ ์๊ธฐ ์ํด์ ์ปดํ์ผ ์์ ์ null ์๋ฌ๋ฅผ ํ์
ํ ์ ์๋๋ก ๋ช
์์ ์ผ๋ก ์ง์ํ๋ค.
/* Java */
int strLen(String s) {
return s.length();
}์๋ฐ์์๋ ์์ ๊ฐ์ ํจ์์์ String์ด null์ผ ๊ฒฝ์ฐ NullPointerException ์๋ฌ๋ฅผ ๋ง๋ค์ง๋ง, ์ฝํ๋ฆฐ์ ๊ฒฝ์ฐ String ํ์
์ด ๋ฐ๋์ ๋๊ฒจ์ง๋๋ก ๊ฐ์ ํ๊ธฐ ๋๋ฌธ์ null์ด ํฌํจ๋ ๋งค๊ฐ๋ณ์๋ฅผ ๋๊ฒจ ์ค ์ ์๋ค. ๋ง์ฝ ์ฝํ๋ฆฐ์์ null์ ๋๊ฒจ์ฃผ๊ณ ์ถ๋ค๋ฉด ์ด๋ฅผ ์จ๋น์ค ์ฐ์ฐ์ ?๋ฅผ ์ฌ์ฉํ์ฌ ๋ณด์ฌ์ค์ผ ํ๋ค.
fun strLen(s: String) = s.length
>>> strLen(null)
ERROR: Null can not be a value of a non-null type String
//๋ช
์์ ์ผ๋ก null ํ์
์ ๋๊ฒจ์ฃผ๊ธฐ ๋๋ฌธ์ ? ์ฌ์ฉ
un strLenSafe(s: String?) = ...null ํ์ ์ ๋ช ์ํ ํ์๋ ํ ์ ์๋ ์ผ์ด ์ ํ๋์ด ์๋ค.
๋ ์ด์
fun strLenSafe(s: String?) = s.length()์ฒ๋ผ ๋ฉ์๋ ํธ์ถ ๋ถ๊ฐnull ํ์ ์ด ์๋ ๊ฐ์๊ฒ
val x: String? = null์ฒ๋ผ null ํ ๋น ๋ถ๊ฐnull ํ์ ์ด ์๋ ํ๋ผ๋ฏธํฐ๋ฅผ ๊ฐ์ง ํจ์์๊ฒ null ํ์ pass ๋ถ๊ฐ
๋จ, if๋ฅผ ํตํด null์ ์ฒดํฌํด ์ค ๋ค์์๋ ์ปดํ์ผ๋ฌ๊ฐ ์ปดํ์ผ ํ๋ ๊ฒ์ด ๊ฐ๋ฅํ๋ค.
fun strLenSafe(s: String?): Int =
if (s != null) s.length else 0ํ์
์ด๋ ๋ฌด์์ธ๊ฐ?
ํ์
์ด๋ โํด๋น ํ์
์ ๋ํด ๊ฐ๋ฅํ ๊ฐ๋ค์ ์งํฉ์ผ๋ก ๋ชจ์ ๋์ ๋ถ๋ฅโ์ด๋ค.
์๋ฐ์์๋ String ํ์ ์์ String ๊ฐ๊ณผ null ๊ฐ ๋ ์ค์ ํ๋๋ฅผ ๊ฐ์ง ์ ์๋ค. ๋ฐ๋ผ์ ์ด๋ฐ ๊ฒฝ์ฐ ์ถ๊ฐ์ ์ธ ํ์ ์ฒดํฌ๊ฐ ํ์ํ๋ค.
๐ ์๋ฐ์์๋
@Nullable๋@NotNull๋ฅผ ํ์ฉํ์ฌ null ํ์ ์ฒดํฌ๊ฐ ๊ฐ๋ฅํ์ง๋ง, ์ด๋ ๋ณ๋ก ์ ์ฉํ์ง ์๋ค. ๋ ๋ค๋ฅธ ํด๊ฒฐ๋ฒ์Optional class๋ฅผ ํ์ฉํ์ฌ null ํ์ ์ ๊ฐ์ธ๋ ๊ฒ์ด์ง๋ง, ์ด๋ ๋ ๋ณต์กํ ์ฝ๋๋ฅผ ์์ฑํ๊ฒ ๋๋ค.
์ด๋ฌํ ์๋ฐ์ ๋ฌธ์ ์ ์ ์ฝํ๋ฆฐ์ Nullable ํ์
์ ์ ๊ณตํจ์ผ๋ก์จ ์์ฝ๊ฒ ํด๊ฒฐ ๊ฐ๋ฅํ๋ค. Nullable๊ณผ None-null์ ๊ตฌ๋ถํจ์ผ๋ก ์ธํด ์ด๋ ํ ๊ฐ์ด ์ด๋ค ๊ณ์ฐ์ ํ ์ ์๋์ง ๋ช
ํํ๊ฒ ์ดํดํ ์ ์๋ค.
Safe call operator: โ?.โ
ํด๋น ์ฐ์ฐ์๋ null ์ฒดํฌ์ ๋์์ ๋ฉ์๋๋ฅผ ํธ์ถํ๋ ์ญํ ์ ํ๋ค.
s?.toUpperCase()
//๊ฐ์ ์๋ฏธ
if (s != null) s.toUpperCase() else null.์ด๋ ์ฃผ์ํ ์ ์, ๋น๋ก String.toUpperCase()์ ํธ์ถ ๊ฒฐ๊ณผ๊ฐ String์ด์ด์ผ ํ์ง๋ง, s๊ฐ null์ผ ๋ ํจ์์ ํธ์ถ ๊ฒฐ๊ณผ๋ String?์ด ๋๋ค.
class Employee(val name: String, val manager: Employee?)
fun managerName(employee: Employee): String? = employee.manager?.name์์ ๊ฐ์ด ?.์ ํ์ฉํ ๊ฒฝ์ฐ, manager๊ฐ ์กด์ฌํ์ง ์๋ ๊ฒฝ์ฐ์ ๋ํด์ ์ถ๊ฐ์ ์ธ ์ฒดํฌ๊ฐ ์์ด ํ ์ค๋ก ๋ฐ๋ก ํด๊ฒฐ์ด ๊ฐ๋ฅํ๋ค. ๋ํ ๋ค์๊ณผ ๊ฐ์ด ์ฌ๋ฌ ๊ฐ์ Safe call operator๋ฅผ ์ฒด์ธ์ผ๋ก ๋ง๋ค์ด ๊ฐ๊ฐ์ ๊ฐ์ null์ธ์ง ์ฒดํฌํ ์ ์๋ค.
class Address(val streetAddress: String, val zipCode: Int, val city: String, val country: String)
class Company(val name: String, val address: Address?)
class Person(val name: String, val company: Company?)
fun Person.countryName(): String {
val country = this.company?.address?.country
return if (country != null) country else "Unknown"
}์จ๋น์ค ์ฐ์ฐ์: โ?:โ
์ฝํ๋ฆฐ์์๋ null ๋์ ๋ฃ์ด์ค ์ ์๋ ๋ํดํธ ๊ฐ์ ์ฒ๋ฆฌํ๋ ์จ๋น์ค ์ฐ์ฐ์๊ฐ ์๋ค. ๋ง์ฝ ๊ฐ์ด null์ด ์๋ ๊ฒฝ์ฐ์๋ ๊ทธ ๊ฐ์ ํ์ฉํ๊ณ , null์ผ ๊ฒฝ์ฐ์๋ ๋ํดํธ ๊ฐ์ ๋ฃ์ด์ค๋ค. ์ด๋ ์ข
์ข
?.์ ๊ฐ์ด ํ์ฉ๋๊ธฐ๋ ํ๋ค.
fun strLenSafe(s: String?): Int = s?.length ?: 0
>>> println(strLenSafe("abc"))
3
>>> println(strLenSafe(null))
0
fun Person.countryName() =
company?.address?.country ?: "Unknown"๋ํ ์ฝํ๋ฆฐ์์๋ ์จ๋น์ค ์ฐ์ฐ์๋ฅผ throw๋ return๊ณผ ํจ๊ป ์ฌ์ฉํ ์ ์์ด ๋์ฑ ํธ๋ฆฌํ๋ค.
data class Address(val city: String, val country: String)
data class Company(val name: String, val address: Address?)
data class Employee(val name: String, val company: Company?)
fun print(employee: Employee) {
// ์๋น์ค ์ฐ์ฐ์๋ก throw๋ ๊ฐ๋ฅ
val address = employee?.company?.address ?: throw IllegalArgumentException("Need Address")
with(address) {
print("city: $city, countyL $country")
}
}์์ ์ฝ๋์์ ๋ง์ฝ address๊ฐ ์กด์ฌํ์ง ์๋๋ค๋ฉด, NullPointerException์ ๋์ง์ง ์๊ณ ๊ทธ ๋์ ์๋ฏธ ์๋ ์๋ฌ๋ฅผ ๋ณด์ฌ์ค๋ค.
์์ ํ ์บ์คํ
: โas?โ
์๋ฐ์์๋ ์บ์คํธํ๋ ค๋ ๊ฐ์ด ์กด์ฌํ์ง ์์ ๊ฒฝ์ฐ ํ์
์บ์คํธ ์ค์ ClassCastException์ด ๋ฐ์ํ ์ ์์ง๋ง, ์ฝํ๋ฆฐ์ as?๋ฅผ ํตํด ํด๋น ์๋ฌ๊ฐ ๋ฐ์ํ์ง ์๋๋ก ํ ์ ์๋ค. ์ด๋ฌํ ํ์
์บ์คํ
์ ์จ๋น์ค ์ฐ์ฐ์๋ฅผ ํจ๊ป ํ์ฉํ ์ ์๋๋ฐ, ์ด๋ equals ๋ฉ์๋๋ฅผ ์ฌ์ฉํ๋ ๊ณผ์ ์์ ์ ๋๋ฌ๋๋ค.
class Person(val firstName: String, val lastName: String) {
override fun equals(o: Any?): Boolean {
val otherPerson = o as? Person ?: return false
return otherPerson.firstName == firstName &&
otherPerson.lastName == lastName
}
override fun hashCode(): Int =
firstName.hashCode() * 37 + lastName.hashCode()
}์์์ ๋ณผ ์ ์๋ฏ์ด, ์ด๋ฌํ ํจํด์ ํ์ฉํ๋ฉด ํ๋ผ๋ฏธํฐ๊ฐ ์ ์ ํ ํ์ ์ ๊ฐ์ง๊ณ ์๋์ง ํ์ธํ๊ณ , ์บ์คํ ํ๊ณ , ์ ์ ํ์ง ์์ ๊ฒฝ์ฐ false๋ฅผ ๋๋ ค๋ณด๋ผ ์ ์๋ค.
๊ทธ๋ฌ๋ ๊ฐ๋์ฉ์ ์ปดํ์ผ๋ฌ์๊ฒ null์ด ์๋๋ผ๋ ๊ฒ์ ๋ช ์ํด์ค์ผ๋ก์จ null์ ๋ค๋ฃฐ ์๋ ์๋ค.
null ์๋ ๋จ์ธ: โ!!โ
fun ignoreNulls(s: String?) {
val sNotNull: String = s!!
println(sNotNull.length)
}๋ง์ฝ s๊ฐ null์ผ ๊ฒฝ์ฐ์๋ ์ด๋ค ์ผ์ด ์๊ธธ๊น? NullPointerException์ ๊ฐ์ ์๋ฌ๋ฅผ ๋ฐํ์์ ๋์ง๋ ๋ฑ์ ์๋ฌ๊ฐ ๋ฐ์ํ๋ค. ๋ฐ๋ผ์ null ์๋ ๋จ์ธ์ NPE์ ๊ฐ์ ์๋ฌ๋ฅผ ๊ฐ์ํ ๋๋ง ์ฌ์ฉํ ์ ์๋ค.
๊ทธ๋ฌ๋ null ์๋ ๋จ์ธ์ ์ ์ฉํ๊ฒ ํ์ฉํ ์ ์๋ ์ํฉ๋ ์๋๋ฐ, ์๋ฅผ ๋ค์ด ํ ํจ์์์ ์ด๋ฏธ null์ด ์๋์ ์ฒดํฌํ๊ณ ๊ทธ ๊ฐ์ ๋ค๋ฅธ ํจ์์์ ํ์ฉํ ๋, ์ปดํ์ผ๋ฌ๋ ํด๋น ๊ฐ์ด null์ด ์๋๋ผ๋ ๊ฒ์ ๋ชจ๋ฅธ๋ค. ๋ฐ๋ผ์ ๋ช ์์ ์ผ๋ก ํํํด์ฃผ์ด์ผ ๋๋ค.
class CopyRowAction(val list: JList<String>) : AbstractAction() {
override fun isEnabled(): Boolean =
list.selectedValue != null
override fun actionPerformed(e: ActionEvent) {
val value = list.selectedValue!!
// copy value to clipboard
}
}๋ง์ฝ ์์ ์ํฉ์์ !!๋ฅผ ์ฌ์ฉํ์ง ์๋๋ค๋ฉด, val value = list.selectedValue ?: return๋ฅผ ํตํด non-null ํ์
์ ํ๋ณดํ ์ ์๋ค.
์ปดํ์ผ๋ฌ๋ ์๋ฌ๊ฐ ๋ฐ์ํ๋ฉด ๋ฐ์ํ line์ ์ถ์ ํ์ง ๋ช ๋ น์ด๋ฅผ ์ฒดํฌํ์ง ์๊ธฐ ๋๋ฌธ์, ํ ์ค์ !!๋ฅผ ๋ ๊ฐ ์ด์ ์ฌ์ฉํ๋ ๊ฒ์ ๋ฐ๋์งํ์ง ๋ชปํ๋ค.
person.company!!.address!!.country //๋ฐ๋์งํ์ง ๋ชปํ ์ฝ๋
let ํจ์
let ํจ์๋ nullable์ ๋ค๋ฃจ๊ธฐ ๋ ์ฝ๊ฒ ๋ง๋ค์ด์ค๋ค. ์ฃผ๋ก null์ด ๋ถ๊ฐ๋ฅํ ํจ์์ ํ๋ผ๋ฏธํฐ๋ก nullableํ ํ์ ์ ๊ฐ์ ๋๊ธฐ๋ ค๊ณ ํ ๋ let์ ํ์ฉํ๋ค.
fun sendEmailTo(email: String) { /*...*/ }
>>> val email: String? = ...
>>> sendEmailTo(email)
ERROR: Type mismatch: inferred type is String? but String was expected
//์๋ฃจ์
์ค ํ๋: null์ด ์๋์ ์ฒดํฌํ๋ค.
if (email != null) sendEmailTo(email)
let ํจ์๋ฅผ ํ์ฉํ๋ฉด ์์ ๋ฌธ์ ๋ฅผ ํด๊ฒฐํ ์ ์๋ค. null์ด ์๋ ๋๋ ์๋์ผ๋ก ์์ฑ๋ it์ผ๋ก ํ์ฉ ๊ฐ๋ฅํ๋ค.
์ฌ๋ฌ ๊ฐ์ null์ธ์ง ์๋์ง ์ฒดํฌํด์ผ ํ ๋๋ ์ค์ฒฉ let์ ํ์ฉํ์ฌ ํ ์ ์๋ค. ๊ทธ๋ฌ๋ ์ด๋ด ๊ฒฝ์ฐ ๊ต์ฅํ ์ฝ๋๊ฐ ๋ณต์กํด์ง ์ ์๊ธฐ ๋๋ฌธ์, ํ๋ฒํ if ์กฐ๊ฑด๋ฌธ์ ํ์ฉํ๋ ๊ฒ์ด ๋์ฑ ๊ถ์ฅ๋๋ค.
null ๋ถ๊ฐ๋ฅ ํ์
์ ์ง์ฐ ์ด๊ธฐํ
๋ง์ ๊ฒฝ์ฐ ์ด๊ธฐํ ๋ฉ์๋๋ ์ธ์คํด์ค๊ฐ ์์ฑ๋ ์งํ ์งํ๋๋ค. ํ๋ฒํ ๊ฒฝ์ฐ, ์ฝํ๋ฆฐ์ ๊ฐ์ ์์ฑ์์์ ์ด๊ธฐํํ ๊ฒ์ ๊ถ์ฅํ๊ณ ํ๋กํผํฐ๊ฐ non-null์ผ ๊ฒฝ์ฐ์ non-null ์ด๊ธฐํ ๊ฐ์ ์ ๊ณตํด์ผ ํ๋ค. ๊ทธ๋ ์ง ์์ ๊ฒฝ์ฐ์๋ nullable ํ์ ์ ๋์ ์ฌ์ฉํด์ผ ํ๋ค.
class MyService {
fun performAction(): String = "foo"
}
class MyTest {
private lateinit var myService: MyService
@Before fun setUp() {
myService = MyService()
}
@Test fun testAction() {
Assert.assertEquals("foo", myService.performAction())
}
}์ง์ฐ ์ด๊ธฐํ์ ํ๋กํผํฐ๋ ํญ์ var์ด๋ผ๋ ๊ฒ์ ๋ช
์ํ์(์ฝ๊ธฐ์ ์ฐ๊ธฐ ๊ฐ๋ฅ). lateinit์ ํ์ฉํ๋ฉด null์ด ๋ถ๊ฐ๋ฅํ ํ์
์ ์ฌ์ฉํ ์ ์์ง๋ง, ํ๋กํผํฐ ์ด๊ธฐํ์ ์ ์ ๊ทผํ ๊ฒฝ์ฐ์๋ โlateinit property myService has not been initializedโ์ ๊ฐ์ ์๋ฌ๊ฐ ๋ฐ์ํ๋ค.
nullable ํ์
์ ํ์ฅ
//ํ์ฅ ํจ์๋ฅผ ํธ์ถํ๋ ๊ฒฝ์ฐ
fun verifyUserInput(input: String?) {
if (input.isNullOrBlank()) {
println("Please fill in the required fields")
}
}nullable ํ์ ์ผ๋ก ํ์ฅ ํจ์๋ฅผ ํธ์ถํ๋ ๊ฒฝ์ฐ, nullable ํ์ ์ผ๋ก ํด๋น ํจ์๋ฅผ ๋ถ๋ฅผ ์ ์๊ฒ ๋๋ค. ๋ํ null ํ์ ์ ๊ฐ์ง ์ ์๊ธฐ ๋๋ฌธ์ ์ด๋ฅผ ๋ช ์์ ์ผ๋ก ์ฒดํฌํด์ค์ผ ํ๋ค.
ํ์
ํ๋ผ๋ฏธํฐ์ nullable
๋ชจ๋ ํจ์์ ํ์
ํ๋ผ๋ฏธํฐ๋ nullable์ผ ์ ์๋ค. ?๋ก ๋๋์ง ์์๋ ํ์
ํ๋ผ๋ฏธํฐ๋ null ํ์
์ ํ์ฉํ๋ค.
// ํ์
ํ๋ผ๋ฏธํฐ๋ ์ ์ผํ๊ฒ **Any?**๋ก ์ถ๋ก ๋๋ฏ๋ก nullableํ๋ค.
fun <T> some1(): T = TODO()
// ์ํ์ ๋์ด null์ด ๋ถ๊ฐ๋ฅํ๊ฒ ํ ์๋ ์๋ค.
fun <T : Any> some2(): T = TODO()์๋ฐ์ nullable
์ฝํ๋ฆฐ์์์ ๋ฌ๋ฆฌ ์๋ฐ์์๋ nullable ํ์ ์ ์ง์ํ์ง ์๋๋ค. ๊ทธ๋ ๋ค๋ฉด ์๋ฐ์ ์ฝํ๋ฆฐ์ ๊ฐ์ด ์ฌ์ฉํ ๋๋ ์ด๋ป๊ฒ ๋ ๊น?
์ฒซ์งธ, ์๋ฐ์์๋ ๊ฐ๋์ฉ @Nullable String๋ฑ์ผ๋ก null์ ๋ํ ์ ๋ณด๋ฅผ ํ์ํด์ค๋ค. ์ด๋ ์ฝํ๋ฆฐ์ String?๊ณผ ๋์น๋๋ค.
๊ทธ๋ฐ๋ฐ ์ด๋ ๊ฒ nullable์ ๋ช
์ํด์ฃผ๋ ์ฃผ์์ด ์๋ ๊ฒฝ์ฐ๋ ํ๋ซํผ ํ์
์ ์ฌ์ฉํ๋ค.
ํ๋ซํผ ํ์
์ฝํ๋ฆฐ์ด null์ ๋ํ ์ ๋ณด๋ฅผ ์์ ์๋ ํ์ ์ผ ๊ฒฝ์ฐ, ์ฒ๋ฆฌ๋ฅผ ๊ฐ๋ฐ์์๊ฒ ์ ์ ์ผ๋ก ๋งก๊ธฐ๊ฒ ๋๋ค.
/* Java */
public class Person {
private final String name;
public Person(String name) {
this.name = name;
}
public String getName() {
return name;
}
}getName์ null์ ๋ฐํํ ๊น, ์๋๋ฉด ๋ฐํํ์ง ์์๊น? ์ฝํ๋ฆฐ ์ปดํ์ผ๋ฌ๋ ์ด ๊ฒฝ์ฐ์ null์ธ์ง ์๋์ง ๋ชจ๋ฅด๊ธฐ ๋๋ฌธ์ ๊ฐ๋ฐ์๊ฐ ์ค์ค๋ก ํด๊ฒฐํด์ผ ํ๋ค. ๊ฐ์ด null์ด ์๋๋ผ๋ ๊ฒ์ ํ์ ํ๋ค๋ฉด ์ถ๊ฐ์ ์ธ ํ์ธ ์์ด ์ฌ์ฉํ ์๋ ์๋ค. ์ฝํ๋ฆฐ์์๋ ์์ ์๋ฐ ์ฝ๋๋ฅผ ๋ค์๊ณผ ๊ฐ์ด ๋ค๋ฃฐ ์ ์๋ค.
fun yellAtSafe(person: Person) {
println((person.name ?: "Anyone").toUpperCase() + "!!!")
}
>>> yellAtSafe(Person(null))
ANYONE!!!์์
์๋ฐ ๋ฉ์๋๋ฅผ ์ฝํ๋ฆฐ์์ ์ค๋ฒ๋ผ์ด๋ฉ ํ ๊ฒฝ์ฐ, ํ๋ผ๋ฏธํฐ์ ๋ฆฌํดํ์ ์ nullable ๋๋ non-null๋ก ํ ์ง ๊ฒฐ์ ํ ์ ์๋ค.
/* Java */
interface StringProcessor {
void process(String value);
}
**//๋ ๊ฐ์ง ์ค๋ฒ๋ผ์ด๋ฉ ๋ชจ๋ ํ์ฉ ๊ฐ๋ฅ**
class StringPrinter : StringProcessor {
override fun process(value: String) {
println(value)
}
}
class NullableStringPrinter : StringProcessor {
override fun process(value: String?) {
if (value != null) {
println(value)
}
}
}์ด๋ ์๋ฐ ๋ฉ์๋๋ฅผ ์ค๋ฒ๋ผ์ด๋ํ๊ณ , ๋ฉ์๋ ๋ณ์๊ฐ null ๋ถ๊ฐ๋ฅํ ํ์ ์ผ๋ก ์ ์ธ๋์์ ๊ฒฝ์ฐ ์ปดํ์ผ๋ฌ๋ null ์๋์ ๋จ์ธํ๋ ํ์ธ์ ์๋์ผ๋ก ์ถ๊ฐํด์ค๋ค.
์์ ํ์
์๋ฐ๋ ์์ ํ์
๊ณผ ๋ ํผ๋ฐ์ค ํ์
์ ๋ช
ํํ ๊ตฌ๋ถ์ ๊ฐ์ง๋ค. ์์ ํ์
์ ๊ทธ๊ฒ์ ๊ฐ์ ๋ฐ๋ก ๊ฐ์ง๊ณ ์๊ณ , ๋ ํผ๋ฐ์ค ํ์
์ ๊ฒฝ์ฐ ํด๋น ๊ฐ์ฒด๋ฅผ ๊ฐ์ง๋ ๋ฉ๋ชจ๋ฆฌ ์์น๋ฅผ ๊ฐ์ง๊ณ ์๋ค. ์์ ํ์
์ ๊ฒฝ์ฐ ํด๋น ๊ฐ์ ์ ์ฅํ๊ฑฐ๋ ๋๊ฒจ์ฃผ๊ธฐ ๋ ํธํ์ง๋ง, ์ปฌ๋ ์
์ ์ ์ฅํ๊ฑฐ๋ ๋ฉ์๋๋ฅผ ํธ์ถํ ์๋ ์๊ธฐ ๋๋ฌธ์ ์๋ฐ์์๋ ํน๋ณํ wrapper ํ์
(Integer์ ๊ฐ์)์ ์ ๊ณตํ๋ค. ๋ฐ๋ผ์ ์ปฌ๋ ์
์ ์ฌ์ฉํ๊ธฐ ์ํด์๋ Collection<Integer>๊ณผ ๊ฐ์ด ์ฌ์ฉํด์ผ ํ๋ค.
๊ทธ๋ฐ๋ฐ ์ฝํ๋ฆฐ์ ์์ ํ์ ๊ณผ ๋ํผ ํ์ ์ ๊ตฌ๋ถ์ด ์๋ค. ์ฝํ๋ฆฐ์ ๋ด๋ถ์ ์ผ๋ก ๋ฐํ์์ ๊ฐ์ฅ ํจ์จ์ ์ธ ๋ฐฉ์์ผ๋ก ์ฒ๋ฆฌํ๋ค. ์๋์ ๊ฐ์ด ๋ ํธ๋ฆฌํ๊ฒ ์ฌ์ฉํ ์ ์๋ค.
val i: Int = 1
val list: List<Int> = listOf(1, 2, 3)์๋ฐ์ ์์ ํ์ ๊ณผ ์ผ์นํ๋ ํ์ ๋ฆฌ์คํธ๋ ๋ค์๊ณผ ๊ฐ๋ค.
Integer typesโByte, Short, Int, Long
Floating-point number typesโFloat, Double
Character typeโChar
Boolean typeโBoolean
Number conversions
์ฝํ๋ฆฐ๊ณผ ์๋ฐ์ ์ค์ํ ์ฐจ์ด๋ ์ซ์์ ๋ณํ์ด๋ค. ์ฝํ๋ฆฐ์ ํ ์ซ์ ํ์ ์์ ๋ค๋ฅธ ์ซ์ ํ์ ์ผ๋ก ๋ณํํด์ฃผ์ง ์๋๋ค(๋ณํ๋๋ ๊ฐ์ด ๋ ํฌ๋ค๊ณ ํ๋๋ผ๋). ์๋ฅผ ๋ค์ด ๋ค์๊ณผ ๊ฐ์ ์ฝ๋๋ type mismatch ์๋ฌ๋ฅผ ์์ฑํ๋ค.
val i = 1
val l: Long = i๋ฐ๋ผ์ i.toLong()์ฒ๋ผ ํ์
๋ณํ์ ๋ช
์์ ์ผ๋ก ๋ณด์ฌ์ค์ผ ํ๋ค. ์ด๋ฌํ ํ์
๋ณํ์ ์ซ์ ๋ฟ๋ง ์๋๋ผ toByte(), toShort(), toChar()์ฒ๋ผ ๋ค๋ฅธ ํ์
์๋ ์ ์ฉ๋๋ค. ์ด๋ฌํ ํจ์๋ ์์ ํ์
์์ ํฐ ํ์
, ํฐ ํ์
์์ ์์ ํ์
๋ชจ๋ ์ง์ํ๋ค.
๐ ์ฐธ๊ณ : ์์ ํ์ ๋ฆฌํฐ๋ด Long: use the L suffix: 123L. Double: use the standard representation of floating-point numbers: 0.12, 2.0, 1.2e10, 1.2e-10. Float: use the f or F suffix: 123.4f, .456F, 1e3f. Hexadecimal literals: use the 0x or 0X prefix (0xCAFEBABL). Binary literals: 0b or 0B prefix (0b000000101).
๋ง์ฝ ์ด๋ฌํ ์ซ์ ๋ฆฌํฐ๋ด์ ์ฌ์ฉํ๋ค๋ฉด, ์ปดํ์ผ๋ฌ๊ฐ ํ์ ๋ณํ์ ๋ช ์์ ์ผ๋ก ํ ์ ์๋ค.
fun foo(l: Long) = println(l)
>>> val b: Byte = 1
>>> val l = b + 1L **//๋ฆฌํฐ๋ด์ ์ฌ์ฉํ์ฌ ํ์
๋ณํ ๊ฐ๋ฅ**
>>> foo(42)
42โAnyโ์ โAny?โ: ์ต์์ ํ์
๋ง์น ๊ฐ์ฒด์์ Object๊ฐ ๋ชจ๋ ํด๋์ค์ ๋ฃจํธ๊ฐ ๋๋ ๊ฒ์ฒ๋ผ, ํ์
์ ์ต์์๋ Any๊ฐ ๋๋ค. ์๋ฐ์์๋ Wrapper๋ก ๊ฐ์ผ ํ์
์ ๋ฃจํธ๊ฐ Object๊ฐ ๋์ง๋ง, ์ฝํ๋ฆฐ์์๋ ๋ชจ๋ ํ์
์ ์ต์์ ํ์
์ด Any๊ฐ ๋๋ค๋ ์ฐจ์ด๊ฐ ์๋ค.
โUnitโ: ์ฝํ๋ฆฐ์ โVoidโ
์ฝํ๋ฆฐ์ Unit์ ์๋ฐ์ void์ ๊ฐ์ ์ญํ ์ ํ๋ค. ์ฆ, ๋๋ ค์ค ๋งํ ๋ฆฌํด ํ์
์ด ๋ฑํ ์์ ๋ ์ฌ์ฉ๋๋ค. Unit์ ์๋ต ๊ฐ๋ฅํ๋ค.
fun f(): Unit { ... }
fun f(): { ... }Unit๊ณผ void์ ์ฐจ์ด์ ์ ๋ฐ๋ก Unit์ด ๋ณธ๊ฒฉ์ ์ธ ํ์
์ด๋ผ๋ ๊ฒ์ด๋ค. void์ ๋ฌ๋ฆฌ, Unit์ ํ์
์์๋ก ์ฌ์ฉ๋ ์ ์๋ค. ๋จ, Unit ํ์
์ ๋ํด์๋ ๋จ ํ๋์ ๊ฐ๋ง ์กด์ฌํ๋ค-๋ฐ๋ก implicitly์ด๋ค. ๋ํ return Unit์ ์์ฑํ ํ์๊ฐ ์๋๋ฐ, ์ปดํ์ผ๋ฌ์ ์ํด ์๋์ผ๋ก ์ถ๊ฐ๋๊ธฐ ๋๋ฌธ์ด๋ค.
Nothing ํ์
: ์ด ํจ์๋ ๋ฆฌํดํ์ง ์์
๊ฐ์ ๋ฆฌํดํด์ค๋ค๋ ๊ฐ๋ ์ ํจ์๊ฐ ์ฑ๊ณต์ ์ผ๋ก ๋๋์ง ์์ ๊ฒฝ์ฐ์ ์ฌ์ฉํ ์ ์๋ค. ์ด๋ Nothing ํ์ ์ ์ฌ์ฉํด์ฃผ๋ฉด ํจ์๊ฐ ์ ์์ ์ผ๋ก ๋๋์ง ์์์ ํํํ ์ ์๋ค. ์ค์ง ๋ฐํ ํ์ ์ผ๋ก๋ง ์ธ ์ ์์ผ๋ฉฐ, ์ด๋ ํ ๊ฐ๋ ๊ฐ์ง์ง ์๋๋ค.
์ปฌ๋ ์
๊ณผ ๋ฐฐ์ด
fun readNumbers(reader: BufferedReader): List<Int?> {
val result = ArrayList<Int?>()
for (line in reader.lineSequence()) {
try {
val number = line.toInt()
result.add(number)
}
catch(e: NumberFormatException) {
result.add(null)
}
}
return result
}List<Int?>๋ Int?: ํ์
์ ๊ฐ์ง๊ณ ์๋ ๋ฐฐ์ด์ด๋ค. List<Int>?์์ ์ฐจ์ด๋ ๋ค์๊ณผ ๊ฐ๋ค.

Read-only and mutable collections
๊ธฐ๋ณธ์ ์ผ๋ก ์ฝํ๋ฆฐ์ด ๊ฐ์ง ์ปฌ๋ ์
์ ์ ๋ถ ๋ณ๊ฒฝํ ์ ์๋ ์ปฌ๋ ์
์ด๋ค. ๊ทธ๋ฐ๋ฐ MutableCollection์ ์ฌ์ฉํ๋ฉด ์ปฌ๋ ์
์ ๋ณ๊ฒฝํ ์ ์๋ ์ปฌ๋ ์
์ ์ฌ์ฉํ ์ ์๋ค. val๊ณผ var์ ๋ถ๋ฆฌํ ๊ฒ์ฒ๋ผ, ์ฝ๊ธฐ ์ ์ฉ ์ปฌ๋ ์
๊ณผ ๋ณ๊ฒฝ ๊ฐ๋ฅ ์ปฌ๋ ์
์ ๋ถ๋ฆฌํ๋ฉด ํ๋ก๊ทธ๋จ ์์ ๋ฐ์ดํฐ์ ๋ฌด์จ ์ผ์ด ์ผ์ด๋๊ณ ์๋์ง ๋ ์ฝ๊ฒ ์ดํดํ ์ ์๋ค. ๋ง์ฝ Collection์ ์ฌ์ฉํ๋ค๋ฉด ๋ฐ์ดํฐ์ ๋ณ๊ฒฝ์ ์ผ์ด๋๊ณ ์์ง ์์์, MutableCollection์ ๋ฐ์ดํฐ๋ฅผ ๋๊ฒจ์ค๋ค๋ฉด ๋ณ๊ฒฝ์ ์๋ํ๊ณ ์๋ ๊ฒ์์ ์ ์ ์๋ค.
fun <T> copyElements(source: Collection<T>, target: MutableCollection<T>) {
for (item in source) {
target.add(item) **//๋ณ๊ฒฝ ๊ฐ๋ฅํ ์ปฌ๋ ์
์ ๋๊ฒจ์ค**
}
}
>>> val source: Collectionthread-safe<Int> = arrayListOf(3, 5, 7)
>>> val target: MutableCollection<Int> = arrayListOf(1)
>>> copyElements(source, target)
>>> println(target)
[1, 3, 5, 7]๊ทธ๋ ๋ค๊ณ ํด์ collection์ด ๊ฐ๋ฆฌํค๋ ๋ฐ์ดํฐ๊ฐ ํญ์ ๋ณ๊ฒฝ ๋ถ๊ฐ๋ฅํ๋ค๋ ๊ฒ์ ์๋๋ค. ๋ค๋ฅธ ๋ ํผ๋ฐ์ค๋ ๋ณ๊ฒฝ ๊ฐ๋ฅํ MutableCollection์ผ ์๋ ์๋ค! ๊ทธ๋ฐ๋ฐ ๋ค์ํ ๋ ํผ๋ฐ์ค๊ฐ ํ๋์ ๋ฐ์ดํฐ๋ฅผ ๊ฐ๋ฆฌํค๊ณ ์์ ๋ ConcurrentModificationException์ด ๋ฐ์ํ ์ ์์ผ๋ฏ๋ก, ์ฝ๊ธฐ ์ ์ฉ๊ณผ ๋ณ๊ฒฝ ๊ฐ๋ฅ์ ๊ตฌ๋ถํด์ฃผ๋ ๊ฒ์ด ํ์์ ์ด๋ค.
Kotlin collections and Java
์ฝํ๋ฆฐ๊ณผ ์๋ฐ ์ปฌ๋ ์ ์ ์๋ก ์ํธ์์ฉํ๋ ์ธํฐํ์ด์ค๊ฐ ์กด์ฌํ๋ค. ์ด ์ฌ์ด์๋ ๋ณํ์ด ํ์ ์์ผ๋ฉฐ, ๋ฐ์ดํฐ๋ฅผ ๋ณต์ฌํ๊ฑฐ๋ wrapper๋ฅผ ์์ฑํ ํ์๊ฐ ์๋ค. ๊ทธ๋ฐ๋ฐ ๋ชจ๋ ์๋ฐ ์ปฌ๋ ์ ์๋ 2๊ฐ์ ์ฝํ๋ฆฐ ์ปฌ๋ ์ , ์ฆ ์ฝ๊ธฐ ์ ์ฉ๊ณผ ๋ณ๊ฒฝ ๊ฐ๋ฅํ ์ปฌ๋ ์ ์ด ์กด์ฌํ๋ค.
์๋ฐ๋ ์ฝ๊ธฐ ์ ์ฉ๊ณผ ๋ณ๊ฒฝ ๊ฐ๋ฅ์ ๊ตฌ๋ถํ์ง ์๊ธฐ ๋๋ฌธ์ ์ฝ๊ธฐ ์ ์ฉ ์ปฌ๋ ์ ์ ๋ณ๊ฒฝํ๊ฒ ๋ ์๋ ์๋ค. ์ด๋ ์ฝํ๋ฆฐ์ ์ฝ๊ธฐ ์ ์ฉ ์ปฌ๋ ์ ์ ์๋ฐ๊ฐ ๋ณ๊ฒฝํ๋ ค๋ ๊ฒฝ์ฐ ๊ทธ ํธ์ถ์ ๊ฑฐ์ ํ๋ค.
/* Java */
// CollectionUtils.java
public class CollectionUtils {
public static List<String> uppercaseAll(List<String> items) {
for (int i = 0; i < items.size(); i++) {
items.set(i, items.get(i).toUpperCase()); //๋ณ๊ฒฝ ์๋
}
return items;
}
}
// Kotlin
// collections.kt
fun printInUppercase(list: List<String>) {
println(CollectionUtils.uppercaseAll(list))
println(list.first())
}
>>> val list = listOf("a", "b", "c")
>>> printInUppercase(list)
[A, B, C]
A๋ฐ๋ผ์ ์ฝํ๋ฆฐ ์ปฌ๋ ์ ์ ์๋ฐ์๊ฒ ๋๊ฒจ์ค ๋๋ ์ ํํ ํ๋ผ๋ฏธํฐ ํ์ ์ ๊ฐ๋ฐ์๊ฐ ์์ฑํด์ผ ํ๋ค.
๋ฐฐ์ด
์ฝํ๋ฆฐ์์ ๋ฐฐ์ด์ ์์ฑํ๊ธฐ ์ํด์๋ ๋ค์๊ณผ ๊ฐ์ ๊ฒฝ์ฐ๋ฅผ ๊ฑฐ์น ์ ์๋ค.
arrayOf ํจ์๋ก ๋ฐฐ์ด์ ์์ฑํ๋ค.
arrayOfNulls ํจ์๋ก null์ ํฌํจํ ๋ฐฐ์ด์ ๋ง๋ ๋ค.
Array ์์ฑ์๋ก ๋ฐฐ์ด๊ณผ ๋๋ค๋ฅผ ๋ง๋ ๋ค. ๊ฐ ์์๋ค์ ๋๋ค๋ก ์ด๊ธฐํ๋๋ค. ๋ฐฐ์ด์ non-null ์์๋ก ์ด๊ธฐํํ๋ค.
Last updated