{"componentChunkName":"component---src-containers-post-index-tsx","path":"/backend/kotlin-in-action/8장-기본_타입_컬렉션_배열/","result":{"pageContext":{"next":{"id":"e6422292-3901-5f36-bb54-df3880644ca6","html":"<h2 id=\"-71-nullpointerexception을-피하고-값이-없는-경우-처리-널-가능성\" style=\"position:relative;\"><a href=\"#-71-nullpointerexception%EC%9D%84-%ED%94%BC%ED%95%98%EA%B3%A0-%EA%B0%92%EC%9D%B4-%EC%97%86%EB%8A%94-%EA%B2%BD%EC%9A%B0-%EC%B2%98%EB%A6%AC-%EB%84%90-%EA%B0%80%EB%8A%A5%EC%84%B1\" aria-label=\" 71 nullpointerexception을 피하고 값이 없는 경우 처리 널 가능성 permalink\" class=\"post-anchor before\"><svg aria-hidden=\"true\" focusable=\"false\" height=\"16\" version=\"1.1\" viewBox=\"0 0 16 16\" width=\"16\"><path fill-rule=\"evenodd\" d=\"M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z\"></path></svg></a>📖 7.1 NullPointerException을 피하고 값이 없는 경우 처리: 널 가능성</h2>\n<ul>\n<li>코틀린을 포함한 최신 언어에서 null에 대한 접근 방법은 가능한 이 문제를 실행 시점에서 컴파일 시점으로 옮기는 것</li>\n<li>널이 될 수 있는지 여부를 타입 시스템에 추가하으로써 컴파일러가 여러 가지 오류를 컴파일 시 미리 감지해서 실행 시점에 발생할 수 있는 예외의 가능성을 줄일 수 있다.</li>\n</ul>\n<h2 id=\"-72-널이-될-수-있는-타입으로-널이-될-수-있는-변수-명시\" style=\"position:relative;\"><a href=\"#-72-%EB%84%90%EC%9D%B4-%EB%90%A0-%EC%88%98-%EC%9E%88%EB%8A%94-%ED%83%80%EC%9E%85%EC%9C%BC%EB%A1%9C-%EB%84%90%EC%9D%B4-%EB%90%A0-%EC%88%98-%EC%9E%88%EB%8A%94-%EB%B3%80%EC%88%98-%EB%AA%85%EC%8B%9C\" aria-label=\" 72 널이 될 수 있는 타입으로 널이 될 수 있는 변수 명시 permalink\" class=\"post-anchor before\"><svg aria-hidden=\"true\" focusable=\"false\" height=\"16\" version=\"1.1\" viewBox=\"0 0 16 16\" width=\"16\"><path fill-rule=\"evenodd\" d=\"M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z\"></path></svg></a>📖 7.2 널이 될 수 있는 타입으로 널이 될 수 있는 변수 명시</h2>\n<ul>\n<li>코틀린과 자바의 첫 번째이자 가장 중요한 차이는 코틀린 타입 시스템이 널이 될 수 있는 타입을 명시적으로 지원한다는 점이다.</li>\n</ul>\n<deckgo-highlight-code language=\"kotlin\" terminal=\"carbon\" theme=\"one-dark\"  >\n          <code slot=\"code\">fun strLen(s: String) = s.length</code>\n        </deckgo-highlight-code>\n<ul>\n<li>null이 인자로 들어올 수 없다면 위와 같이 정의 가능</li>\n</ul>\n<deckgo-highlight-code language=\"kotlin\" terminal=\"carbon\" theme=\"one-dark\"  >\n          <code slot=\"code\">fun main() {\n    strLen(null) // error\n}</code>\n        </deckgo-highlight-code>\n<ul>\n<li>컴파일 시, 오류가 발생한다.</li>\n</ul>\n<deckgo-highlight-code language=\"kotlin\" terminal=\"carbon\" theme=\"one-dark\"  >\n          <code slot=\"code\">fun strLenSafe(s: String?) = s.length() // error</code>\n        </deckgo-highlight-code>\n<ul>\n<li>타입 이름 뒤에 물음표를 붙이면 그 타입의 변수나 프로퍼티에 null 참조를 저장할 수 있다.</li>\n<li>널이 될 수 있는 타입 값의 메서드를 직접 호출할 수는 없다.</li>\n</ul>\n<deckgo-highlight-code language=\"kotlin\" terminal=\"carbon\" theme=\"one-dark\"  >\n          <code slot=\"code\">fun main() {\n    val x: String? = null\n    var y: String = x // error\n}</code>\n        </deckgo-highlight-code>\n<ul>\n<li>널이 될 수 있는 값을 널이 될 수 없는 타입의 변수에 대입할 수 없다.</li>\n</ul>\n<deckgo-highlight-code language=\"kotlin\" terminal=\"carbon\" theme=\"one-dark\"  >\n          <code slot=\"code\">fun main() {\n    val x: String? = null\n    strLen(x) // error\n}</code>\n        </deckgo-highlight-code>\n<ul>\n<li>널이 될 수 있는 타입의 값을 null이 아닌 타입의 파라미터를 받는 함수에 전달할 수 없다.</li>\n</ul>\n<deckgo-highlight-code language=\"kotlin\" terminal=\"carbon\" theme=\"one-dark\"  >\n          <code slot=\"code\">fun strLenSafe(s: String?): Int = if (s != null) s.length else 0</code>\n        </deckgo-highlight-code>\n<ul>\n<li>null과 비교하고 나면 컴파일러는 그 사실을 기억하고 null이 아님이 확실한 영역에서는 해당 값을 null이 아닌 타입의 값처럼 사용할 수 있다.</li>\n</ul>\n<h2 id=\"-73-타입의-의미-자세히-살펴보기\" style=\"position:relative;\"><a href=\"#-73-%ED%83%80%EC%9E%85%EC%9D%98-%EC%9D%98%EB%AF%B8-%EC%9E%90%EC%84%B8%ED%9E%88-%EC%82%B4%ED%8E%B4%EB%B3%B4%EA%B8%B0\" aria-label=\" 73 타입의 의미 자세히 살펴보기 permalink\" class=\"post-anchor before\"><svg aria-hidden=\"true\" focusable=\"false\" height=\"16\" version=\"1.1\" viewBox=\"0 0 16 16\" width=\"16\"><path fill-rule=\"evenodd\" d=\"M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z\"></path></svg></a>📖 7.3 타입의 의미 자세히 살펴보기</h2>\n<ul>\n<li>타입\n<ul>\n<li>가능한 값의 집합과 그런 값들에 대해 수행할 수 있는 연산의 집합</li>\n</ul>\n</li>\n<li>자바의 타입 시스템은 null을 제대로 다루지 못한다.\n<ul>\n<li>ex. String 타입의 변수에는 null, String 모두 들어갈 수 있지만 완전히 다르다. 심지어, instanceof 연산자도 null이 String이 아니라고 답한다.</li>\n</ul>\n</li>\n<li>코틀린의 널이 될 수 있는 타입은 이런 문제에 대해 종합적인 해법을 제공한다.</li>\n</ul>\n<h2 id=\"-74-안전한-호출-연산자로-null-검사와-메서드-호출-합치기-\" style=\"position:relative;\"><a href=\"#-74-%EC%95%88%EC%A0%84%ED%95%9C-%ED%98%B8%EC%B6%9C-%EC%97%B0%EC%82%B0%EC%9E%90%EB%A1%9C-null-%EA%B2%80%EC%82%AC%EC%99%80-%EB%A9%94%EC%84%9C%EB%93%9C-%ED%98%B8%EC%B6%9C-%ED%95%A9%EC%B9%98%EA%B8%B0-\" aria-label=\" 74 안전한 호출 연산자로 null 검사와 메서드 호출 합치기  permalink\" class=\"post-anchor before\"><svg aria-hidden=\"true\" focusable=\"false\" height=\"16\" version=\"1.1\" viewBox=\"0 0 16 16\" width=\"16\"><path fill-rule=\"evenodd\" d=\"M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z\"></path></svg></a>📖 7.4 안전한 호출 연산자로 null 검사와 메서드 호출 합치기: ?.</h2>\n<ul>\n<li><code>?.</code>는 null 검사와 메서드 호출을 한 연산으로 수행</li>\n</ul>\n<deckgo-highlight-code language=\"kotlin\" terminal=\"carbon\" theme=\"one-dark\"  >\n          <code slot=\"code\">class Employee(val name: String, val manager: Employee?)\n\nfun managerName(employee: Employee): String? = employee.manager?.name\n\nfun main() {\n    val ceo = Employee(&quot;Da Boss&quot;, null)\n    val developer = Employee(&quot;Bob Smith&quot;, ceo)\n    println(managerName(developer))\n    println(managerName(ceo))\n}</code>\n        </deckgo-highlight-code>\n<ul>\n<li>Employee로 프로퍼티 접근 시 안전한 호출을 사용하는 방법</li>\n</ul>\n<deckgo-highlight-code language=\"kotlin\" terminal=\"carbon\" theme=\"one-dark\"  >\n          <code slot=\"code\">class Address(val streetAddress: String, val zipCode: Int, val city: String, val country: String)\n\nclass Company(val name: String, val address: Address?)\n\nclass Person(val name: String, val company: Company?)\n\nfun Person.countryName(): String {\n    val country = this.company?.address?.country\n    return if (country != null) country else &quot;Unknown&quot;\n}\n\nfun main() {\n    val person = Person(&quot;Dmitry&quot;, null)\n    println(person.countryName())\n    // Unknown\n}</code>\n        </deckgo-highlight-code>\n<ul>\n<li>null 검사를 한줄로 연쇄적으로 할 수 있다!</li>\n</ul>\n<h2 id=\"-75-엘비스-연산자로-null에-대한-기본값-제공-\" style=\"position:relative;\"><a href=\"#-75-%EC%97%98%EB%B9%84%EC%8A%A4-%EC%97%B0%EC%82%B0%EC%9E%90%EB%A1%9C-null%EC%97%90-%EB%8C%80%ED%95%9C-%EA%B8%B0%EB%B3%B8%EA%B0%92-%EC%A0%9C%EA%B3%B5-\" aria-label=\" 75 엘비스 연산자로 null에 대한 기본값 제공  permalink\" class=\"post-anchor before\"><svg aria-hidden=\"true\" focusable=\"false\" height=\"16\" version=\"1.1\" viewBox=\"0 0 16 16\" width=\"16\"><path fill-rule=\"evenodd\" d=\"M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z\"></path></svg></a>📖 7.5 엘비스 연산자로 null에 대한 기본값 제공: ?:</h2>\n<ul>\n<li>엘비스 연산자\n<ul>\n<li>null 대신 사용할 기본값을 지정할 때 편리하게 사용할 수 있는 연산자</li>\n<li>null 복합 연산자라고도 부름</li>\n</ul>\n</li>\n</ul>\n<deckgo-highlight-code language=\"kotlin\" terminal=\"carbon\" theme=\"one-dark\"  >\n          <code slot=\"code\">fun greet(name: String?) {\n    val recipient: String = name ?: &quot;unnamed&quot;\n    println(&quot;Hello, $recipient&quot;)\n}</code>\n        </deckgo-highlight-code>\n<deckgo-highlight-code language=\"kotlin\" terminal=\"carbon\" theme=\"one-dark\"  >\n          <code slot=\"code\">fun strLenSafe(s: String?): Int = s?.length ?: 0</code>\n        </deckgo-highlight-code>\n<ul>\n<li>한줄로 줄여쓸 수 있다.</li>\n</ul>\n<deckgo-highlight-code language=\"kotlin\" terminal=\"carbon\" theme=\"one-dark\"  >\n          <code slot=\"code\">class Address(val streetAddress: String, val zipCode: Int, val city: String, val country: String)\n\nclass Company(val name: String, val address: Address?)\n\nclass Person(val name: String, val company: Company?)\n\nfun printShippingLabel(person: Person) {\n    val address = person.company?.address\n        ?: throw IllegalArgumentException(&quot;No address&quot;)\n    with(address) {\n        println(streetAddress)\n        println(&quot;$zipCode $city, $country&quot;)\n    }\n}</code>\n        </deckgo-highlight-code>\n<ul>\n<li>코드가 매우 간결해진다!</li>\n</ul>\n<h2 id=\"-76-예외를-발생시키지-않고-안전하게-타입을-캐스트하기-as\" style=\"position:relative;\"><a href=\"#-76-%EC%98%88%EC%99%B8%EB%A5%BC-%EB%B0%9C%EC%83%9D%EC%8B%9C%ED%82%A4%EC%A7%80-%EC%95%8A%EA%B3%A0-%EC%95%88%EC%A0%84%ED%95%98%EA%B2%8C-%ED%83%80%EC%9E%85%EC%9D%84-%EC%BA%90%EC%8A%A4%ED%8A%B8%ED%95%98%EA%B8%B0-as\" aria-label=\" 76 예외를 발생시키지 않고 안전하게 타입을 캐스트하기 as permalink\" class=\"post-anchor before\"><svg aria-hidden=\"true\" focusable=\"false\" height=\"16\" version=\"1.1\" viewBox=\"0 0 16 16\" width=\"16\"><path fill-rule=\"evenodd\" d=\"M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z\"></path></svg></a>📖 7.6 예외를 발생시키지 않고 안전하게 타입을 캐스트하기: as?</h2>\n<ul>\n<li>코틀린에서 대상 값을 as로 지정한 타입으로 바꿀 수 없으면 <code>ClassCastException</code>이 발생</li>\n<li>as를 사용할 때마다 is로 변환 가능한 타입인지 검사할 수 있지만 너무 귀찮다.</li>\n</ul>\n<deckgo-highlight-code language=\"kotlin\" terminal=\"carbon\" theme=\"one-dark\"  >\n          <code slot=\"code\">class Person(val firstName: String, val lastName: String) {\n    override fun equals(o: Any?): Boolean {\n        val otherPerson = o as? Person ?: return false\n\n        return otherPerson.firstName == firstName &amp;&amp; otherPerson.lastName == lastName\n    }\n\n    override fun hashCode(): Int = firstName.hashCode() * 37 + lastName.hashCode()\n}</code>\n        </deckgo-highlight-code>\n<ul>\n<li>as? 연산자는 어떤 값을 지정한 타입으로 변환한다. 변활할 수 없으면 null을 반환한다.</li>\n<li>하나의 식으로 해결 가능해진다.</li>\n</ul>\n<h2 id=\"-77-널-아님-단언-\" style=\"position:relative;\"><a href=\"#-77-%EB%84%90-%EC%95%84%EB%8B%98-%EB%8B%A8%EC%96%B8-\" aria-label=\" 77 널 아님 단언  permalink\" class=\"post-anchor before\"><svg aria-hidden=\"true\" focusable=\"false\" height=\"16\" version=\"1.1\" viewBox=\"0 0 16 16\" width=\"16\"><path fill-rule=\"evenodd\" d=\"M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z\"></path></svg></a>📖 7.7 널 아님 단언: !!</h2>\n<ul>\n<li>널 아님 단언은 코틀린에서 널이 될 수 있는 타입의 값을 다룰 때 사용할 수 있는 도구 중에서 가장 단순하면서도 무딘 도구다.</li>\n<li><code>!!</code>을 사용하면 어떤 값이든 널이 아닌 타입으로 바꿀 수 있다.</li>\n<li>null에 대해 <code>!!</code>를 사용하면 NPE 발생</li>\n</ul>\n<deckgo-highlight-code language=\"kotlin\" terminal=\"carbon\" theme=\"one-dark\"  >\n          <code slot=\"code\">fun ignoreNulls(str: String?) {\n    val strNotNull: String = str!! // 예외는 이 지점을 가리킨다.\n    println(strNotNull.length)\n}</code>\n        </deckgo-highlight-code>\n<ul>\n<li>예외가 가리키는 지점은 단언문이 위치한 곳을 가리킨다.</li>\n<li>결국 <code>!!</code>는 컴파일러에게 null이 아님을 알고 있으며, 예외가 발생해도 감수하겠다는 것을 말하는 것이다.</li>\n<li>굳이 느낌표 두개를 선택한 것은 더 나은 방법을 찾아보라는 코틀린 설계자들의 의도이다.\n<ul>\n<li>기호가 못생기고 무례함</li>\n</ul>\n</li>\n</ul>\n<deckgo-highlight-code language=\"kotlin\" terminal=\"carbon\" theme=\"one-dark\"  >\n          <code slot=\"code\">class SelectableTextList(\n    val contents: List&lt;String&gt;,\n    var selectedIndex: Int? = null\n)\n\nclass CopyRowAction(val list: SelectableTextList) {\n    fun isActionEnabled(): Boolean = list.selectedIndex != null\n    fun executeCopyRow() {\n        val index = list.selectedIndex!!\n        val value = list.contents[index]\n    }\n}</code>\n        </deckgo-highlight-code>\n<ul>\n<li><code>!!</code>를 사용하여 발생하는 에러 스택 트레이스에는 몇번째 줄인지에 대한 정보가 들어있지 않다.\n<ul>\n<li>여러 <code>!!</code> 단언문을 한 줄에 함께 쓰는 일을 피하는 것이 좋다.</li>\n</ul>\n</li>\n</ul>\n<h2 id=\"-78-let-함수\" style=\"position:relative;\"><a href=\"#-78-let-%ED%95%A8%EC%88%98\" aria-label=\" 78 let 함수 permalink\" class=\"post-anchor before\"><svg aria-hidden=\"true\" focusable=\"false\" height=\"16\" version=\"1.1\" viewBox=\"0 0 16 16\" width=\"16\"><path fill-rule=\"evenodd\" d=\"M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z\"></path></svg></a>📖 7.8 let 함수</h2>\n<deckgo-highlight-code language=\"kotlin\" terminal=\"carbon\" theme=\"one-dark\"  >\n          <code slot=\"code\">fun sendEmailTo(email: String) {\n    println(&quot;Sending email to $email&quot;)\n}\n\nfun main() {\n    var email: String? = &quot;yole@gmail.com&quot;\n    email.let { sendEmailTo(it) }\n    email = null\n    email?.let { sendEmailTo(it) }\n}</code>\n        </deckgo-highlight-code>\n<ul>\n<li>let을 사용하는 가장 흔한 용례는 널이 될 수 있는 값을 널이 아닌 값만 인자로 받는 함수에 넘기는 경우이다.</li>\n<li>let 함수는 자신의 수신 객체를 인자로 전달받은 람다에 넘긴다.</li>\n<li>아주 긴 식이 있고 그 값이 null이 아닐 때 수행해야 하는 로직이 있을 때 let을 쓰면 훨씬 더 편하다.</li>\n</ul>\n<h2 id=\"-79-직접-초기화하지-않는-널이-아닌-타입-지연-초기화-프로퍼티\" style=\"position:relative;\"><a href=\"#-79-%EC%A7%81%EC%A0%91-%EC%B4%88%EA%B8%B0%ED%99%94%ED%95%98%EC%A7%80-%EC%95%8A%EB%8A%94-%EB%84%90%EC%9D%B4-%EC%95%84%EB%8B%8C-%ED%83%80%EC%9E%85-%EC%A7%80%EC%97%B0-%EC%B4%88%EA%B8%B0%ED%99%94-%ED%94%84%EB%A1%9C%ED%8D%BC%ED%8B%B0\" aria-label=\" 79 직접 초기화하지 않는 널이 아닌 타입 지연 초기화 프로퍼티 permalink\" class=\"post-anchor before\"><svg aria-hidden=\"true\" focusable=\"false\" height=\"16\" version=\"1.1\" viewBox=\"0 0 16 16\" width=\"16\"><path fill-rule=\"evenodd\" d=\"M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z\"></path></svg></a>📖 7.9 직접 초기화하지 않는 널이 아닌 타입: 지연 초기화 프로퍼티</h2>\n<deckgo-highlight-code language=\"kotlin\" terminal=\"carbon\" theme=\"one-dark\"  >\n          <code slot=\"code\">class MyService {\n    fun performAction(): String = &quot;Action Done!&quot;\n}\n\nclass MyTest {\n    private var myService: MyService? = null\n    \n    @BeforeAll fun setUp() {\n        myService = MyService()\n    }\n    \n    @Test fun testAction() {\n        assertEquals(&quot;Action Done!&quot;, myService!!.performAction())\n    }\n}</code>\n        </deckgo-highlight-code>\n<ul>\n<li>이 코드는 보기 나쁘다.</li>\n<li>코틀린에서는 클래스 안의 널이 아닌 프로퍼티를 생성자 안에서 초기화하지 않고 특별한 메서드 안에서 초기화할 수는 없다. 일반적으로 생성자에서 모든 프로퍼티를 초기화해야 한다.</li>\n<li>프로퍼티 타입이 널이 될 수 없는 타입이라면 반드시 널이 아닌 값으로 초기화해야 한다.</li>\n</ul>\n<deckgo-highlight-code language=\"kotlin\" terminal=\"carbon\" theme=\"one-dark\"  >\n          <code slot=\"code\">class MyTest {\n    private lateinit var myService: MyService\n\n    @BeforeAll fun setUp() {\n        myService = MyService()\n    }\n\n    @Test fun testAction() {\n        assertEquals(&quot;Action Done!&quot;, myService.performAction())\n    }\n}</code>\n        </deckgo-highlight-code>\n<ul>\n<li><code>lateinit</code> 변경자를 붙이면 프로퍼티를 나중에 초기화할 수 있다.</li>\n<li>지연 초기화 프로퍼티는 항상 var여야한다.</li>\n</ul>\n<h2 id=\"-710-안전한-호출-연산자-없이-타입-확장-널이-될-수-있는-타입에-대한-확장\" style=\"position:relative;\"><a href=\"#-710-%EC%95%88%EC%A0%84%ED%95%9C-%ED%98%B8%EC%B6%9C-%EC%97%B0%EC%82%B0%EC%9E%90-%EC%97%86%EC%9D%B4-%ED%83%80%EC%9E%85-%ED%99%95%EC%9E%A5-%EB%84%90%EC%9D%B4-%EB%90%A0-%EC%88%98-%EC%9E%88%EB%8A%94-%ED%83%80%EC%9E%85%EC%97%90-%EB%8C%80%ED%95%9C-%ED%99%95%EC%9E%A5\" aria-label=\" 710 안전한 호출 연산자 없이 타입 확장 널이 될 수 있는 타입에 대한 확장 permalink\" class=\"post-anchor before\"><svg aria-hidden=\"true\" focusable=\"false\" height=\"16\" version=\"1.1\" viewBox=\"0 0 16 16\" width=\"16\"><path fill-rule=\"evenodd\" d=\"M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z\"></path></svg></a>📖 7.10 안전한 호출 연산자 없이 타입 확장: 널이 될 수 있는 타입에 대한 확장</h2>\n<deckgo-highlight-code language=\"kotlin\" terminal=\"carbon\" theme=\"one-dark\"  >\n          <code slot=\"code\">fun verifyUserInput(input: String?) {\n    if (input.isNullOrBlank()) {\n        println(&quot;Please fill in the required fields&quot;)\n    }\n}\n\nfun main() {\n    verifyUserInput(&quot; &quot;)\n    verifyUserInput(null) // 예외 발생하지 않음.\n}</code>\n        </deckgo-highlight-code>\n<ul>\n<li>메서드 호출이 null을 수신 객체로 받고 내부에서 null을 처리하게 할 수 있다.\n<ul>\n<li>확장 함수에서만 가능</li>\n<li>일반 멤버 호출은 객체 인스턴스를 통해 디스패치되므로 그 인스턴스가 null인지 여부를 검사하지 않는다.</li>\n</ul>\n</li>\n<li>널이 될 수 있는 타입의 확장 함수는 자신의 수신 객체가 null일 때 어떻게 해야 하는지 스스로 안다.\n<ul>\n<li>안전한 호출 없이도 호출 가능</li>\n</ul>\n</li>\n</ul>\n<deckgo-highlight-code language=\"kotlin\" terminal=\"carbon\" theme=\"one-dark\"  >\n          <code slot=\"code\">fun sendEmailTo(email: String) {\n    println(&quot;Sending email to $email&quot;)\n}\n\nfun main() {\n    val recipient: String? = null\n    recipient.let { sendEmailTo(it) } // 안전한 호출을 사용하지 않아 널이 될 수 있는 타입으로 취금\n}</code>\n        </deckgo-highlight-code>\n<ul>\n<li>let은 this가 null인지 검사하지 않는다.</li>\n<li>let을 사용할 때 수신 객체가 null이 아닌지 검사하고 싶다면 안전한 호출 연산인 <code>?.</code>를 사용해야 한다.</li>\n</ul>\n<h2 id=\"-711-타입-파라미터의-널-가능성\" style=\"position:relative;\"><a href=\"#-711-%ED%83%80%EC%9E%85-%ED%8C%8C%EB%9D%BC%EB%AF%B8%ED%84%B0%EC%9D%98-%EB%84%90-%EA%B0%80%EB%8A%A5%EC%84%B1\" aria-label=\" 711 타입 파라미터의 널 가능성 permalink\" class=\"post-anchor before\"><svg aria-hidden=\"true\" focusable=\"false\" height=\"16\" version=\"1.1\" viewBox=\"0 0 16 16\" width=\"16\"><path fill-rule=\"evenodd\" d=\"M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z\"></path></svg></a>📖 7.11 타입 파라미터의 널 가능성</h2>\n<ul>\n<li>타입 파라미터 T를 클래스나 함수 안에서 타입 이름으로 사용하면 이름 끝에 물음표가 없더라도 T가 널이 될 수 있는 타입이다.</li>\n</ul>\n<deckgo-highlight-code language=\"kotlin\" terminal=\"carbon\" theme=\"one-dark\"  >\n          <code slot=\"code\">fun &lt;T&gt; printHashCode(t: T) {\n    println(t?.hashCode()) // 안전한 호출 사용\n}\n\nfun main() {\n    printHashCode(null)\n    // null\n}</code>\n        </deckgo-highlight-code>\n<ul>\n<li><code>printHashCode</code> 호출에서 타입 파라미터 T에 대해 추론한 타입은 널이 될 수 있는 Any? 타입이다.</li>\n<li>타입 파라미터가 널이 아님을 확실히 하려면 널이 될 수 없는 타입 상계를 지정해야 한다.</li>\n</ul>\n<deckgo-highlight-code language=\"kotlin\" terminal=\"carbon\" theme=\"one-dark\"  >\n          <code slot=\"code\">fun &lt;T: Any&gt; printHashCode(t: T) {\n    println(t.hashCode()) // 안전한 호출 사용\n}\n\nfun main() {\n    printHashCode(null) // error\n}</code>\n        </deckgo-highlight-code>\n<ul>\n<li>타입 파라미터는 널이 될 수 있는 타입을 표시하려면 반드시 물음표를 타입 이름 뒤에 붙여야 한다는 규칙의 유일한 예외이다.</li>\n</ul>\n<h2 id=\"-712-널-가능성과-자바\" style=\"position:relative;\"><a href=\"#-712-%EB%84%90-%EA%B0%80%EB%8A%A5%EC%84%B1%EA%B3%BC-%EC%9E%90%EB%B0%94\" aria-label=\" 712 널 가능성과 자바 permalink\" class=\"post-anchor before\"><svg aria-hidden=\"true\" focusable=\"false\" height=\"16\" version=\"1.1\" viewBox=\"0 0 16 16\" width=\"16\"><path fill-rule=\"evenodd\" d=\"M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z\"></path></svg></a>📖 7.12 널 가능성과 자바</h2>\n<h3 id=\"-7121-플랫폼-타입\" style=\"position:relative;\"><a href=\"#-7121-%ED%94%8C%EB%9E%AB%ED%8F%BC-%ED%83%80%EC%9E%85\" aria-label=\" 7121 플랫폼 타입 permalink\" class=\"post-anchor before\"><svg aria-hidden=\"true\" focusable=\"false\" height=\"16\" version=\"1.1\" viewBox=\"0 0 16 16\" width=\"16\"><path fill-rule=\"evenodd\" d=\"M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z\"></path></svg></a>🔖 7.12.1 플랫폼 타입</h3>\n<ul>\n<li>플랫폼 타입은 코틀린이 널 관련 정보를 알 수 없는 타입\n<ul>\n<li>널이 될 수 있는 타입 or 널이 될 수 없는 타입으로 처리해도 된다.</li>\n<li>컴파일러는 모든 연산을 허용</li>\n</ul>\n</li>\n<li>자바 API를 다룰 때는 조심해야 한다.\n<ul>\n<li>대부분의 라이브러리는 널 관련 어노테이션을 쓰지 않는다.</li>\n</ul>\n</li>\n<li>코틀린에서 플랫폼 타입을 선언할 수는 없다.</li>\n<li>자바에서 가져온 널 값을 널이 될 수 없는 코틀린 변수에 대입하면 실행 시점에 대입이 이뤄질 때 예외가 발생</li>\n</ul>\n<h3 id=\"-7122-상속\" style=\"position:relative;\"><a href=\"#-7122-%EC%83%81%EC%86%8D\" aria-label=\" 7122 상속 permalink\" class=\"post-anchor before\"><svg aria-hidden=\"true\" focusable=\"false\" height=\"16\" version=\"1.1\" viewBox=\"0 0 16 16\" width=\"16\"><path fill-rule=\"evenodd\" d=\"M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z\"></path></svg></a>🔖 7.12.2 상속</h3>\n<ul>\n<li>코틀린에서 자바 메서드를 오버라이드할 때 그 메서드의 파라미터와 반환 타입을 널이 될 수 있는 타입으로 선언할지 널이 될 수 없는 타입으로 선언할지 결정해야 한다.</li>\n<li>자바 클래스나 인터페이스를 코틀린에서 구현할 경우 널 가능성을 제대로 처리하는 것이 중요\n<ul>\n<li>코틀린 컴파일러는 널이 될 수 없는 타입으로 선언한 모든 파라미터에 대해 널이 아님을 검사하는 단언문을 만들어준다.</li>\n</ul>\n</li>\n</ul>","excerpt":"📖 7.1 NullPointerException을 피하고 값이 없는 경우 처리: 널 가능성 코틀린을 포함한 최신 언어에서 null에 대한 접근 방법은 가능한 이 문제를 실행 시점에서 컴파일 시점으로 옮기는 것 널이 될 수 있는지 여부를 타입 시스템에 추가하으로써 컴파일러가 여러 가지 오류를 컴파일 시 미리 감지해서 실행 시점에 발생할 수 있는 예외의 가능성을 줄일 수 있다. 📖 7.…","fields":{"slug":"/backend/kotlin-in-action/7장-널이_될_수_있는_값/"},"frontmatter":{"title":"Kotlin in Action - 7장 널이 될 수 있는 값","thumbnail":{"childImageSharp":{"fluid":{"src":"/static/f7f7ddfa31d1614405dc5af1487b9ec4/9c94d/kotlin-in-action.png"}}},"draft":false,"category":"Back-End","tags":["Kotlin"],"date":"April 13, 2025"}},"previous":{"id":"dd1d7589-298b-5305-9cd3-3de0eb0cc3f7","html":"<p>코틀린은 어떤 언어 기능과 미리 정해진 이름의 함수를 연결해주는 기법인 관례에 의존한다.</p>\n<h2 id=\"-91-산술-연산자를-오버로드해서-임의의-클래스에-대한-연산을-더-편리하게-만들기\" style=\"position:relative;\"><a href=\"#-91-%EC%82%B0%EC%88%A0-%EC%97%B0%EC%82%B0%EC%9E%90%EB%A5%BC-%EC%98%A4%EB%B2%84%EB%A1%9C%EB%93%9C%ED%95%B4%EC%84%9C-%EC%9E%84%EC%9D%98%EC%9D%98-%ED%81%B4%EB%9E%98%EC%8A%A4%EC%97%90-%EB%8C%80%ED%95%9C-%EC%97%B0%EC%82%B0%EC%9D%84-%EB%8D%94-%ED%8E%B8%EB%A6%AC%ED%95%98%EA%B2%8C-%EB%A7%8C%EB%93%A4%EA%B8%B0\" aria-label=\" 91 산술 연산자를 오버로드해서 임의의 클래스에 대한 연산을 더 편리하게 만들기 permalink\" class=\"post-anchor before\"><svg aria-hidden=\"true\" focusable=\"false\" height=\"16\" version=\"1.1\" viewBox=\"0 0 16 16\" width=\"16\"><path fill-rule=\"evenodd\" d=\"M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z\"></path></svg></a>📖 9.1 산술 연산자를 오버로드해서 임의의 클래스에 대한 연산을 더 편리하게 만들기</h2>\n<h3 id=\"-911-plus-times-divide-등-이항-산술-연산-오버로딩\" style=\"position:relative;\"><a href=\"#-911-plus-times-divide-%EB%93%B1-%EC%9D%B4%ED%95%AD-%EC%82%B0%EC%88%A0-%EC%97%B0%EC%82%B0-%EC%98%A4%EB%B2%84%EB%A1%9C%EB%94%A9\" aria-label=\" 911 plus times divide 등 이항 산술 연산 오버로딩 permalink\" class=\"post-anchor before\"><svg aria-hidden=\"true\" focusable=\"false\" height=\"16\" version=\"1.1\" viewBox=\"0 0 16 16\" width=\"16\"><path fill-rule=\"evenodd\" d=\"M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z\"></path></svg></a>🔖 9.1.1 plus, times, divide 등: 이항 산술 연산 오버로딩</h3>\n<deckgo-highlight-code language=\"kotlin\" terminal=\"carbon\" theme=\"one-dark\"  >\n          <code slot=\"code\">data class Point(val x: Int, val y: Int) {\n    operator fun plus(other: Point): Point {\n        return Point(x + other.x, y + other.y)\n    }\n}\n\nfun main() {\n    val p1 = Point(10, 20)\n    val p2 = Point(30, 40)\n    println(p1 + p2) // + 기호를 쓰면 plus 함수 호출\n}</code>\n        </deckgo-highlight-code>\n<ul>\n<li>plus 함수 앞에 operator 키워드를 붙여야 한다.</li>\n<li>연산자를 확장 함수로 정의할 수도 있다.</li>\n</ul>\n<table>\n<thead>\n<tr>\n<th>표현식</th>\n<th>함수 이름</th>\n</tr>\n</thead>\n<tbody>\n<tr>\n<td>a + b</td>\n<td>plus</td>\n</tr>\n<tr>\n<td>a - b</td>\n<td>minus</td>\n</tr>\n<tr>\n<td>a * b</td>\n<td>times</td>\n</tr>\n<tr>\n<td>a / b</td>\n<td>div</td>\n</tr>\n<tr>\n<td>a % b</td>\n<td>mod</td>\n</tr>\n</tbody>\n</table>\n<ul>\n<li>연산자 우선순위는 표준 숫자 타입에 대한 연산자 우선순위와 같다.</li>\n</ul>\n<deckgo-highlight-code language=\"kotlin\" terminal=\"carbon\" theme=\"one-dark\"  >\n          <code slot=\"code\">operator fun Point.times(scale: Double): Point {\n    return Point((x * scale).toInt(), (y * scale).toInt())\n}\n\nfun main() {\n    val p = Point(10, 20)\n    println(p * 1.5)\n}</code>\n        </deckgo-highlight-code>\n<ul>\n<li>연산자를 정의할 때 두 피연산자가 같은 타입일 필요는 없다.</li>\n<li>자동으로 교환법칙을 지원하지 않는다.</li>\n</ul>\n<deckgo-highlight-code language=\"kotlin\" terminal=\"carbon\" theme=\"one-dark\"  >\n          <code slot=\"code\">operator fun Char.times(count: Int): String {\n    return toString().repeat(count)\n}\n\nfun main() {\n    println(&#39;a&#39; * 3)\n}</code>\n        </deckgo-highlight-code>\n<ul>\n<li>연산자 함수의 반환 타입이 꼭 두 피연산자 중 하나와 일치해야만 하는 것도 아니다.</li>\n</ul>\n<h3 id=\"-912-연산을-적용한-다음에-그-결과를-바로-대입-복합-대입-연산자-오버로딩\" style=\"position:relative;\"><a href=\"#-912-%EC%97%B0%EC%82%B0%EC%9D%84-%EC%A0%81%EC%9A%A9%ED%95%9C-%EB%8B%A4%EC%9D%8C%EC%97%90-%EA%B7%B8-%EA%B2%B0%EA%B3%BC%EB%A5%BC-%EB%B0%94%EB%A1%9C-%EB%8C%80%EC%9E%85-%EB%B3%B5%ED%95%A9-%EB%8C%80%EC%9E%85-%EC%97%B0%EC%82%B0%EC%9E%90-%EC%98%A4%EB%B2%84%EB%A1%9C%EB%94%A9\" aria-label=\" 912 연산을 적용한 다음에 그 결과를 바로 대입 복합 대입 연산자 오버로딩 permalink\" class=\"post-anchor before\"><svg aria-hidden=\"true\" focusable=\"false\" height=\"16\" version=\"1.1\" viewBox=\"0 0 16 16\" width=\"16\"><path fill-rule=\"evenodd\" d=\"M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z\"></path></svg></a>🔖 9.1.2 연산을 적용한 다음에 그 결과를 바로 대입: 복합 대입 연산자 오버로딩</h3>\n<deckgo-highlight-code language=\"kotlin\" terminal=\"carbon\" theme=\"one-dark\"  >\n          <code slot=\"code\">fun main() {\n    var point = Point(1, 2)\n    point += Point(3, 4)\n    println(point)\n}</code>\n        </deckgo-highlight-code>\n<ul>\n<li>plus와 같은 연산자를 오버로딩하면 복합 대입 연산자를 자동으로 지원한다.</li>\n</ul>\n<deckgo-highlight-code language=\"kotlin\" terminal=\"carbon\" theme=\"one-dark\"  >\n          <code slot=\"code\">operator fun &lt;T&gt; MutableCollection&lt;T&gt;.plusAssign(element: T) {\n    this.add(element)\n}</code>\n        </deckgo-highlight-code>\n<ul>\n<li>어떤 클래스가 plus, plusAssign을 모두 정의하고 +=을 사용하면 오류를 보고한다.</li>\n<li>코틀린 표준 라이브러리는 컬렉션에 대해 2가지 접근 방법을 제공\n<ul>\n<li>+,-는 항상 새로운 컬렉션 반환</li>\n<li>+=, -= 연산자는 항상 변경 가능한 컬렉션에 작용해 메모리에 있는 객체 상태를 변화</li>\n</ul>\n</li>\n</ul>\n<h3 id=\"-913-피연산자가-1개뿐인-연산자-단항-연산자-오버로딩\" style=\"position:relative;\"><a href=\"#-913-%ED%94%BC%EC%97%B0%EC%82%B0%EC%9E%90%EA%B0%80-1%EA%B0%9C%EB%BF%90%EC%9D%B8-%EC%97%B0%EC%82%B0%EC%9E%90-%EB%8B%A8%ED%95%AD-%EC%97%B0%EC%82%B0%EC%9E%90-%EC%98%A4%EB%B2%84%EB%A1%9C%EB%94%A9\" aria-label=\" 913 피연산자가 1개뿐인 연산자 단항 연산자 오버로딩 permalink\" class=\"post-anchor before\"><svg aria-hidden=\"true\" focusable=\"false\" height=\"16\" version=\"1.1\" viewBox=\"0 0 16 16\" width=\"16\"><path fill-rule=\"evenodd\" d=\"M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z\"></path></svg></a>🔖 9.1.3 피연산자가 1개뿐인 연산자: 단항 연산자 오버로딩</h3>\n<deckgo-highlight-code language=\"kotlin\" terminal=\"carbon\" theme=\"one-dark\"  >\n          <code slot=\"code\">operator fun Point.unaryMinus(): Point {\n    return Point(-x, -y)\n}</code>\n        </deckgo-highlight-code>\n<ul>\n<li>단항 연산자를 오버로딩하기 위해 사용하는 함수는 인자를 취하지 않는다.</li>\n</ul>\n<table>\n<thead>\n<tr>\n<th>표현식</th>\n<th>함수 이름</th>\n</tr>\n</thead>\n<tbody>\n<tr>\n<td>+a</td>\n<td>unaryPlus</td>\n</tr>\n<tr>\n<td>-a</td>\n<td>unaryMinus</td>\n</tr>\n<tr>\n<td>!a</td>\n<td>not</td>\n</tr>\n<tr>\n<td>a++, ++a</td>\n<td>inc</td>\n</tr>\n<tr>\n<td>a-- , --a</td>\n<td>dec</td>\n</tr>\n</tbody>\n</table>\n<deckgo-highlight-code language=\"kotlin\" terminal=\"carbon\" theme=\"one-dark\"  >\n          <code slot=\"code\">fun main() {\n    var bd = BigDecimal.ZERO\n    println(bd++) // 후위 증가 연산자는 println이 실행된 다음에 값 증가\n    println(bd)\n    println(++bd) // 전위 증가 연산자는 println이 실행되기 전에 값 증가\n}</code>\n        </deckgo-highlight-code>\n<h2 id=\"-92-비교-연산자를-오버로딩해서-객체들-사이의-관계를-쉽게-검사\" style=\"position:relative;\"><a href=\"#-92-%EB%B9%84%EA%B5%90-%EC%97%B0%EC%82%B0%EC%9E%90%EB%A5%BC-%EC%98%A4%EB%B2%84%EB%A1%9C%EB%94%A9%ED%95%B4%EC%84%9C-%EA%B0%9D%EC%B2%B4%EB%93%A4-%EC%82%AC%EC%9D%B4%EC%9D%98-%EA%B4%80%EA%B3%84%EB%A5%BC-%EC%89%BD%EA%B2%8C-%EA%B2%80%EC%82%AC\" aria-label=\" 92 비교 연산자를 오버로딩해서 객체들 사이의 관계를 쉽게 검사 permalink\" class=\"post-anchor before\"><svg aria-hidden=\"true\" focusable=\"false\" height=\"16\" version=\"1.1\" viewBox=\"0 0 16 16\" width=\"16\"><path fill-rule=\"evenodd\" d=\"M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z\"></path></svg></a>📖 9.2 비교 연산자를 오버로딩해서 객체들 사이의 관계를 쉽게 검사</h2>\n<h3 id=\"-921-동등성-연산자-equals\" style=\"position:relative;\"><a href=\"#-921-%EB%8F%99%EB%93%B1%EC%84%B1-%EC%97%B0%EC%82%B0%EC%9E%90-equals\" aria-label=\" 921 동등성 연산자 equals permalink\" class=\"post-anchor before\"><svg aria-hidden=\"true\" focusable=\"false\" height=\"16\" version=\"1.1\" viewBox=\"0 0 16 16\" width=\"16\"><path fill-rule=\"evenodd\" d=\"M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z\"></path></svg></a>🔖 9.2.1 동등성 연산자: equals</h3>\n<ul>\n<li>!= 연산자를 사용하는 식도 equals 호출로 컴파일 된다.</li>\n<li>null 검사도 한다.</li>\n</ul>\n<deckgo-highlight-code language=\"kotlin\" terminal=\"carbon\" theme=\"one-dark\"  >\n          <code slot=\"code\">class Point(val x: Int, val y: Int) {\n    override fun equals(other: Any?): Boolean {\n        if (other === this) return true\n        if (other !is Point) return false\n        return other.x == x &amp;&amp; other.y == y\n    }\n}</code>\n        </deckgo-highlight-code>\n<ul>\n<li>동등성 비교 연산자(===)는 자바 == 연산자와 같다.\n<ul>\n<li>자신의 두 피연산자가 서로 같은 객체ㅡㄹ 가리키는지 비교한다.</li>\n</ul>\n</li>\n<li>===를 오버로딩할 수는 없다.</li>\n</ul>\n<h3 id=\"-922-순서-연산자-compareto----\" style=\"position:relative;\"><a href=\"#-922-%EC%88%9C%EC%84%9C-%EC%97%B0%EC%82%B0%EC%9E%90-compareto----\" aria-label=\" 922 순서 연산자 compareto     permalink\" class=\"post-anchor before\"><svg aria-hidden=\"true\" focusable=\"false\" height=\"16\" version=\"1.1\" viewBox=\"0 0 16 16\" width=\"16\"><path fill-rule=\"evenodd\" d=\"M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z\"></path></svg></a>🔖 9.2.2 순서 연산자: compareTo (&#x3C;, >, &#x3C;=, >=)</h3>\n<deckgo-highlight-code language=\"kotlin\" terminal=\"carbon\" theme=\"one-dark\"  >\n          <code slot=\"code\">class Person(val firstName: String, val lastName: String) : Comparable&lt;Person&gt; {\n    override fun compareTo(other: Person): Int {\n        return compareValuesBy(this, other, Person::lastName, Person::firstName)\n    }\n}\n\nfun main() {\n    val p1 = Person(&quot;Alice&quot;, &quot;Smith&quot;)\n    val p2 = Person(&quot;Bob&quot;, &quot;Johnson&quot;)\n    println(p1 &lt; p2)\n}</code>\n        </deckgo-highlight-code>\n<ul>\n<li>비교 연산자를 제공한다.</li>\n<li><code>compareValuesBy</code>는 두 객체와 여러 비교 함수를 인자로 받는다.\n<ul>\n<li>첫 번째 비교 함수에 두 객체를 넘겨 두 객체가 같지 않다는 결과가 나오면 그 결괏값을 즉시 반환</li>\n</ul>\n</li>\n<li>비교 연산자를 자바 클래스에 대해 사용하기 위해 특별히 확장 메서드를 만들거나 할 필요는 없다.</li>\n</ul>\n<h2 id=\"-93-컬렉션과-범위에-대해-쓸-수-있는-관례\" style=\"position:relative;\"><a href=\"#-93-%EC%BB%AC%EB%A0%89%EC%85%98%EA%B3%BC-%EB%B2%94%EC%9C%84%EC%97%90-%EB%8C%80%ED%95%B4-%EC%93%B8-%EC%88%98-%EC%9E%88%EB%8A%94-%EA%B4%80%EB%A1%80\" aria-label=\" 93 컬렉션과 범위에 대해 쓸 수 있는 관례 permalink\" class=\"post-anchor before\"><svg aria-hidden=\"true\" focusable=\"false\" height=\"16\" version=\"1.1\" viewBox=\"0 0 16 16\" width=\"16\"><path fill-rule=\"evenodd\" d=\"M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z\"></path></svg></a>📖 9.3 컬렉션과 범위에 대해 쓸 수 있는 관례</h2>\n<h3 id=\"-931-인덱스로-원소-접근-get과-set\" style=\"position:relative;\"><a href=\"#-931-%EC%9D%B8%EB%8D%B1%EC%8A%A4%EB%A1%9C-%EC%9B%90%EC%86%8C-%EC%A0%91%EA%B7%BC-get%EA%B3%BC-set\" aria-label=\" 931 인덱스로 원소 접근 get과 set permalink\" class=\"post-anchor before\"><svg aria-hidden=\"true\" focusable=\"false\" height=\"16\" version=\"1.1\" viewBox=\"0 0 16 16\" width=\"16\"><path fill-rule=\"evenodd\" d=\"M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z\"></path></svg></a>🔖 9.3.1 인덱스로 원소 접근: get과 set</h3>\n<deckgo-highlight-code language=\"kotlin\" terminal=\"carbon\" theme=\"one-dark\"  >\n          <code slot=\"code\">operator fun Point.get(index: Int): Int {\n    return when (index) {\n        0 -&gt; x\n        1 -&gt; y\n        else -&gt;\n            throw IndexOutOfBoundsException(&quot;Invalid coordinate $index&quot;)\n    }\n}\n\nfun main() {\n    val p = Point(10, 20)\n    println(p[1])\n}</code>\n        </deckgo-highlight-code>\n<ul>\n<li>get 메서드의 파라미터로 Int가 아닌 타입도 사용할 수 있다.</li>\n</ul>\n<deckgo-highlight-code language=\"kotlin\" terminal=\"carbon\" theme=\"one-dark\"  >\n          <code slot=\"code\">data class MutablePoint(var x: Int, var y: Int)\n\noperator fun MutablePoint.set(index: Int, value: Int) {\n    when (index) {\n        0 -&gt; x = value\n        1 -&gt; y = value\n        else -&gt;\n            throw IndexOutOfBoundsException(&quot;Invalid coordinate $index&quot;)\n    }\n}\n\nfun main() {\n    val p = MutablePoint(10, 20)\n    p[1] = 42\n    println(p)\n}</code>\n        </deckgo-highlight-code>\n<ul>\n<li>set도 관례로 표현할 수 있다.</li>\n</ul>\n<h3 id=\"-932-어떤-객체가-컬렉션에-들어있는지-검사-in-관레\" style=\"position:relative;\"><a href=\"#-932-%EC%96%B4%EB%96%A4-%EA%B0%9D%EC%B2%B4%EA%B0%80-%EC%BB%AC%EB%A0%89%EC%85%98%EC%97%90-%EB%93%A4%EC%96%B4%EC%9E%88%EB%8A%94%EC%A7%80-%EA%B2%80%EC%82%AC-in-%EA%B4%80%EB%A0%88\" aria-label=\" 932 어떤 객체가 컬렉션에 들어있는지 검사 in 관레 permalink\" class=\"post-anchor before\"><svg aria-hidden=\"true\" focusable=\"false\" height=\"16\" version=\"1.1\" viewBox=\"0 0 16 16\" width=\"16\"><path fill-rule=\"evenodd\" d=\"M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z\"></path></svg></a>🔖 9.3.2 어떤 객체가 컬렉션에 들어있는지 검사: in 관레</h3>\n<deckgo-highlight-code language=\"kotlin\" terminal=\"carbon\" theme=\"one-dark\"  >\n          <code slot=\"code\">data class Rectangle(val upperLeft: Point, val lowerRight: Point)\n\noperator fun Rectangle.contains(p: Point): Boolean {\n    return p.x in upperLeft.x..&lt;lowerRight.x &amp;&amp;\n            p.y in upperLeft.y..&lt;lowerRight.y // 열린 범위\n}\n\nfun main() {\n    val rect = Rectangle(Point(10, 20), Point(50, 50))\n    println(Point(20, 30) in rect)\n    println(Point(5, 5) in rect)\n}</code>\n        </deckgo-highlight-code>\n<ul>\n<li>..&#x3C; 연산자를 쓰면 열린 범위를 만들 수 있다.</li>\n<li>in 함수는 contains 메서드의 수신 객체가 되고 in의 왼쪽에 있는 객체는 contains 메서드에 인자로 전달된다.</li>\n</ul>\n<h3 id=\"-933-객체로부터-범위-만들기-rangeto와-rangeuntil-관레\" style=\"position:relative;\"><a href=\"#-933-%EA%B0%9D%EC%B2%B4%EB%A1%9C%EB%B6%80%ED%84%B0-%EB%B2%94%EC%9C%84-%EB%A7%8C%EB%93%A4%EA%B8%B0-rangeto%EC%99%80-rangeuntil-%EA%B4%80%EB%A0%88\" aria-label=\" 933 객체로부터 범위 만들기 rangeto와 rangeuntil 관레 permalink\" class=\"post-anchor before\"><svg aria-hidden=\"true\" focusable=\"false\" height=\"16\" version=\"1.1\" viewBox=\"0 0 16 16\" width=\"16\"><path fill-rule=\"evenodd\" d=\"M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z\"></path></svg></a>🔖 9.3.3 객체로부터 범위 만들기: rangeTo와 rangeUntil 관레</h3>\n<deckgo-highlight-code language=\"kotlin\" terminal=\"carbon\" theme=\"one-dark\"  >\n          <code slot=\"code\">operator fun &lt;T: Comparable&lt;T&gt;&gt; T.rangeTo(other: T): ClosedRange&lt;T&gt;</code>\n        </deckgo-highlight-code>\n<ul>\n<li>코틀린 표준 라이브러리에는 모든 Comparable 객체에 대해 적용 가능한 rangeTo 함수가 들어있다.</li>\n</ul>\n<deckgo-highlight-code language=\"kotlin\" terminal=\"carbon\" theme=\"one-dark\"  >\n          <code slot=\"code\">fun main() {\n    val now = LocalDate.now()\n    val vacation = now..now.plusDays(10)\n    println(now.plusWeeks(1) in vacation)\n}</code>\n        </deckgo-highlight-code>\n<ul>\n<li><code>now..now.plusDays(10)</code> 식은 컴파일러에 의해 <code>now.rangeTo(now.plusDays(10))</code>으로 변환된다.</li>\n<li>rangeTo 연산자는 다른 산술 연산자보다 우선순위가 낮다.\n<ul>\n<li>혼동을 피하기 위해 괄호를 쓰자!</li>\n</ul>\n</li>\n</ul>\n<deckgo-highlight-code language=\"kotlin\" terminal=\"carbon\" theme=\"one-dark\"  >\n          <code slot=\"code\">fun main() {\n    val n = 9\n    (0..n).forEach { println(it) }\n}</code>\n        </deckgo-highlight-code>\n<ul>\n<li>범위의 메서드를 호출하려면 범위를 괄호로 둘러싸야 한다.</li>\n<li>rangeUntil 연산자(..&#x3C;)>=는 열린 범위를 만든다.</li>\n</ul>\n<h3 id=\"-934-자신의-타입에-대해-루프-수행-iterator-관례\" style=\"position:relative;\"><a href=\"#-934-%EC%9E%90%EC%8B%A0%EC%9D%98-%ED%83%80%EC%9E%85%EC%97%90-%EB%8C%80%ED%95%B4-%EB%A3%A8%ED%94%84-%EC%88%98%ED%96%89-iterator-%EA%B4%80%EB%A1%80\" aria-label=\" 934 자신의 타입에 대해 루프 수행 iterator 관례 permalink\" class=\"post-anchor before\"><svg aria-hidden=\"true\" focusable=\"false\" height=\"16\" version=\"1.1\" viewBox=\"0 0 16 16\" width=\"16\"><path fill-rule=\"evenodd\" d=\"M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z\"></path></svg></a>🔖 9.3.4 자신의 타입에 대해 루프 수행: iterator 관례</h3>\n<deckgo-highlight-code language=\"kotlin\" terminal=\"carbon\" theme=\"one-dark\"  >\n          <code slot=\"code\">operator fun CharSequence.iterator(): CharIterator</code>\n        </deckgo-highlight-code>\n<ul>\n<li>이 라이브러리 함수는 문자열을 이터레이션할 수 있게 해준다.</li>\n</ul>\n<deckgo-highlight-code language=\"kotlin\" terminal=\"carbon\" theme=\"one-dark\"  >\n          <code slot=\"code\">operator fun ClosedRange&lt;LocalDate&gt;.iterator(): Iterator&lt;LocalDate&gt; =\n    object : Iterator&lt;LocalDate&gt; {\n        var current = start\n        override fun hasNext(): Boolean =\n            current &lt;= endInclusive\n        override fun next(): LocalDate {\n            val thisDate = current\n            current = current.plusDays(1)\n            return thisDate\n        }\n    }\n\nfun main() {\n    val newYear = LocalDate.ofYearDay(2042, 1)\n    val daysOff = newYear.minusDays(1)..newYear\n    for (dayOff in daysOff) {\n        println(dayOff)\n    }\n}</code>\n        </deckgo-highlight-code>\n<ul>\n<li><code>ClosedRange&#x3C;LocalDate></code>에 대한 확장 함수 iterator를 정의했기 때문에 LocalDate의 범위 객체를 for 루프에 사용할 수 있다.</li>\n</ul>\n<h2 id=\"-94-component-함수를-사용해-구조-분해-선언-제공\" style=\"position:relative;\"><a href=\"#-94-component-%ED%95%A8%EC%88%98%EB%A5%BC-%EC%82%AC%EC%9A%A9%ED%95%B4-%EA%B5%AC%EC%A1%B0-%EB%B6%84%ED%95%B4-%EC%84%A0%EC%96%B8-%EC%A0%9C%EA%B3%B5\" aria-label=\" 94 component 함수를 사용해 구조 분해 선언 제공 permalink\" class=\"post-anchor before\"><svg aria-hidden=\"true\" focusable=\"false\" height=\"16\" version=\"1.1\" viewBox=\"0 0 16 16\" width=\"16\"><path fill-rule=\"evenodd\" d=\"M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z\"></path></svg></a>📖 9.4 component 함수를 사용해 구조 분해 선언 제공</h2>\n<deckgo-highlight-code language=\"kotlin\" terminal=\"carbon\" theme=\"one-dark\"  >\n          <code slot=\"code\">fun main() {\n    val p = Point(10, 20)\n    val (x, y) = p\n    println(x)\n    println(y)\n}</code>\n        </deckgo-highlight-code>\n<ul>\n<li>구조 분해 선언은 일반 변수 선언과 비슷해 보인다.</li>\n<li>내부에서 구조 분해 선언의 각 변수를 초기화하고자 componentN이라는 함수를 호출한다.\n<ul>\n<li>N은 구조 분해 선언에 있는 변수 위치에 따라 붙는 번호</li>\n</ul>\n</li>\n<li>data class의 주 생성자에 들어있는 프로퍼티에 대해서는 컴파일러가 자동으로 componentN 함수를 만들어준다.</li>\n</ul>\n<deckgo-highlight-code language=\"kotlin\" terminal=\"carbon\" theme=\"one-dark\"  >\n          <code slot=\"code\">data class NameComponents(val name: String, val extension: String)\n\nfun splitFileName(fullName: String): NameComponents {\n    val result = fullName.split(&#39;.&#39;, limit = 2)\n    return NameComponents(result[0], result[1])\n}\n\nfun main() {\n    val (name, ext) = splitFileName(&quot;example.kt&quot;)\n    println(name)\n    println(ext)\n}</code>\n        </deckgo-highlight-code>\n<ul>\n<li>구조 분해 선언을 사용해 여러 값 반환이 가능하다.</li>\n</ul>\n<deckgo-highlight-code language=\"kotlin\" terminal=\"carbon\" theme=\"one-dark\"  >\n          <code slot=\"code\">data class NameComponents(val name: String, val extension: String)\n\nfun splitFileName(fullName: String): NameComponents {\n    val (name, extension) = fullName.split(&#39;.&#39;, limit = 2)\n    return NameComponents(name, extension)\n}</code>\n        </deckgo-highlight-code>\n<ul>\n<li>컬렉션에 대해 구조 분해 선언 사용이 가능하다.</li>\n</ul>\n<h3 id=\"-941-구조-분해-선언과-루프\" style=\"position:relative;\"><a href=\"#-941-%EA%B5%AC%EC%A1%B0-%EB%B6%84%ED%95%B4-%EC%84%A0%EC%96%B8%EA%B3%BC-%EB%A3%A8%ED%94%84\" aria-label=\" 941 구조 분해 선언과 루프 permalink\" class=\"post-anchor before\"><svg aria-hidden=\"true\" focusable=\"false\" height=\"16\" version=\"1.1\" viewBox=\"0 0 16 16\" width=\"16\"><path fill-rule=\"evenodd\" d=\"M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z\"></path></svg></a>🔖 9.4.1 구조 분해 선언과 루프</h3>\n<deckgo-highlight-code language=\"kotlin\" terminal=\"carbon\" theme=\"one-dark\"  >\n          <code slot=\"code\">fun printEntries(map: Map&lt;String, String&gt;) {\n    for ((key, value) in map) {\n        println(&quot;$key -&gt; $value&quot;)\n    }\n}\n\nfun main() {\n    val map = mapOf(&quot;Oracle&quot; to &quot;Java&quot;, &quot;JetBrains&quot; to &quot;Kotlin&quot;)\n    printEntries(map)\n}</code>\n        </deckgo-highlight-code>\n<ul>\n<li>맵에 대한 확장 함수로 iterator가 들어있다.</li>\n</ul>\n<deckgo-highlight-code language=\"kotlin\" terminal=\"carbon\" theme=\"one-dark\"  >\n          <code slot=\"code\">    map.forEach { (key, value) -&gt; println(&quot;$key -&gt; $value&quot;) }</code>\n        </deckgo-highlight-code>\n<ul>\n<li>람다가 구조 분해 선언을 쓸 수 있다.</li>\n</ul>\n<h3 id=\"-942-_-문자를-사용해-구조-분해-값-무시\" style=\"position:relative;\"><a href=\"#-942-_-%EB%AC%B8%EC%9E%90%EB%A5%BC-%EC%82%AC%EC%9A%A9%ED%95%B4-%EA%B5%AC%EC%A1%B0-%EB%B6%84%ED%95%B4-%EA%B0%92-%EB%AC%B4%EC%8B%9C\" aria-label=\" 942 _ 문자를 사용해 구조 분해 값 무시 permalink\" class=\"post-anchor before\"><svg aria-hidden=\"true\" focusable=\"false\" height=\"16\" version=\"1.1\" viewBox=\"0 0 16 16\" width=\"16\"><path fill-rule=\"evenodd\" d=\"M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z\"></path></svg></a>🔖 9.4.2 _ 문자를 사용해 구조 분해 값 무시</h3>\n<deckgo-highlight-code language=\"kotlin\" terminal=\"carbon\" theme=\"one-dark\"  >\n          <code slot=\"code\">data class Person(val firstName: String, val lastName: String, val age: Int, val city: String)\n\nfun introducePerson(p: Person) {\n    val (firstName, lastName, age, city) = p\n    println(&quot;This is $firstName, aged $age.&quot;)\n}</code>\n        </deckgo-highlight-code>\n<ul>\n<li>전체 객체를 구조 분해해야만 하는 것은 아니기 때문에 구조 분해 선언에서 뒤쪽의 구조 분해 선언을 제거할 수는 잇다.</li>\n</ul>\n<deckgo-highlight-code language=\"kotlin\" terminal=\"carbon\" theme=\"one-dark\"  >\n          <code slot=\"code\">    val (firstName, lastName, age) = p</code>\n        </deckgo-highlight-code>\n<ul>\n<li>lastName을 그냥 없앨 수는 없다.</li>\n</ul>\n<deckgo-highlight-code language=\"kotlin\" terminal=\"carbon\" theme=\"one-dark\"  >\n          <code slot=\"code\">fun introducePerson(p: Person) {\n    val (firstName, _, age) = p\n    println(&quot;This is $firstName, aged $age.&quot;)\n}</code>\n        </deckgo-highlight-code>\n<ul>\n<li><code>_</code> 를 쓰면 컴포넌트를 무시하고 구조 분해 선언이 가능하다.</li>\n</ul>\n<h2 id=\"-95-프로퍼티-접근자-로직-재활용-위임-프로퍼티\" style=\"position:relative;\"><a href=\"#-95-%ED%94%84%EB%A1%9C%ED%8D%BC%ED%8B%B0-%EC%A0%91%EA%B7%BC%EC%9E%90-%EB%A1%9C%EC%A7%81-%EC%9E%AC%ED%99%9C%EC%9A%A9-%EC%9C%84%EC%9E%84-%ED%94%84%EB%A1%9C%ED%8D%BC%ED%8B%B0\" aria-label=\" 95 프로퍼티 접근자 로직 재활용 위임 프로퍼티 permalink\" class=\"post-anchor before\"><svg aria-hidden=\"true\" focusable=\"false\" height=\"16\" version=\"1.1\" viewBox=\"0 0 16 16\" width=\"16\"><path fill-rule=\"evenodd\" d=\"M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z\"></path></svg></a>📖 9.5 프로퍼티 접근자 로직 재활용: 위임 프로퍼티</h2>\n<h3 id=\"-951-위임-프로퍼티의-기본-문법과-내부-동작\" style=\"position:relative;\"><a href=\"#-951-%EC%9C%84%EC%9E%84-%ED%94%84%EB%A1%9C%ED%8D%BC%ED%8B%B0%EC%9D%98-%EA%B8%B0%EB%B3%B8-%EB%AC%B8%EB%B2%95%EA%B3%BC-%EB%82%B4%EB%B6%80-%EB%8F%99%EC%9E%91\" aria-label=\" 951 위임 프로퍼티의 기본 문법과 내부 동작 permalink\" class=\"post-anchor before\"><svg aria-hidden=\"true\" focusable=\"false\" height=\"16\" version=\"1.1\" viewBox=\"0 0 16 16\" width=\"16\"><path fill-rule=\"evenodd\" d=\"M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z\"></path></svg></a>🔖 9.5.1 위임 프로퍼티의 기본 문법과 내부 동작</h3>\n<deckgo-highlight-code language=\"kotlin\" terminal=\"carbon\" theme=\"one-dark\"  >\n          <code slot=\"code\">class Foo {\n    var p = Type by Delegate()\n}</code>\n        </deckgo-highlight-code>\n<ul>\n<li>p 프로퍼티는 접근자 로직을 다른 객체에 위임한다.</li>\n</ul>\n<deckgo-highlight-code language=\"kotlin\" terminal=\"carbon\" theme=\"one-dark\"  >\n          <code slot=\"code\">class Foo {\n    private val delegate = Delegate()\n    \n    var p: Type\n        set(value: Type) = delegate.setValue(/**/, value)\n        get() = delegate.getValue(/**/)\n}</code>\n        </deckgo-highlight-code>\n<ul>\n<li>프로퍼티 위임 관례에 따라 Delegate 클래스는 get, set을 제공해야한다.</li>\n<li>by 키워드는 프로퍼티와 위임객체를 연결한다.</li>\n</ul>\n<h3 id=\"-952-위임-프로퍼티-사용-by-lazy를-사용한-지연-초기화\" style=\"position:relative;\"><a href=\"#-952-%EC%9C%84%EC%9E%84-%ED%94%84%EB%A1%9C%ED%8D%BC%ED%8B%B0-%EC%82%AC%EC%9A%A9-by-lazy%EB%A5%BC-%EC%82%AC%EC%9A%A9%ED%95%9C-%EC%A7%80%EC%97%B0-%EC%B4%88%EA%B8%B0%ED%99%94\" aria-label=\" 952 위임 프로퍼티 사용 by lazy를 사용한 지연 초기화 permalink\" class=\"post-anchor before\"><svg aria-hidden=\"true\" focusable=\"false\" height=\"16\" version=\"1.1\" viewBox=\"0 0 16 16\" width=\"16\"><path fill-rule=\"evenodd\" d=\"M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z\"></path></svg></a>🔖 9.5.2 위임 프로퍼티 사용: by lazy()를 사용한 지연 초기화</h3>\n<ul>\n<li>지연 초기화는 객체의 일부분을 초기화하지 않고 남겨뒀다가 실제로 그 부분의 값이 필요할 경우 초기화할 때 쓰이는 패턴</li>\n</ul>\n<deckgo-highlight-code language=\"kotlin\" terminal=\"carbon\" theme=\"one-dark\"  >\n          <code slot=\"code\">class Person(val name: String) {\n    private var _emails: List&lt;String&gt;? = null\n\n    val emails: List&lt;String&gt;\n        get() {\n            if (_emails == null) {\n                _emails = loadEamils(this)\n            }\n            return _emails!!\n        }\n}\n\n\nfun main() {\n    val p = Person(&quot;Alice&quot;)\n    p.emails\n}</code>\n        </deckgo-highlight-code>\n<ul>\n<li>뒷받침하는 프로퍼티 기법 사용</li>\n<li>클래스 같은 개념을 표헌하는 프로퍼티가 2개 있을 때 비공개 프로퍼티 앞에 밑줄을 붙이며, 공개 프로퍼티에는 아무것도 붙이지 않는다.</li>\n</ul>\n<deckgo-highlight-code language=\"kotlin\" terminal=\"carbon\" theme=\"one-dark\"  >\n          <code slot=\"code\">class Person(val name: String) {\n    val emails by lazy { loadEmails(this) }\n}</code>\n        </deckgo-highlight-code>\n<ul>\n<li>위임 프로퍼티를 사용하면 훨씬 간단해진다.</li>\n<li>lazy 함수는 기본적으로 스레드 안전하다.</li>\n</ul>\n<h3 id=\"-953-위임-프로퍼티-구현\" style=\"position:relative;\"><a href=\"#-953-%EC%9C%84%EC%9E%84-%ED%94%84%EB%A1%9C%ED%8D%BC%ED%8B%B0-%EA%B5%AC%ED%98%84\" aria-label=\" 953 위임 프로퍼티 구현 permalink\" class=\"post-anchor before\"><svg aria-hidden=\"true\" focusable=\"false\" height=\"16\" version=\"1.1\" viewBox=\"0 0 16 16\" width=\"16\"><path fill-rule=\"evenodd\" d=\"M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z\"></path></svg></a>🔖 9.5.3 위임 프로퍼티 구현</h3>\n<deckgo-highlight-code language=\"kotlin\" terminal=\"carbon\" theme=\"one-dark\"  >\n          <code slot=\"code\">fun interface Observer {\n    fun onChange(name: String, oldValue: Any?, newValue: Any?)\n}\n\nopen class Observable {\n    val observers = mutableListOf&lt;Observer&gt;()\n    fun notifyObservers(propName: String, oldValue: Any?, newValue: Any?) {\n        for (obs in observers) {\n            obs.onChange(propName, oldValue, newValue)\n        }\n    }\n}\n\nclass Person(val name: String, age: Int, salary: Int) : Observable() {\n    var age: Int = age\n        set(newValue) {\n            val oldValue = field\n            field = newValue\n            notifyObservers(&quot;age&quot;, oldValue, newValue)\n        }\n    var salary: Int = salary\n        set(newValue) {\n            val oldValue = field\n            field = newValue\n            notifyObservers(&quot;salary&quot;, oldValue, newValue)\n        }\n}</code>\n        </deckgo-highlight-code>\n<ul>\n<li>field 키워드를 사용해 age와 salary 프로퍼티를 뒷받침하는 필드에 접근하는 방법을 보여준다.</li>\n</ul>\n<deckgo-highlight-code language=\"kotlin\" terminal=\"carbon\" theme=\"one-dark\"  >\n          <code slot=\"code\">class ObservableProperty(val propName: String, var propValue: Int, val observable: Observable) {\n    fun getValue(): Int = propValue\n    fun setValue(newValue: Int) {\n        val oldValue = propValue\n        propValue = newValue\n        observable.notifyObservers(propName, oldValue, newValue)\n    }\n}\n\nclass Person(val name: String, age: Int, salary: Int) : Observable() {\n    val _age = ObservableProperty(&quot;age&quot;, age, this)\n    var age: Int\n        get() = _age.getValue()\n        set(value) {\n            _age.setValue(value)\n        }\n    val _salary = ObservableProperty(&quot;salary&quot;, salary, this)\n    var salary: Int\n        get() = _salary.getValue()\n        set(value) {\n            _salary.setValue(value)\n        }\n}</code>\n        </deckgo-highlight-code>\n<ul>\n<li>프로퍼티 값을 저장하고 그 값이 바뀌면 자동으로 변경 통지를 전달해주는 클래스를 만들었다.</li>\n<li>위임 프로퍼티를 사용하면 더 간단해진다.</li>\n</ul>\n<deckgo-highlight-code language=\"kotlin\" terminal=\"carbon\" theme=\"one-dark\"  >\n          <code slot=\"code\">class ObservableProperty(var propValue: Int, val observable: Observable) {\n    operator fun getValue(thisRef: Any?, prop: KProperty&lt;*&gt;): Int = propValue\n    operator fun setValue(thisRef: Any?, prop: KProperty&lt;*&gt;, newValue: Int) {\n        val oldValue = propValue\n        propValue = newValue\n        observable.notifyObservers(prop.name, oldValue, newValue)\n    }\n}</code>\n        </deckgo-highlight-code>\n<ul>\n<li>operator 변경자가 붙는다.</li>\n<li>KProperty 타입의 객체를 사용해 프로퍼티 표현</li>\n</ul>\n<deckgo-highlight-code language=\"kotlin\" terminal=\"carbon\" theme=\"one-dark\"  >\n          <code slot=\"code\">class Person(val name: String, age: Int, salary: Int) : Observable() {\n    val age by ObservableProperty(age, this)\n    var salary by ObservableProperty(salary, this)\n}</code>\n        </deckgo-highlight-code>\n<ul>\n<li>여러 작업을 컴파일러가 자동으로 처리해준다.</li>\n<li>by의 오른쪽에 있는 식이 꼭 새 인스턴스를 만들 필요는 없다.</li>\n</ul>\n<h3 id=\"-954-위임-프로퍼티는-커스텀-접근자가-있는-감춰진-프로퍼티로-변환된다\" style=\"position:relative;\"><a href=\"#-954-%EC%9C%84%EC%9E%84-%ED%94%84%EB%A1%9C%ED%8D%BC%ED%8B%B0%EB%8A%94-%EC%BB%A4%EC%8A%A4%ED%85%80-%EC%A0%91%EA%B7%BC%EC%9E%90%EA%B0%80-%EC%9E%88%EB%8A%94-%EA%B0%90%EC%B6%B0%EC%A7%84-%ED%94%84%EB%A1%9C%ED%8D%BC%ED%8B%B0%EB%A1%9C-%EB%B3%80%ED%99%98%EB%90%9C%EB%8B%A4\" aria-label=\" 954 위임 프로퍼티는 커스텀 접근자가 있는 감춰진 프로퍼티로 변환된다 permalink\" class=\"post-anchor before\"><svg aria-hidden=\"true\" focusable=\"false\" height=\"16\" version=\"1.1\" viewBox=\"0 0 16 16\" width=\"16\"><path fill-rule=\"evenodd\" d=\"M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z\"></path></svg></a>🔖 9.5.4 위임 프로퍼티는 커스텀 접근자가 있는 감춰진 프로퍼티로 변환된다</h3>\n<deckgo-highlight-code language=\"kotlin\" terminal=\"carbon\" theme=\"one-dark\"  >\n          <code slot=\"code\">class C {\n    private val &lt;delegate&gt; = MyDelegate()\n    \n    var prop: Type\n        get() = &lt;delegate&gt;.getValue(this, &lt;property&gt;)\n        set(value: Type) = &lt;delegate&gt;.setValue(this, &lt;property&gt;, value)\n}</code>\n        </deckgo-highlight-code>\n<ul>\n<li>컴파일러는 모든 프로퍼티 접근자 안에 getValue, setValue 호출 코드를 생성해준다.</li>\n<li>프로퍼티 값이 저장될 장소를 바꿀 수도 있고 프로퍼티를 읽거나 쓸 때 벌어질 일을 변경할 수도 있다.</li>\n</ul>\n<h3 id=\"-955-맵에-위임해서-동적으로-애트리뷰트-접근\" style=\"position:relative;\"><a href=\"#-955-%EB%A7%B5%EC%97%90-%EC%9C%84%EC%9E%84%ED%95%B4%EC%84%9C-%EB%8F%99%EC%A0%81%EC%9C%BC%EB%A1%9C-%EC%95%A0%ED%8A%B8%EB%A6%AC%EB%B7%B0%ED%8A%B8-%EC%A0%91%EA%B7%BC\" aria-label=\" 955 맵에 위임해서 동적으로 애트리뷰트 접근 permalink\" class=\"post-anchor before\"><svg aria-hidden=\"true\" focusable=\"false\" height=\"16\" version=\"1.1\" viewBox=\"0 0 16 16\" width=\"16\"><path fill-rule=\"evenodd\" d=\"M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z\"></path></svg></a>🔖 9.5.5 맵에 위임해서 동적으로 애트리뷰트 접근</h3>\n<deckgo-highlight-code language=\"kotlin\" terminal=\"carbon\" theme=\"one-dark\"  >\n          <code slot=\"code\">class Person {\n    private val _attributes = mutableMapOf&lt;String, String&gt;()\n\n    fun setAttribute(attrName: String, value: String) {\n        _attributes[attrName] = value\n    }\n\n    var name: String\n        get() = _attributes[&quot;name&quot;]!!\n        set(value) {\n            _attributes[&quot;name&quot;] = value\n        }\n}</code>\n        </deckgo-highlight-code>\n<ul>\n<li>위임 프로퍼티를 활용하도록 변경하는 것은 아주 쉽다.</li>\n</ul>\n<deckgo-highlight-code language=\"kotlin\" terminal=\"carbon\" theme=\"one-dark\"  >\n          <code slot=\"code\">class Person {\n    private val _attributes = mutableMapOf&lt;String, String&gt;()\n\n    fun setAttribute(attrName: String, value: String) {\n        _attributes[attrName] = value\n    }\n\n    var name: String by _attributes\n}</code>\n        </deckgo-highlight-code>\n<ul>\n<li>Map에 대해 getValue, setValue 확장 함수를 제공하기에 동작함.</li>\n<li>getValue에서 맵에 프로퍼티 값을 저장할 때는 자동으로 프로퍼티 이름을 키로 활용</li>\n</ul>\n<h3 id=\"-956-실전-프레임워크가-위임-프로퍼티를-활용하는-방법\" style=\"position:relative;\"><a href=\"#-956-%EC%8B%A4%EC%A0%84-%ED%94%84%EB%A0%88%EC%9E%84%EC%9B%8C%ED%81%AC%EA%B0%80-%EC%9C%84%EC%9E%84-%ED%94%84%EB%A1%9C%ED%8D%BC%ED%8B%B0%EB%A5%BC-%ED%99%9C%EC%9A%A9%ED%95%98%EB%8A%94-%EB%B0%A9%EB%B2%95\" aria-label=\" 956 실전 프레임워크가 위임 프로퍼티를 활용하는 방법 permalink\" class=\"post-anchor before\"><svg aria-hidden=\"true\" focusable=\"false\" height=\"16\" version=\"1.1\" viewBox=\"0 0 16 16\" width=\"16\"><path fill-rule=\"evenodd\" d=\"M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z\"></path></svg></a>🔖 9.5.6 실전 프레임워크가 위임 프로퍼티를 활용하는 방법</h3>\n<ul>\n<li>위임 프로퍼티는 프로퍼티의 접근 로직을 재사용 가능하게 만들어, 코드의 중복을 줄이고 유지보수성을 향상시킨다.</li>\n<li>프레임워크나 라이브러리에서 공통된 프로퍼티 동작을 캡슐화하여, 사용자 코드의 간결함과 일관성을 유지할 수 있다.</li>\n</ul>","excerpt":"코틀린은 어떤 언어 기능과 미리 정해진 이름의 함수를 연결해주는 기법인 관례에 의존한다. 📖 9.1 산술 연산자를 오버로드해서 임의의 클래스에 대한 연산을 더 편리하게 만들기 🔖 9.1.1 plus, times, divide 등: 이항 산술 연산 오버로딩 plus 함수 앞에 operator 키워드를 붙여야 한다. 연산자를 확장 함수로 정의할 수도 있다. 표현식 함수 이름 a + b plus a - b minus a * b times a / b div a % b mod…","fields":{"slug":"/backend/kotlin-in-action/9장-연산자_오버로딩과_다른_관례/"},"frontmatter":{"title":"Kotlin in Action - 9장 연산자 오버로딩과 다른 관례","thumbnail":{"childImageSharp":{"fluid":{"src":"/static/f7f7ddfa31d1614405dc5af1487b9ec4/9c94d/kotlin-in-action.png"}}},"draft":false,"category":"Back-End","tags":["Kotlin"],"date":"April 27, 2025"}},"node":{"id":"551d6a82-918e-5d5b-a5da-34a62ef7491d","html":"<h2 id=\"-81-원시-타입과-기본-타입\" style=\"position:relative;\"><a href=\"#-81-%EC%9B%90%EC%8B%9C-%ED%83%80%EC%9E%85%EA%B3%BC-%EA%B8%B0%EB%B3%B8-%ED%83%80%EC%9E%85\" aria-label=\" 81 원시 타입과 기본 타입 permalink\" class=\"post-anchor before\"><svg aria-hidden=\"true\" focusable=\"false\" height=\"16\" version=\"1.1\" viewBox=\"0 0 16 16\" width=\"16\"><path fill-rule=\"evenodd\" d=\"M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z\"></path></svg></a>📖 8.1 원시 타입과 기본 타입</h2>\n<h3 id=\"-811-정수-부동소수점-수-문자-불리언-값을-원시-타입으로-표현\" style=\"position:relative;\"><a href=\"#-811-%EC%A0%95%EC%88%98-%EB%B6%80%EB%8F%99%EC%86%8C%EC%88%98%EC%A0%90-%EC%88%98-%EB%AC%B8%EC%9E%90-%EB%B6%88%EB%A6%AC%EC%96%B8-%EA%B0%92%EC%9D%84-%EC%9B%90%EC%8B%9C-%ED%83%80%EC%9E%85%EC%9C%BC%EB%A1%9C-%ED%91%9C%ED%98%84\" aria-label=\" 811 정수 부동소수점 수 문자 불리언 값을 원시 타입으로 표현 permalink\" class=\"post-anchor before\"><svg aria-hidden=\"true\" focusable=\"false\" height=\"16\" version=\"1.1\" viewBox=\"0 0 16 16\" width=\"16\"><path fill-rule=\"evenodd\" d=\"M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z\"></path></svg></a>🔖 8.1.1 정수, 부동소수점 수, 문자, 불리언 값을 원시 타입으로 표현</h3>\n<deckgo-highlight-code language=\"kotlin\" terminal=\"carbon\" theme=\"one-dark\"  >\n          <code slot=\"code\">val i: Int = 1\nval list: List&lt;Int&gt; = listOf(1, 2, 3)</code>\n        </deckgo-highlight-code>\n<ul>\n<li>코틀린은 원시 타입과 래퍼 타입을 구분하지 않는다.</li>\n<li>매번 객체로 표현하는 것이 아니라 실행 시점에 숫자 타입은 가능한 한 가장 효율적인 방식으로 표현된다.</li>\n</ul>\n<h3 id=\"-812-양수를-표현하기-위해-모든-비트-범위-사용-부호-없는-숫자-타입\" style=\"position:relative;\"><a href=\"#-812-%EC%96%91%EC%88%98%EB%A5%BC-%ED%91%9C%ED%98%84%ED%95%98%EA%B8%B0-%EC%9C%84%ED%95%B4-%EB%AA%A8%EB%93%A0-%EB%B9%84%ED%8A%B8-%EB%B2%94%EC%9C%84-%EC%82%AC%EC%9A%A9-%EB%B6%80%ED%98%B8-%EC%97%86%EB%8A%94-%EC%88%AB%EC%9E%90-%ED%83%80%EC%9E%85\" aria-label=\" 812 양수를 표현하기 위해 모든 비트 범위 사용 부호 없는 숫자 타입 permalink\" class=\"post-anchor before\"><svg aria-hidden=\"true\" focusable=\"false\" height=\"16\" version=\"1.1\" viewBox=\"0 0 16 16\" width=\"16\"><path fill-rule=\"evenodd\" d=\"M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z\"></path></svg></a>🔖 8.1.2 양수를 표현하기 위해 모든 비트 범위 사용: 부호 없는 숫자 타입</h3>\n<table>\n<thead>\n<tr>\n<th>타입</th>\n<th>비트 크기</th>\n<th>표현 범위</th>\n</tr>\n</thead>\n<tbody>\n<tr>\n<td><code>UByte</code></td>\n<td>8비트</td>\n<td>0 ~ 255</td>\n</tr>\n<tr>\n<td><code>UShort</code></td>\n<td>16비트</td>\n<td>0 ~ 65,535</td>\n</tr>\n<tr>\n<td><code>UInt</code></td>\n<td>32비트</td>\n<td>0 ~ 4,294,967,295</td>\n</tr>\n<tr>\n<td><code>ULong</code></td>\n<td>64비트</td>\n<td>0 ~ 18,446,744,073,709,551,615</td>\n</tr>\n</tbody>\n</table>\n<ul>\n<li>코틀린에는 4가지 부호없는 타입이 있다.</li>\n<li>일반적인 원시 타입을 확장해 부호 없는 타입을 제공한다.</li>\n</ul>\n<h3 id=\"-813-널이-될-수-있는-기본-타입-int-boolean-등\" style=\"position:relative;\"><a href=\"#-813-%EB%84%90%EC%9D%B4-%EB%90%A0-%EC%88%98-%EC%9E%88%EB%8A%94-%EA%B8%B0%EB%B3%B8-%ED%83%80%EC%9E%85-int-boolean-%EB%93%B1\" aria-label=\" 813 널이 될 수 있는 기본 타입 int boolean 등 permalink\" class=\"post-anchor before\"><svg aria-hidden=\"true\" focusable=\"false\" height=\"16\" version=\"1.1\" viewBox=\"0 0 16 16\" width=\"16\"><path fill-rule=\"evenodd\" d=\"M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z\"></path></svg></a>🔖 8.1.3 널이 될 수 있는 기본 타입: Int?, Boolean? 등</h3>\n<ul>\n<li>null 참조를 자바의 참조 타입의 변수에만 대입할 수 있기 때문에 널이 될 수 있는 코틀린 타입은 자바 원시 타입으로 표현할 수 없다.</li>\n</ul>\n<deckgo-highlight-code language=\"kotlin\" terminal=\"carbon\" theme=\"one-dark\"  >\n          <code slot=\"code\">data class Person(val name: String, val age: Int? = null) {\n    fun isOlderThan(other: Person): Boolean? {\n        if (age == null || other.age == null) {\n            return null\n        }\n        return age &gt; other.age\n    }\n}</code>\n        </deckgo-highlight-code>\n<ul>\n<li>두 값이 널이 아닌지 검사해야 한다.</li>\n<li>컴파일러는 널 검사를 마친 다음에야 두 값을 일반적인 값처럼 다루도록 허용한다.</li>\n</ul>\n<h3 id=\"-814-수-변환\" style=\"position:relative;\"><a href=\"#-814-%EC%88%98-%EB%B3%80%ED%99%98\" aria-label=\" 814 수 변환 permalink\" class=\"post-anchor before\"><svg aria-hidden=\"true\" focusable=\"false\" height=\"16\" version=\"1.1\" viewBox=\"0 0 16 16\" width=\"16\"><path fill-rule=\"evenodd\" d=\"M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z\"></path></svg></a>🔖 8.1.4 수 변환</h3>\n<ul>\n<li>코틀린과 자바의 가장 큰 차이점 중 하나는 수를 변환하는 방식이다.</li>\n<li>코틀린은 한 타입의 수를 다른 타입의 수로 자동 변환하지 않는다.</li>\n</ul>\n<deckgo-highlight-code language=\"kotlin\" terminal=\"carbon\" theme=\"one-dark\"  >\n          <code slot=\"code\">val i = 1\nval i: Long = b.toLong() // 자동 변환 X</code>\n        </deckgo-highlight-code>\n<ul>\n<li>모든 원시 타입에 대해 양방향 변환 함수가 모두 제공</li>\n</ul>\n<deckgo-highlight-code language=\"kotlin\" terminal=\"carbon\" theme=\"one-dark\"  >\n          <code slot=\"code\">val x = 1\nval list = listOf(1L, 2L, 3L)\nx in list // false</code>\n        </deckgo-highlight-code>\n<ul>\n<li>암시적 변환을 허용하지 않는다.</li>\n<li>숫자 리터럴을 상요할 때는 변환 함수를 호출할 필요가 없다.\n<ul>\n<li>ex) 1L, 0.2f</li>\n<li>컴파일러가 필요한 변환을 자동으로 넣어준다.</li>\n</ul>\n</li>\n</ul>\n<deckgo-highlight-code language=\"kotlin\" terminal=\"carbon\" theme=\"one-dark\"  >\n          <code slot=\"code\">fun main() {\n    println(Int.MAX_VALUE + 1)\n    // 음수 최솟값\n    \n    println(Int.MIN_VALUE - 1)\n    // 양수 최댓값\n}</code>\n        </deckgo-highlight-code>\n<ul>\n<li>코틀린 산술 연산자에서도 숫자 연산 시 오버플로나 언더플로가 발생할 수 있다.</li>\n<li>검사하느라 추가비용이 들지 않는다.</li>\n</ul>\n<h3 id=\"-815-any와-any-코틀린-타입-계층의-뿌리\" style=\"position:relative;\"><a href=\"#-815-any%EC%99%80-any-%EC%BD%94%ED%8B%80%EB%A6%B0-%ED%83%80%EC%9E%85-%EA%B3%84%EC%B8%B5%EC%9D%98-%EB%BF%8C%EB%A6%AC\" aria-label=\" 815 any와 any 코틀린 타입 계층의 뿌리 permalink\" class=\"post-anchor before\"><svg aria-hidden=\"true\" focusable=\"false\" height=\"16\" version=\"1.1\" viewBox=\"0 0 16 16\" width=\"16\"><path fill-rule=\"evenodd\" d=\"M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z\"></path></svg></a>🔖 8.1.5 Any와 Any?: 코틀린 타입 계층의 뿌리</h3>\n<ul>\n<li>자바와 달리 Any가 원시 타입을 포함한 모든 타입의 조상 타입이다.</li>\n</ul>\n<deckgo-highlight-code language=\"kotlin\" terminal=\"carbon\" theme=\"one-dark\"  >\n          <code slot=\"code\">val answer: Any = 42</code>\n        </deckgo-highlight-code>\n<ul>\n<li>원시 타입 값을 Any 타입의 변수에 대입하면 자동으로 값을 객체로 감싼다.(박싱)</li>\n<li>Any가 널이 될 수 없는 타입임에 유의</li>\n</ul>\n<h3 id=\"-816-unit-타입-코틀린의-void\" style=\"position:relative;\"><a href=\"#-816-unit-%ED%83%80%EC%9E%85-%EC%BD%94%ED%8B%80%EB%A6%B0%EC%9D%98-void\" aria-label=\" 816 unit 타입 코틀린의 void permalink\" class=\"post-anchor before\"><svg aria-hidden=\"true\" focusable=\"false\" height=\"16\" version=\"1.1\" viewBox=\"0 0 16 16\" width=\"16\"><path fill-rule=\"evenodd\" d=\"M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z\"></path></svg></a>🔖 8.1.6 Unit 타입: 코틀린의 void</h3>\n<deckgo-highlight-code language=\"kotlin\" terminal=\"carbon\" theme=\"one-dark\"  >\n          <code slot=\"code\">fun f(): Unit {}\nfun f() {}</code>\n        </deckgo-highlight-code>\n<ul>\n<li>Unit을 반환하지만 타입을 지정할 필요는 없다.</li>\n<li>return을 명시할 필요가 없다.\n<ul>\n<li>컴파일러가 암시적으로 <code>return Unit</code>을 넣어준다.</li>\n</ul>\n</li>\n<li>함수형 프로그래밍에서 전통적으로 Unit은 <strong>단 하나의 인스턴스만 갖는 타입</strong>을 의미</li>\n<li>java의 void와 인스턴스의 유무가 가장 큰 차이</li>\n</ul>\n<h3 id=\"-817-nothing-타입-이-함수는-결코-반환되지-않는다\" style=\"position:relative;\"><a href=\"#-817-nothing-%ED%83%80%EC%9E%85-%EC%9D%B4-%ED%95%A8%EC%88%98%EB%8A%94-%EA%B2%B0%EC%BD%94-%EB%B0%98%ED%99%98%EB%90%98%EC%A7%80-%EC%95%8A%EB%8A%94%EB%8B%A4\" aria-label=\" 817 nothing 타입 이 함수는 결코 반환되지 않는다 permalink\" class=\"post-anchor before\"><svg aria-hidden=\"true\" focusable=\"false\" height=\"16\" version=\"1.1\" viewBox=\"0 0 16 16\" width=\"16\"><path fill-rule=\"evenodd\" d=\"M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z\"></path></svg></a>🔖 8.1.7 Nothing 타입: 이 함수는 결코 반환되지 않는다</h3>\n<deckgo-highlight-code language=\"kotlin\" terminal=\"carbon\" theme=\"one-dark\"  >\n          <code slot=\"code\">fun fail(message: String): Nothing {\n    throw IllegalArgumentException(message)\n}</code>\n        </deckgo-highlight-code>\n<ul>\n<li>Nothing 타입은 아무 값도 포함하지 않는다.</li>\n<li>함수의 반환 타입이나 반환 타입으로 쓰일 타입 파라미터로만 쓸 수 있다.</li>\n</ul>\n<deckgo-highlight-code language=\"kotlin\" terminal=\"carbon\" theme=\"one-dark\"  >\n          <code slot=\"code\">val address = company.address ?: fail(&quot;No address&quot;)</code>\n        </deckgo-highlight-code>\n<ul>\n<li>Nothing을 반환하는 함수를 엘비스 연산자의 오른쪽에 사용해서 전제조건을 검사할 수 있다.</li>\n</ul>\n<h2 id=\"-82-컬렉션과-배열\" style=\"position:relative;\"><a href=\"#-82-%EC%BB%AC%EB%A0%89%EC%85%98%EA%B3%BC-%EB%B0%B0%EC%97%B4\" aria-label=\" 82 컬렉션과 배열 permalink\" class=\"post-anchor before\"><svg aria-hidden=\"true\" focusable=\"false\" height=\"16\" version=\"1.1\" viewBox=\"0 0 16 16\" width=\"16\"><path fill-rule=\"evenodd\" d=\"M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z\"></path></svg></a>📖 8.2 컬렉션과 배열</h2>\n<h3 id=\"-821-널이-될-수-있는-값의-컬렉션과-널이-될-수-있는-컬렉션\" style=\"position:relative;\"><a href=\"#-821-%EB%84%90%EC%9D%B4-%EB%90%A0-%EC%88%98-%EC%9E%88%EB%8A%94-%EA%B0%92%EC%9D%98-%EC%BB%AC%EB%A0%89%EC%85%98%EA%B3%BC-%EB%84%90%EC%9D%B4-%EB%90%A0-%EC%88%98-%EC%9E%88%EB%8A%94-%EC%BB%AC%EB%A0%89%EC%85%98\" aria-label=\" 821 널이 될 수 있는 값의 컬렉션과 널이 될 수 있는 컬렉션 permalink\" class=\"post-anchor before\"><svg aria-hidden=\"true\" focusable=\"false\" height=\"16\" version=\"1.1\" viewBox=\"0 0 16 16\" width=\"16\"><path fill-rule=\"evenodd\" d=\"M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z\"></path></svg></a>🔖 8.2.1 널이 될 수 있는 값의 컬렉션과 널이 될 수 있는 컬렉션</h3>\n<deckgo-highlight-code language=\"kotlin\" terminal=\"carbon\" theme=\"one-dark\"  >\n          <code slot=\"code\">fun readNumbers(text: String): List&lt;Int?&gt; {\n    val result = mutableListOf&lt;Int?&gt;()\n    for (line in text.lineSequence()) {\n        val numberOrNull = line.toIntOrNull()\n        result.add(numberOrNull)\n    }\n    return result\n}</code>\n        </deckgo-highlight-code>\n<ul>\n<li><code>List&#x3C;Int?></code>\n<ul>\n<li>List 자체는 항상 Null이 아니다.</li>\n<li>각 원소는 Null이 될 수 있다.</li>\n</ul>\n</li>\n<li><code>List&#x3C;Int>?</code>\n<ul>\n<li>List는 Null이 될 수 있다.</li>\n<li>각 원소는 Null이 될 수 없다.</li>\n</ul>\n</li>\n</ul>\n<deckgo-highlight-code language=\"kotlin\" terminal=\"carbon\" theme=\"one-dark\"  >\n          <code slot=\"code\">fun readNumbers2(text: String): List&lt;Int?&gt; = text.lineSequence().map { it.toIntOrNull() }.toList()</code>\n        </deckgo-highlight-code>\n<ul>\n<li>함수형 프로그래밍으로 간단하게 표현할 수 있다.</li>\n<li>널이 될 수 있는 값으로 이뤄지고, 널이 될 수 있는 리스트를 정의해야 한다면 <code>List&#x3C;Int?>?</code>로 표현할 수 있다.</li>\n</ul>\n<deckgo-highlight-code language=\"kotlin\" terminal=\"carbon\" theme=\"one-dark\"  >\n          <code slot=\"code\">\nfun addValidNumbers(numbers: List&lt;Int?&gt;) {\n    var sumOfValidNumbers = 0\n    var invalidNumbers = 0\n    for (number in numbers) {\n        if (number != null) {\n            sumOfValidNumbers += number\n        } else {\n            invalidNumbers++\n        }\n    }\n    println(&quot;Sum of valid numbers: $sumOfValidNumbers&quot;)\n    println(&quot;Invalid numbers: $invalidNumbers&quot;)\n}\n\nfun main() {\n    val input = &quot;&quot;&quot;\n        1\n        abc\n        42\n    &quot;&quot;&quot;.trimIndent()\n    val numbers = readNumbers(input)\n    addValidNumbers(numbers)\n    // Sum of valid numbers: 43\n    // Invalid numbers: 1\n}</code>\n        </deckgo-highlight-code>\n<ul>\n<li><code>filterNotNull</code>을 사용해 간단하게 만들 수 있다.</li>\n</ul>\n<deckgo-highlight-code language=\"kotlin\" terminal=\"carbon\" theme=\"one-dark\"  >\n          <code slot=\"code\">fun addValidNumbers(numbers: List&lt;Int?&gt;) {\n    val validNumbers = numbers.filterNotNull()\n    println(&quot;Sum of valid numbers: ${validNumbers.sum()}&quot;)\n    println(&quot;Invalid numbers: ${numbers.size - validNumbers.size}&quot;)\n}</code>\n        </deckgo-highlight-code>\n<h3 id=\"-822-읽기-전용과-변경-가능한-컬렉션\" style=\"position:relative;\"><a href=\"#-822-%EC%9D%BD%EA%B8%B0-%EC%A0%84%EC%9A%A9%EA%B3%BC-%EB%B3%80%EA%B2%BD-%EA%B0%80%EB%8A%A5%ED%95%9C-%EC%BB%AC%EB%A0%89%EC%85%98\" aria-label=\" 822 읽기 전용과 변경 가능한 컬렉션 permalink\" class=\"post-anchor before\"><svg aria-hidden=\"true\" focusable=\"false\" height=\"16\" version=\"1.1\" viewBox=\"0 0 16 16\" width=\"16\"><path fill-rule=\"evenodd\" d=\"M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z\"></path></svg></a>🔖 8.2.2 읽기 전용과 변경 가능한 컬렉션</h3>\n<p>코틀린의 컬렉션 인터페이스는 읽기 전용(read-only) 과 변경 가능(mutable) 두 가지 버전이 있다.</p>\n<deckgo-highlight-code language=\"kotlin\" terminal=\"carbon\" theme=\"one-dark\"  >\n          <code slot=\"code\">fun &lt;T&gt; copyElements(source: Collection&lt;T&gt;, target: MutableCollection&lt;T&gt;) {\n    for (item in source) {\n        target.add(item)\n    }\n}</code>\n        </deckgo-highlight-code>\n<deckgo-highlight-code language=\"kotlin\" terminal=\"carbon\" theme=\"one-dark\"  >\n          <code slot=\"code\">fun &lt;T&gt; copyElements(source: Collection&lt;T&gt;, target: MutableCollection&lt;T&gt;) {\n    for (item in source) {\n        target.add(item)\n    }\n}\n\nfun main() {\n    val source: Collection&lt;Int&gt; = arrayListOf(3, 5, 7)\n    val target: Collection&lt;Int&gt; = arrayListOf(1)\n    copyElements(source, target) // 컴파일 에러 발생\n}</code>\n        </deckgo-highlight-code>\n<ul>\n<li>읽기 전용 컬렉션이 항상 thread safe하지는 않는다.\n<ul>\n<li>내부에서 변경할 수도 있음!</li>\n<li>즉, 불변은 아니다.</li>\n</ul>\n</li>\n</ul>\n<h3 id=\"-823-코틀린-컬렉션과-자바-컬렉션은-밀접히-연관됨\" style=\"position:relative;\"><a href=\"#-823-%EC%BD%94%ED%8B%80%EB%A6%B0-%EC%BB%AC%EB%A0%89%EC%85%98%EA%B3%BC-%EC%9E%90%EB%B0%94-%EC%BB%AC%EB%A0%89%EC%85%98%EC%9D%80-%EB%B0%80%EC%A0%91%ED%9E%88-%EC%97%B0%EA%B4%80%EB%90%A8\" aria-label=\" 823 코틀린 컬렉션과 자바 컬렉션은 밀접히 연관됨 permalink\" class=\"post-anchor before\"><svg aria-hidden=\"true\" focusable=\"false\" height=\"16\" version=\"1.1\" viewBox=\"0 0 16 16\" width=\"16\"><path fill-rule=\"evenodd\" d=\"M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z\"></path></svg></a>🔖 8.2.3 코틀린 컬렉션과 자바 컬렉션은 밀접히 연관됨</h3>\n<p>모든 코틀린 컬렉션은 그에 상응하는 자바 컬렉션 인터페이스의 인스턴스이다.</p>\n<ul>\n<li>코틀린의 읽기 전용과 변경 가능 인터페이스의 기본 구조는 <code>java.util</code> 패키지에 있는 자바 컬렉션 인터페이스의 구조와 같다.</li>\n<li>읽기 전용 인터페이스에는 컬렉션을 변경할 수 있는 모든 요소가 빠져있다.</li>\n</ul>\n<table>\n<thead>\n<tr>\n<th>컬렉션 타입</th>\n<th>읽기 전용 타입</th>\n<th>변경 가능 타입</th>\n</tr>\n</thead>\n<tbody>\n<tr>\n<td><code>List</code></td>\n<td><code>listOf</code>, <code>List</code></td>\n<td><code>mutableListOf</code>, <code>MutableList</code></td>\n</tr>\n<tr>\n<td><code>Set</code></td>\n<td><code>setOf</code></td>\n<td><code>mutableSetOf</code>, <code>MutableSet</code> 등</td>\n</tr>\n<tr>\n<td><code>Map</code></td>\n<td><code>mapOf</code></td>\n<td><code>mutableMapOf</code>, <code>MutableMap</code> 등</td>\n</tr>\n</tbody>\n</table>\n<deckgo-highlight-code language=\"java\" terminal=\"carbon\" theme=\"one-dark\"  >\n          <code slot=\"code\">public class CollectionUtils {\n    public static List&lt;String&gt; upperCaseAll(List&lt;String&gt; items) {\n        for (int i = 0; i &lt; items.size(); i++) {\n             items.set(i, items.get(i).toUpperCase());\n        }\n        return items;\n    }\n}</code>\n        </deckgo-highlight-code>\n<deckgo-highlight-code language=\"kotlin\" terminal=\"carbon\" theme=\"one-dark\"  >\n          <code slot=\"code\">fun printInUpperCase(list: List&lt;String&gt;) {\n    println(CollectionUtils.upperCaseAll(list))\n    println(list.first())\n}\n\nfun main() {\n    val list = listOf(&quot;a&quot;, &quot;b&quot;, &quot;c&quot;)\n    printInUpperCase(list)\n}</code>\n        </deckgo-highlight-code>\n<ul>\n<li>컬렉션을 변경하는 자바 메서드에게 읽기 전용 <code>Collection</code>을 넘겨도 코틀린 컴파일러가 이를 막을 수 없다.</li>\n<li>컬렉션을 자바 코드에게 넘길 때는 특별히 주의를 기울여야 한다.</li>\n</ul>\n<h3 id=\"-824-자바에서-선언한-컬렉션은-코틀린에서-플랫폼-타입으로-보임\" style=\"position:relative;\"><a href=\"#-824-%EC%9E%90%EB%B0%94%EC%97%90%EC%84%9C-%EC%84%A0%EC%96%B8%ED%95%9C-%EC%BB%AC%EB%A0%89%EC%85%98%EC%9D%80-%EC%BD%94%ED%8B%80%EB%A6%B0%EC%97%90%EC%84%9C-%ED%94%8C%EB%9E%AB%ED%8F%BC-%ED%83%80%EC%9E%85%EC%9C%BC%EB%A1%9C-%EB%B3%B4%EC%9E%84\" aria-label=\" 824 자바에서 선언한 컬렉션은 코틀린에서 플랫폼 타입으로 보임 permalink\" class=\"post-anchor before\"><svg aria-hidden=\"true\" focusable=\"false\" height=\"16\" version=\"1.1\" viewBox=\"0 0 16 16\" width=\"16\"><path fill-rule=\"evenodd\" d=\"M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z\"></path></svg></a>🔖 8.2.4 자바에서 선언한 컬렉션은 코틀린에서 플랫폼 타입으로 보임</h3>\n<ul>\n<li>플랫폼 타입의 경우 코틀린 쪽에는 널 관련 정보가 없다.</li>\n<li>자바 쪽에서 선언한 컬렉션 타입의 변수를 코틀린에서는 플랫폼 타입으로 본다.</li>\n<li>컬렉션 타입이 시그니처에 들어간 자바 메서드 구현을 오버라이드하려는 경우 읽기 전용 컬렉션과 변경 가능 컬렉션의 차이가 문제가 된다.\n<ul>\n<li>컬렉션이 null이 될 수 있는가?</li>\n<li>컬렉션의 원소가 null이 될 수 있는가?</li>\n<li>여러번이 작성할 메서드가 컬렉션을 변경할 수 있는가?</li>\n</ul>\n</li>\n</ul>\n<deckgo-highlight-code language=\"java\" terminal=\"carbon\" theme=\"one-dark\"  >\n          <code slot=\"code\">public interface FileContentProcessor {\n    void processContents(File path, byte[] binaryContents, List&lt;String&gt; textContents);\n}</code>\n        </deckgo-highlight-code>\n<ul>\n<li>이를 코틀린으로 구현하면 아래와 같다.</li>\n</ul>\n<deckgo-highlight-code language=\"kotlin\" terminal=\"carbon\" theme=\"one-dark\"  >\n          <code slot=\"code\">class FileIndexer : FileContentProcessor {\n    override fun processContents(path: File, binaryContents: ByteArray?, textContents: MutableList&lt;String&gt;?) {\n        TODO(&quot;Not yet implemented&quot;)\n    }\n}</code>\n        </deckgo-highlight-code>\n<deckgo-highlight-code language=\"java\" terminal=\"carbon\" theme=\"one-dark\"  >\n          <code slot=\"code\">interface DataParser&lt;T&gt; {\n    void parseData(\n            String input,\n            List&lt;T&gt; output,\n            List&lt;String&gt; errors\n    );\n}</code>\n        </deckgo-highlight-code>\n<ul>\n<li>이를 코틀린으로 구현하면 아래와 같다.</li>\n</ul>\n<deckgo-highlight-code language=\"kotlin\" terminal=\"carbon\" theme=\"one-dark\"  >\n          <code slot=\"code\">class PersonParser : DataParser&lt;Person&gt; {\n    override fun parseData(input: String, output: MutableList&lt;Person&gt;, errors: MutableList&lt;String?&gt;) {\n        TODO(&quot;Not yet implemented&quot;)\n    }\n}</code>\n        </deckgo-highlight-code>\n<ul>\n<li>java 인터페이스나 클래스가 어떤 맥락에서 사용되는지 정확히 알아야 한다.</li>\n</ul>\n<h3 id=\"-825-성능과-상호운용을-위해-객체의-배열이나-원시-타입의-배열을-만들기\" style=\"position:relative;\"><a href=\"#-825-%EC%84%B1%EB%8A%A5%EA%B3%BC-%EC%83%81%ED%98%B8%EC%9A%B4%EC%9A%A9%EC%9D%84-%EC%9C%84%ED%95%B4-%EA%B0%9D%EC%B2%B4%EC%9D%98-%EB%B0%B0%EC%97%B4%EC%9D%B4%EB%82%98-%EC%9B%90%EC%8B%9C-%ED%83%80%EC%9E%85%EC%9D%98-%EB%B0%B0%EC%97%B4%EC%9D%84-%EB%A7%8C%EB%93%A4%EA%B8%B0\" aria-label=\" 825 성능과 상호운용을 위해 객체의 배열이나 원시 타입의 배열을 만들기 permalink\" class=\"post-anchor before\"><svg aria-hidden=\"true\" focusable=\"false\" height=\"16\" version=\"1.1\" viewBox=\"0 0 16 16\" width=\"16\"><path fill-rule=\"evenodd\" d=\"M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z\"></path></svg></a>🔖 8.2.5 성능과 상호운용을 위해 객체의 배열이나 원시 타입의 배열을 만들기</h3>\n<deckgo-highlight-code language=\"kotlin\" terminal=\"carbon\" theme=\"one-dark\"  >\n          <code slot=\"code\">fun main(args: Array&lt;String&gt;) {\n    for (i in args.indices) {\n        println(&quot;Argument $i is: ${args[i]}&quot;)\n    }\n}</code>\n        </deckgo-highlight-code>\n<ul>\n<li>코틀린 배열은 타입 파라미터를 받는 클래스다.</li>\n</ul>\n<deckgo-highlight-code language=\"kotlin\" terminal=\"carbon\" theme=\"one-dark\"  >\n          <code slot=\"code\">fun main() {\n    val letters = Array&lt;String&gt;(26) { i -&gt; (&#39;a&#39; + i).toString() }\n    println(letters.joinToString(&quot;&quot;))\n}</code>\n        </deckgo-highlight-code>\n<ul>\n<li>타입인자를 생략해도 컴파일러가 알아서 원소 타입을 추론해준다.</li>\n</ul>\n<deckgo-highlight-code language=\"kotlin\" terminal=\"carbon\" theme=\"one-dark\"  >\n          <code slot=\"code\">fun main() {\n    val letters = Array(26) { i -&gt; (&#39;a&#39; + i).toString() }\n    println(letters.joinToString(&quot;&quot;))\n}</code>\n        </deckgo-highlight-code>\n<ul>\n<li>데이터가 이미 컬렉션에 들어 있다면 컬렉션을 배열로 변환해야 한다.</li>\n</ul>\n<deckgo-highlight-code language=\"kotlin\" terminal=\"carbon\" theme=\"one-dark\"  >\n          <code slot=\"code\">fun main() {\n    val strings = listOf(&quot;a&quot;, &quot;b&quot;, &quot;c&quot;)\n    println(&quot;%s/%s/%s&quot;.format(*strings.toTypedArray())) // 스프레드 연산자 사용\n}</code>\n        </deckgo-highlight-code>\n<ul>\n<li>코틀린은 원시 타입의 배열을 표현하는 별도 클래스를 각 원시 타입마다 하나씩 제공한다.</li>\n</ul>\n<deckgo-highlight-code language=\"kotlin\" terminal=\"carbon\" theme=\"one-dark\"  >\n          <code slot=\"code\">val fiveZeros = IntArray(5)\nval fiveZerosToo = intArrayOf(0, 0, 0, 0, 0)\n\nfun main() {\n    val squares = IntArray(5) { i -&gt; (i + 1) * (i + 1) }\n    println(squares.joinToString())\n}</code>\n        </deckgo-highlight-code>\n<ul>\n<li>배열을 만드는 방법은 다양하다.</li>\n</ul>","excerpt":"📖 8.1 원시 타입과 기본 타입 🔖 8.1.1 정수, 부동소수점 수, 문자, 불리언 값을 원시 타입으로 표현 코틀린은 원시 타입과 래퍼 타입을 구분하지 않는다. 매번 객체로 표현하는 것이 아니라 실행 시점에 숫자 타입은 가능한 한 가장 효율적인 방식으로 표현된다. 🔖 8.1.2 양수를 표현하기 위해 모든 비트 범위 사용: 부호 없는 숫자 타입 타입 비트 크기 표현 범위 UByte 8비트 0 ~ 255 UShort 16비트 0 ~ 65,535 UInt 32비트 0 ~ 4,294,967,29…","fields":{"slug":"/backend/kotlin-in-action/8장-기본_타입_컬렉션_배열/"},"frontmatter":{"title":"Kotlin in Action - 8장 기본 타입, 컬렉션, 배열","thumbnail":{"childImageSharp":{"fluid":{"src":"/static/f7f7ddfa31d1614405dc5af1487b9ec4/9c94d/kotlin-in-action.png"}}},"draft":false,"category":"Back-End","tags":["Kotlin"],"date":"April 20, 2025"}}}},"staticQueryHashes":["2374173507","2996537568","3691437124"]}