{"componentChunkName":"component---src-containers-post-index-tsx","path":"/backend/kotlin-in-action/11장-제네릭스/","result":{"pageContext":{"next":{"id":"19b26918-c9bd-5dc5-9a91-76021099d3ff","html":"<h2 id=\"-101-다른-함수를-인자로-받거나-반환하는-함수-정의-고차-함수\" style=\"position:relative;\"><a href=\"#-101-%EB%8B%A4%EB%A5%B8-%ED%95%A8%EC%88%98%EB%A5%BC-%EC%9D%B8%EC%9E%90%EB%A1%9C-%EB%B0%9B%EA%B1%B0%EB%82%98-%EB%B0%98%ED%99%98%ED%95%98%EB%8A%94-%ED%95%A8%EC%88%98-%EC%A0%95%EC%9D%98-%EA%B3%A0%EC%B0%A8-%ED%95%A8%EC%88%98\" aria-label=\" 101 다른 함수를 인자로 받거나 반환하는 함수 정의 고차 함수 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>📖 10.1 다른 함수를 인자로 받거나 반환하는 함수 정의: 고차 함수</h2>\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\">list.filter { it &gt; 0}</code>\n        </deckgo-highlight-code>\n<ul>\n<li>표준 라이브러리 함수인 filter는 술어 함수를 인자로 받으므로 고차 함수</li>\n</ul>\n<h3 id=\"-1011-함수-타입은-람다의-파라미터-타입과-반환-타입을-지정한다\" style=\"position:relative;\"><a href=\"#-1011-%ED%95%A8%EC%88%98-%ED%83%80%EC%9E%85%EC%9D%80-%EB%9E%8C%EB%8B%A4%EC%9D%98-%ED%8C%8C%EB%9D%BC%EB%AF%B8%ED%84%B0-%ED%83%80%EC%9E%85%EA%B3%BC-%EB%B0%98%ED%99%98-%ED%83%80%EC%9E%85%EC%9D%84-%EC%A7%80%EC%A0%95%ED%95%9C%EB%8B%A4\" aria-label=\" 1011 함수 타입은 람다의 파라미터 타입과 반환 타입을 지정한다 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>🔖 10.1.1 함수 타입은 람다의 파라미터 타입과 반환 타입을 지정한다</h3>\n<deckgo-highlight-code language=\"kotlin\" terminal=\"carbon\" theme=\"one-dark\"  >\n          <code slot=\"code\">val sum = { x: Int, y: Int -&gt; x + y }\nval action = { println(42) }</code>\n        </deckgo-highlight-code>\n<ul>\n<li>컴파일러는 sum과 action이 함수 타입임을 추론</li>\n</ul>\n<deckgo-highlight-code language=\"kotlin\" terminal=\"carbon\" theme=\"one-dark\"  >\n          <code slot=\"code\">val sum: (Int, Int) -&gt; Int = { x, y -&gt; x + y } // Int 파라미터를 2개 받아서 Int 값을 반환하는 함수\nval action: () -&gt; Unit = { println(42) } // 아무 인자도 받지 않고 아무 값도 반환하지 않는 함수</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\">var canReturnNu11: (Int, Int) -&gt; Int? = { x, y -&gt; null }</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\">var funOrNull: ((Int, Int) -&gt; Int)? = null</code>\n        </deckgo-highlight-code>\n<ul>\n<li>함수 전체가 널이 될 수 있는 타입임을 선언하기 위해 함수 타입을 괄호로 감싸고 그 뒤에 물음표를 붙여야만 한다.</li>\n</ul>\n<h3 id=\"-1012-인자로-전달-받은-함수-호출\" style=\"position:relative;\"><a href=\"#-1012-%EC%9D%B8%EC%9E%90%EB%A1%9C-%EC%A0%84%EB%8B%AC-%EB%B0%9B%EC%9D%80-%ED%95%A8%EC%88%98-%ED%98%B8%EC%B6%9C\" aria-label=\" 1012 인자로 전달 받은 함수 호출 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>🔖 10.1.2 인자로 전달 받은 함수 호출</h3>\n<deckgo-highlight-code language=\"kotlin\" terminal=\"carbon\" theme=\"one-dark\"  >\n          <code slot=\"code\">fun twoAndThree(operation: (Int, Int) -&gt; Int) {\n    val result = operation(2, 3)\n    println(&quot;The result is $result&quot;)\n}\n\nfun main() {\n    twoAndThree { a, b -&gt; a + b }\n    // The result is 5\n    twoAndThree { a, b -&gt; a * b }\n    // The result is 6\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\">fun String.filter(predicate: (Char) -&gt; Boolean): String {\n    return buildString {\n        for (char in this@filter) {\n            if (predicate(char)) append(char)\n        }\n    }\n}</code>\n        </deckgo-highlight-code>\n<ul>\n<li>filter 함수는 술어를 파라미터로 받는다.</li>\n<li>확장 함수와 buildString 함수 모두 수신 객체를 정의하기 때문에 this 앞에 레이블읇 붙여 buildString 람다의 수신 객체가 아니라 filter의 바깥쪽 수신 객체에 접근해야 한다.</li>\n</ul>\n<h3 id=\"-1013-자바에서-코틀린-함수-타입-사용\" style=\"position:relative;\"><a href=\"#-1013-%EC%9E%90%EB%B0%94%EC%97%90%EC%84%9C-%EC%BD%94%ED%8B%80%EB%A6%B0-%ED%95%A8%EC%88%98-%ED%83%80%EC%9E%85-%EC%82%AC%EC%9A%A9\" aria-label=\" 1013 자바에서 코틀린 함수 타입 사용 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>🔖 10.1.3 자바에서 코틀린 함수 타입 사용</h3>\n<deckgo-highlight-code language=\"kotlin\" terminal=\"carbon\" theme=\"one-dark\"  >\n          <code slot=\"code\">fun processTheAnswer (f: (Int) -&gt; Int) {\n    printin(f(42))\n}</code>\n        </deckgo-highlight-code>\n<deckgo-highlight-code language=\"java\" terminal=\"carbon\" theme=\"one-dark\"  >\n          <code slot=\"code\">processTheAnswer(number -&gt; number + 1);</code>\n        </deckgo-highlight-code>\n<ul>\n<li>자동 SAM 변환을 통해 코틀린 람다를 함수형 인터페이스를 요구하는 자바 메서드에게 넘길 수 있다. 즉, 자바에서 정의된 고차 함수를 아무 문제 없이 사용할 수 있다는 의미다.</li>\n<li>자바에서 람다를 인자로 받는 코틀린 확장함수를 사용할 때는 첫 번째 인자로 수신 객체를 명시적으로 전달해야 한다.</li>\n</ul>\n<h3 id=\"-1014-함수-타입의-파라미터에-대해-기본값을-지정할-수-있고-널이-될-수도-있다\" style=\"position:relative;\"><a href=\"#-1014-%ED%95%A8%EC%88%98-%ED%83%80%EC%9E%85%EC%9D%98-%ED%8C%8C%EB%9D%BC%EB%AF%B8%ED%84%B0%EC%97%90-%EB%8C%80%ED%95%B4-%EA%B8%B0%EB%B3%B8%EA%B0%92%EC%9D%84-%EC%A7%80%EC%A0%95%ED%95%A0-%EC%88%98-%EC%9E%88%EA%B3%A0-%EB%84%90%EC%9D%B4-%EB%90%A0-%EC%88%98%EB%8F%84-%EC%9E%88%EB%8B%A4\" aria-label=\" 1014 함수 타입의 파라미터에 대해 기본값을 지정할 수 있고 널이 될 수도 있다 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>🔖 10.1.4 함수 타입의 파라미터에 대해 기본값을 지정할 수 있고, 널이 될 수도 있다.</h3>\n<deckgo-highlight-code language=\"kotlin\" terminal=\"carbon\" theme=\"one-dark\"  >\n          <code slot=\"code\">fun &lt;T&gt; Collection&lt;T&gt;.joinToString(separator: String = &quot;, &quot;, prefix: String = &quot;&quot;, postfix: String = &quot;&quot;): String {\n    val result = StringBuilder(prefix)\n    \n    for ((index, element) in this.withIndex()) {\n        if (index &gt; 0) result.append(separator)\n        result.append(element)\n    }\n    result.append(postfix)\n    return result.toString()\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 &lt;T&gt; Collection&lt;T&gt;.joinToString(separator: String = &quot;, &quot;, prefix: String = &quot;&quot;, postfix: String = &quot;&quot;, transform: (T) -&gt; String = { it.toString() }): String {\n    val result = StringBuilder(prefix)\n\n    for ((index, element) in this.withIndex()) {\n        if (index &gt; 0) result.append(separator)\n        result.append(transform(element))\n    }\n    result.append(postfix)\n    return result.toString()\n}</code>\n        </deckgo-highlight-code>\n<ul>\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\">fun &lt;T&gt; Collection&lt;T&gt;.joinToString(separator: String = &quot;, &quot;, prefix: String = &quot;&quot;, postfix: String = &quot;&quot;, transform: ((T) -&gt; String)? = null): String {\n    val result = StringBuilder(prefix)\n\n    for ((index, element) in this.withIndex()) {\n        if (index &gt; 0) result.append(separator)\n        val str = transform?.invoke(element) ?: element.toString()\n        result.append(str)\n    }\n    result.append(postfix)\n    return result.toString()\n}</code>\n        </deckgo-highlight-code>\n<ul>\n<li>transform은 넘이 될 수 있는 함수 타입이지만 그 반환 타입은 널이 될 수 없는 타입이다.</li>\n<li>transform은 자신이 null이 아니면 String 타입의 널이 아닌 값을 반환한다는 사실을 보장한다.</li>\n</ul>\n<h3 id=\"-1015-함수를-함수에서-반환\" style=\"position:relative;\"><a href=\"#-1015-%ED%95%A8%EC%88%98%EB%A5%BC-%ED%95%A8%EC%88%98%EC%97%90%EC%84%9C-%EB%B0%98%ED%99%98\" aria-label=\" 1015 함수를 함수에서 반환 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>🔖 10.1.5 함수를 함수에서 반환</h3>\n<deckgo-highlight-code language=\"kotlin\" terminal=\"carbon\" theme=\"one-dark\"  >\n          <code slot=\"code\">fun getShippingCostCalculator(delivery: Delivery): (Order) -&gt; Double {\n    if (delivery == Delivery.EXPEDITED) {\n        return { order -&gt; 6 + 2.1 * order.itemCount }\n    }\n    return { order -&gt; 1.2 * order.itemCount }\n}\n\nfun main() {\n    val calculator = getShippingCostCalculator(Delivery.EXPEDITED)\n    println(&quot;Shipping costs ${calculator(Order(3))}&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\">class ContactListFilters {\n    var prefix: String = &quot;&quot;\n    var onlyWithPhoneNumber: Boolean = false\n\n    fun getPredicate(): (Person) -&gt; Boolean {\n        val startWithPrefix = { p: Person -&gt; p.firstName.startsWith(prefix) || p.lastName.startsWith(prefix)}\n        if (!onlyWithPhoneNumber) {\n            return startWithPrefix\n        }\n        return { startWithPrefix(it) &amp;&amp; it.phoneNumber != null }\n    }\n}</code>\n        </deckgo-highlight-code>\n<ul>\n<li>getPredicate method는 filter 함수에 인자로 넘길 수 있는 함수를 반환한다.</li>\n<li>고차 함수는 코드 구조를 개선하고 중복을 없앨 때 쓸 수 있는 아주 강력한 도구다.</li>\n</ul>\n<h3 id=\"-1016-람다를-활용해-중복을-줄여-코드-재사용성-높이기\" style=\"position:relative;\"><a href=\"#-1016-%EB%9E%8C%EB%8B%A4%EB%A5%BC-%ED%99%9C%EC%9A%A9%ED%95%B4-%EC%A4%91%EB%B3%B5%EC%9D%84-%EC%A4%84%EC%97%AC-%EC%BD%94%EB%93%9C-%EC%9E%AC%EC%82%AC%EC%9A%A9%EC%84%B1-%EB%86%92%EC%9D%B4%EA%B8%B0\" aria-label=\" 1016 람다를 활용해 중복을 줄여 코드 재사용성 높이기 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>🔖 10.1.6 람다를 활용해 중복을 줄여 코드 재사용성 높이기</h3>\n<deckgo-highlight-code language=\"kotlin\" terminal=\"carbon\" theme=\"one-dark\"  >\n          <code slot=\"code\">data class SiteVisit(\n    val path: String,\n    val duration: Double,\n    val os: OS\n)\n\nenum class OS { WINDOWS, LINUX, MAC, IOS, ANDROID }\n\nval log = listOf(\n    SiteVisit(&quot;/&quot;, 34.0, OS.WINDOWS),\n    SiteVisit(&quot;/&quot;, 22.0, OS.MAC),\n    SiteVisit(&quot;/login&quot;, 12.0, OS.WINDOWS),\n    SiteVisit(&quot;/signup&quot;, 8.0, OS.IOS),\n    SiteVisit(&quot;/&quot;, 16.3, OS.ANDROID),\n)\n\nval averageWindowsDuration = log\n    .filter { it.os == OS.WINDOWS }\n    .map(SiteVisit::duration)\n    .average()</code>\n        </deckgo-highlight-code>\n<ul>\n<li>윈도우 사용자의 평균 방문 시간 출력은 average 함수로 쉽게 표현할 수 있다.</li>\n</ul>\n<deckgo-highlight-code language=\"kotlin\" terminal=\"carbon\" theme=\"one-dark\"  >\n          <code slot=\"code\">fun List&lt;SiteVisit&gt;.averageDurationFor(os: OS) = filter { it.os == os }.map(SiteVisit::duration).average()</code>\n        </deckgo-highlight-code>\n<ul>\n<li>중복을 피하기 위해 OS를 파라미터로 뽑아낼 수 있다.</li>\n</ul>\n<deckgo-highlight-code language=\"kotlin\" terminal=\"carbon\" theme=\"one-dark\"  >\n          <code slot=\"code\">fun List&lt;SiteVisit&gt;.averageDurationFor(predicate: (SiteVisit) -&gt; Boolean) = filter(SiteVisit).map(SiteVisit::duration).average()</code>\n        </deckgo-highlight-code>\n<ul>\n<li>코드 중복을 줄일 때 함수 타입이 상당히 도움이 된다.</li>\n</ul>\n<h2 id=\"-102-인라인-함수를-사용해-람다의-부가-비용-없애기\" style=\"position:relative;\"><a href=\"#-102-%EC%9D%B8%EB%9D%BC%EC%9D%B8-%ED%95%A8%EC%88%98%EB%A5%BC-%EC%82%AC%EC%9A%A9%ED%95%B4-%EB%9E%8C%EB%8B%A4%EC%9D%98-%EB%B6%80%EA%B0%80-%EB%B9%84%EC%9A%A9-%EC%97%86%EC%95%A0%EA%B8%B0\" aria-label=\" 102 인라인 함수를 사용해 람다의 부가 비용 없애기 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>📖 10.2 인라인 함수를 사용해 람다의 부가 비용 없애기</h2>\n<ul>\n<li>inline 변경자를 어떤 함수에 붙이면 컴파일러는 그 함수가 쓰이는 위치에 함수 호출을 생성하는 대신에 함수를 구현하는 코드로 바꿔치기 해준다.</li>\n</ul>\n<h3 id=\"-1021-인라이닝이-작동하는-방식\" style=\"position:relative;\"><a href=\"#-1021-%EC%9D%B8%EB%9D%BC%EC%9D%B4%EB%8B%9D%EC%9D%B4-%EC%9E%91%EB%8F%99%ED%95%98%EB%8A%94-%EB%B0%A9%EC%8B%9D\" aria-label=\" 1021 인라이닝이 작동하는 방식 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>🔖 10.2.1 인라이닝이 작동하는 방식</h3>\n<deckgo-highlight-code language=\"kotlin\" terminal=\"carbon\" theme=\"one-dark\"  >\n          <code slot=\"code\">inline fun &lt;T&gt; synchronized(lock: Lock, action: () -&gt; T): T {\n    lock.lock()\n    try {\n        return action()\n    }\n    finally {\n        lock.unlock()\n    }\n}\n\nfun main() {\n    val l = ReentrantLock()\n    synchronized(1) {\n        // ...\n    }\n}</code>\n        </deckgo-highlight-code>\n<ul>\n<li>어떤 함수를 inline으로 선언하면 그 함수의 본문이 인라인된다.\n<ul>\n<li>함수를 호출하는 코드를 함수를 호출하는 바이트코드 대신에 함수 본문을 번역한 바이트코드로 컴파일한다는 뜻</li>\n</ul>\n</li>\n<li>synchronized 함수를 inline으로 선언했으므로 synchronized를 호출하는 코드는 모두 자바의 synchronized 문과 같아진다.</li>\n</ul>\n<deckgo-highlight-code language=\"kotlin\" terminal=\"carbon\" theme=\"one-dark\"  >\n          <code slot=\"code\">class LockOwner(val lock: Lock) {\n    fun runUnderLock(body: () -&gt; Unit) {\n        synchronized(lock, body)\n    } \n}</code>\n        </deckgo-highlight-code>\n<ul>\n<li>인라인 함수를 호출하면서 람다를 넘기는 대신에 함수 타입의 변수를 넘길 수도 있다.</li>\n<li>함수에 더해 프로퍼티 접근자에도 inline을 붙일 수 있다.</li>\n</ul>\n<h3 id=\"-1022-인라인-함수의-제약\" style=\"position:relative;\"><a href=\"#-1022-%EC%9D%B8%EB%9D%BC%EC%9D%B8-%ED%95%A8%EC%88%98%EC%9D%98-%EC%A0%9C%EC%95%BD\" aria-label=\" 1022 인라인 함수의 제약 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>🔖 10.2.2 인라인 함수의 제약</h3>\n<deckgo-highlight-code language=\"kotlin\" terminal=\"carbon\" theme=\"one-dark\"  >\n          <code slot=\"code\">class FunctionStorage {\n    var myStoredFunction: ((Int) -&gt; Unit)? = null\n    inline fun storeFunction(f: (Int) -&gt; Unit) { \n        myStoredFunction = f // error\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 &lt;T, R&gt; Sequence&lt;T&gt;.map(transform: (T) -&gt; R): Sequence&lt;R&gt; {\n    return TransformingSequence(this, transform)\n}</code>\n        </deckgo-highlight-code>\n<ul>\n<li>Sequence.map 의 정의다.</li>\n<li>transform 파라미터로 전달받은 함수 값을 호출하지 않는 대신, TransformingSequence라는 클래스의 생성자에게 그 함수 값을 넘긴다.</li>\n</ul>\n<deckgo-highlight-code language=\"kotlin\" terminal=\"carbon\" theme=\"one-dark\"  >\n          <code slot=\"code\">inline fun foo(inlined: () -&gt; Unit, noinline notInlined: () -&gt; Unit) {\n    //..\n}</code>\n        </deckgo-highlight-code>\n<ul>\n<li>인라이닝하면 안 되는 람다를 파라미터로 받는다면 noinline 변경자를 파라미터 이름 앞에 붙여 인라이닝을 금지할 수 있다.</li>\n</ul>\n<h3 id=\"-1023-컬렉션-연산-인라이닝\" style=\"position:relative;\"><a href=\"#-1023-%EC%BB%AC%EB%A0%89%EC%85%98-%EC%97%B0%EC%82%B0-%EC%9D%B8%EB%9D%BC%EC%9D%B4%EB%8B%9D\" aria-label=\" 1023 컬렉션 연산 인라이닝 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>🔖 10.2.3 컬렉션 연산 인라이닝</h3>\n<deckgo-highlight-code language=\"kotlin\" terminal=\"carbon\" theme=\"one-dark\"  >\n          <code slot=\"code\">data class Person(val name: String, val age: Int)\n\nval people = listOf(Person(&quot;Alice&quot;, 29), Person(&quot;Bob&quot;, 31))\n\nfun main() {\n    println(people.filter { it.age &lt; 30 })\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 result = mutableListOf&lt;Person&gt;()\n    for (person in people) {\n        if (person.age &lt; 30) {\n            result.add(person)\n        }\n    }\n    println(result)\n}</code>\n        </deckgo-highlight-code>\n<ul>\n<li>코틀린에서 filter는 인라인 함수다.</li>\n<li>즉, filter를 써서 생긴 바이트코드와 위 예제는 거의 같다.</li>\n</ul>\n<deckgo-highlight-code language=\"kotlin\" terminal=\"carbon\" theme=\"one-dark\"  >\n          <code slot=\"code\">fun main() {\n    println(\n        people.filter { it.age &lt; 30 }\n            .map(Person::name)\n    )\n}</code>\n        </deckgo-highlight-code>\n<ul>\n<li>map도 인라인 함수다.</li>\n<li>시퀀스에 사용된 람다는 인라이닝되지 않는다. 따라서 지연 계산을 통해 성능을 향상시키려는 이유로 모든 컬렉션 연산에 asSequence를 붙이려고 해서는 안 된다.</li>\n<li>시퀀스를 통해 성능을 향상시킬 수 있는 경우는 컬렉션 크기가 큰 경우뿐이다.</li>\n</ul>\n<h3 id=\"-1024-언제-함수를-인라인으로-선언할지-결정\" style=\"position:relative;\"><a href=\"#-1024-%EC%96%B8%EC%A0%9C-%ED%95%A8%EC%88%98%EB%A5%BC-%EC%9D%B8%EB%9D%BC%EC%9D%B8%EC%9C%BC%EB%A1%9C-%EC%84%A0%EC%96%B8%ED%95%A0%EC%A7%80-%EA%B2%B0%EC%A0%95\" aria-label=\" 1024 언제 함수를 인라인으로 선언할지 결정 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>🔖 10.2.4 언제 함수를 인라인으로 선언할지 결정</h3>\n<ul>\n<li>inline 키워드를 사용해도 람다를 인자로 받는 함수만 성능이 좋아질 가능성이 높다.</li>\n<li>일반 함수 호출의 경우 JVM은 이미 강력하게 인라이닝을 지원한다.\n<ul>\n<li>바이트코드를 실제 기계어 코드로 번역하는 과정(JIT)에서 일어난다.</li>\n</ul>\n</li>\n<li>바이트코드에서는 각 함수 구현이 정확히 한 번만 있으면 되고 그 함수를 호출하는 모든 부분에 따로 코드를 중복할 필요가 없다. 반면, 코틀린 인라인 함수는 코드 중복이 생긴다.</li>\n<li>람다를 인자로 받는 함수를 인라이닝하면 이익이 더 많다.\n<ul>\n<li>함수 호출 비용 감소</li>\n<li>람다를 표현하는 클래스와 람다 인스턴스에 해당하는 객체를 만들 필요도 없어진다.</li>\n<li>JVM은 함수 호출과 람다를 인라이닝해줄 정도로 똑똑하지 못하다.</li>\n<li>추가 기능을 사용할 수 있다.</li>\n</ul>\n</li>\n<li>inline 변경자를 함수에 붙일 때는 코드 크기에 주의를 기울여야 한다.</li>\n</ul>\n<h3 id=\"-1025-withlock-use-uselines로-자원-관리를-위해-인라인된-람다-사용\" style=\"position:relative;\"><a href=\"#-1025-withlock-use-uselines%EB%A1%9C-%EC%9E%90%EC%9B%90-%EA%B4%80%EB%A6%AC%EB%A5%BC-%EC%9C%84%ED%95%B4-%EC%9D%B8%EB%9D%BC%EC%9D%B8%EB%90%9C-%EB%9E%8C%EB%8B%A4-%EC%82%AC%EC%9A%A9\" aria-label=\" 1025 withlock use uselines로 자원 관리를 위해 인라인된 람다 사용 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>🔖 10.2.5 withLock, use, useLines로 자원 관리를 위해 인라인된 람다 사용</h3>\n<deckgo-highlight-code language=\"kotlin\" terminal=\"carbon\" theme=\"one-dark\"  >\n          <code slot=\"code\">val l: Lock = ReentrantLock()\nl.withLock {\n    //..\n}</code>\n        </deckgo-highlight-code>\n<ul>\n<li>withLock은 락을 획득한 후 작업하는 숙어를 별도의 함수로 분리</li>\n</ul>\n<deckgo-highlight-code language=\"kotlin\" terminal=\"carbon\" theme=\"one-dark\"  >\n          <code slot=\"code\">fun readFirstLineFromFile(fileName: String): String {\n    BufferedReader(FileReader(fileName)).use { br -&gt;\n        return br.readLine()\n    }\n}</code>\n        </deckgo-highlight-code>\n<ul>\n<li>use 함수는 닫을 수 있는 자원에 대해 호출하는 확장 함수다.\n<ul>\n<li>람다를 호출하고 사용 후 자원이 확실히 닫히게 한다.</li>\n</ul>\n</li>\n<li>use 함수는 인라인 함수이며, 성능에는 영향이 없다.</li>\n</ul>\n<deckgo-highlight-code language=\"kotlin\" terminal=\"carbon\" theme=\"one-dark\"  >\n          <code slot=\"code\">fun readFirstLineFromFile(fileName: String): String {\n    Path(fileName).useLines { \n        return it.first()\n    }\n}</code>\n        </deckgo-highlight-code>\n<ul>\n<li>use는 Closeable과 함께 쓰이지만 useLines는 File과 Path 객체에 정의돼 있고, 람다가 문자열 시퀀스에 접근하게 해준다.</li>\n</ul>\n<h2 id=\"-103-람다에서-반환-고차-함수에서-흐름-제어\" style=\"position:relative;\"><a href=\"#-103-%EB%9E%8C%EB%8B%A4%EC%97%90%EC%84%9C-%EB%B0%98%ED%99%98-%EA%B3%A0%EC%B0%A8-%ED%95%A8%EC%88%98%EC%97%90%EC%84%9C-%ED%9D%90%EB%A6%84-%EC%A0%9C%EC%96%B4\" aria-label=\" 103 람다에서 반환 고차 함수에서 흐름 제어 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>📖 10.3 람다에서 반환: 고차 함수에서 흐름 제어</h2>\n<h3 id=\"-1031-람다-안의-return-문-람다를-둘러싼-함수에서-반환\" style=\"position:relative;\"><a href=\"#-1031-%EB%9E%8C%EB%8B%A4-%EC%95%88%EC%9D%98-return-%EB%AC%B8-%EB%9E%8C%EB%8B%A4%EB%A5%BC-%EB%91%98%EB%9F%AC%EC%8B%BC-%ED%95%A8%EC%88%98%EC%97%90%EC%84%9C-%EB%B0%98%ED%99%98\" aria-label=\" 1031 람다 안의 return 문 람다를 둘러싼 함수에서 반환 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>🔖 10.3.1 람다 안의 return 문: 람다를 둘러싼 함수에서 반환</h3>\n<deckgo-highlight-code language=\"kotlin\" terminal=\"carbon\" theme=\"one-dark\"  >\n          <code slot=\"code\">data class Person(val name: String, val age: Int)\n\nval people = listOf(Person(&quot;Alice&quot;, 29), Person(&quot;Bob&quot;, 31))\n\nfun lookForAlice(people: List&lt;Person&gt;) {\n    for (person in people) {\n        if (person.name == &quot;Alice&quot;) {\n            println(&quot;Found!&quot;)\n            return\n        }\n    }\n    println(&quot;Alice is not found&quot;)\n}</code>\n        </deckgo-highlight-code>\n<deckgo-highlight-code language=\"kotlin\" terminal=\"carbon\" theme=\"one-dark\"  >\n          <code slot=\"code\">fun lookForAlice(people: List&lt;Person&gt;) {\n    people.forEach {\n        if (it.name == &quot;Alice&quot;) {\n            println(&quot;Found!&quot;)\n            return\n        }\n    }\n    println(&quot;Alice is not found&quot;)\n}</code>\n        </deckgo-highlight-code>\n<ul>\n<li>위 두 예제는 같은 의미다.</li>\n<li>람다 안에서 return을 사용하면 람다에서만 반환되는 것이 아니라 그 람다를 호출하는 함수가 실행을 끝내고 반환된다.</li>\n<li>비로컬 return: 자신을 둘러쌓고 있는 블록보다 더 바깥에 있는 다른 블록을 반환하게 만드는 return 문</li>\n<li>return이 바깥쪽 함수를 반환시킬 수 있는 때는 람다를 인자로 받는 함수가 인라인 함수인 경우다.\n<ul>\n<li>인라이닝되지 않는 함수에 전달되는 람다 안에서 return을 사용할 수는 없다.</li>\n</ul>\n</li>\n</ul>\n<h3 id=\"-1032-람다로부터-반환-레이블을-사용한-return\" style=\"position:relative;\"><a href=\"#-1032-%EB%9E%8C%EB%8B%A4%EB%A1%9C%EB%B6%80%ED%84%B0-%EB%B0%98%ED%99%98-%EB%A0%88%EC%9D%B4%EB%B8%94%EC%9D%84-%EC%82%AC%EC%9A%A9%ED%95%9C-return\" aria-label=\" 1032 람다로부터 반환 레이블을 사용한 return 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>🔖 10.3.2 람다로부터 반환: 레이블을 사용한 return</h3>\n<ul>\n<li>로컬 return: 람다의 실행을 끝내고 람다를 호출했던 코드의 실행을 계속 이어간다.</li>\n<li>로컬 return과 비로컬 return을 구분하기 위해 레이블을 사용한다.</li>\n</ul>\n<deckgo-highlight-code language=\"kotlin\" terminal=\"carbon\" theme=\"one-dark\"  >\n          <code slot=\"code\">fun lookForAlice(people: List&lt;Person&gt;) {\n    people.forEach label@{\n        if (it.name != &quot;Alice&quot;) return@label\n        println(&quot;Found Alice&quot;)\n    }\n}</code>\n        </deckgo-highlight-code>\n<ul>\n<li>return으로 실행을 끝내고 싶은 람다식 앞에 레이블을 붙인다.</li>\n</ul>\n<deckgo-highlight-code language=\"kotlin\" terminal=\"carbon\" theme=\"one-dark\"  >\n          <code slot=\"code\">fun lookForAlice(people: List&lt;Person&gt;) {\n    people.forEach {\n        if (it.name != &quot;Alice&quot;) return@forEach\n        println(&quot;Found Alice!&quot;)\n    }\n}</code>\n        </deckgo-highlight-code>\n<ul>\n<li>람다식의 레이블을 명시하면 함수 이름을 레이블로 사용할 수 없다는 점에 유의</li>\n</ul>\n<h3 id=\"-1033-익명-함수-기본적으로-로컬-return\" style=\"position:relative;\"><a href=\"#-1033-%EC%9D%B5%EB%AA%85-%ED%95%A8%EC%88%98-%EA%B8%B0%EB%B3%B8%EC%A0%81%EC%9C%BC%EB%A1%9C-%EB%A1%9C%EC%BB%AC-return\" aria-label=\" 1033 익명 함수 기본적으로 로컬 return 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>🔖 10.3.3 익명 함수: 기본적으로 로컬 return</h3>\n<deckgo-highlight-code language=\"kotlin\" terminal=\"carbon\" theme=\"one-dark\"  >\n          <code slot=\"code\">fun lookForAlice(people: List&lt;Person&gt;) {\n    people.forEach (fun (person) {\n        if (person.name == &quot;Alice&quot;) return\n        println(&quot;${person.name} is not Alice&quot;)\n    })\n}</code>\n        </deckgo-highlight-code>\n<ul>\n<li>return은 가장 가까운 함수를 가리킨다.</li>\n<li>익명 함수 안에서 레이블이 붙지 않은 return 식은 익명 함수 자체를 반환시킬 뿐 익명 함수를 둘러싼 다른 함수를 반환시키지 않는다.</li>\n<li>익명 함수는 일반 함수와 비슷해 보이지만 실제로는 람다식에 대한 문법적 편의일 뿐이다.</li>\n</ul>","excerpt":"📖 10.1 다른 함수를 인자로 받거나 반환하는 함수 정의: 고차 함수 고차 할수는 다른 할수를 인자로 받거나 함수를 반환하는 합수 코물린에서는 다나 할수 참조를 사용해 함수를 값으로 표현할 수 있다. 표준 라이브러리 함수인 filter는 술어 함수를 인자로 받으므로 고차 함수 🔖 10.1.1 함수 타입은 람다의 파라미터 타입과 반환 타입을 지정한다 컴파일러는 sum과 action…","fields":{"slug":"/backend/kotlin-in-action/10장-고차함수-람다를_파라미터와_반환값으로_사용/"},"frontmatter":{"title":"Kotlin in Action - 10장 고차 함수: 람다를 파라미터와 반환값으로 사용","thumbnail":{"childImageSharp":{"fluid":{"src":"/static/f7f7ddfa31d1614405dc5af1487b9ec4/9c94d/kotlin-in-action.png"}}},"draft":false,"category":"Back-End","tags":["Kotlin"],"date":"May 11, 2025"}},"previous":{"id":"9b2d33a9-b5b2-5ac0-8299-1e8a5e308b54","html":"<h2 id=\"-121-어노테이션-선언과-적용\" style=\"position:relative;\"><a href=\"#-121-%EC%96%B4%EB%85%B8%ED%85%8C%EC%9D%B4%EC%85%98-%EC%84%A0%EC%96%B8%EA%B3%BC-%EC%A0%81%EC%9A%A9\" aria-label=\" 121 어노테이션 선언과 적용 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>📖 12.1 어노테이션 선언과 적용</h2>\n<h3 id=\"-1211-어노테이션을-적용해-선언해-표지-남기기\" style=\"position:relative;\"><a href=\"#-1211-%EC%96%B4%EB%85%B8%ED%85%8C%EC%9D%B4%EC%85%98%EC%9D%84-%EC%A0%81%EC%9A%A9%ED%95%B4-%EC%84%A0%EC%96%B8%ED%95%B4-%ED%91%9C%EC%A7%80-%EB%82%A8%EA%B8%B0%EA%B8%B0\" aria-label=\" 1211 어노테이션을 적용해 선언해 표지 남기기 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>🔖 12.1.1 어노테이션을 적용해 선언해 표지 남기기</h3>\n<ul>\n<li>코틀린에서 어노테이션을 적용하려면 @와 어노테이션 이름을 선언 앞에 넣으면 된다.</li>\n</ul>\n<deckgo-highlight-code language=\"kotlin\" terminal=\"carbon\" theme=\"one-dark\"  >\n          <code slot=\"code\">@Deprecated(&quot;Use removeAt(index) instead.&quot;, ReplaceWith(&quot;removeAt(index)&quot;))\nfun remove(index: Int) {\n}</code>\n        </deckgo-highlight-code>\n<ul>\n<li><code>@Deprecated</code> 어노테이션은 최대 3가지 파라미터를 받는다.\n<ul>\n<li><code>message</code>: 사용중단 예고 이유</li>\n<li><code>replaceWith</code>: 옛 버전을 대신할 수 있는 패턴 제시</li>\n<li><code>level</code>: 점진적인 사용 중단을 지원 제공\n<ul>\n<li><code>WARNING</code>: 경고만 함</li>\n<li><code>ERROR</code>: 컴파일되지 못하게 막음.</li>\n<li><code>HIDDEN</code>: 컴파일되지 못하게 막으며, 예전에 컴파일된 코드와의 이진호환성만 유지</li>\n</ul>\n</li>\n</ul>\n</li>\n<li>코틀린에서 어노테이션 인자를 지정하는 문법은 자바와 약간 다르다.\n<ul>\n<li>클래스를 어노테이션 인자로 지정: <code>::class</code>를 클래스 이름 뒤에 넣어야 한다.</li>\n<li>다른 어노테이션을 인자로 지정: 인자로 들어가는 어노테이션의 이름 앞에 <code>@</code>를 넣지 말라.</li>\n<li>배열을 인자로 지정: 각괄호(<code>[]</code>)를 사용, <code>arrayOf</code> 함수 사용</li>\n</ul>\n</li>\n<li>어노테이션 인자를 컴파일 시점에 알 수 있어야 한다.\n<ul>\n<li>즉, 임의의 프로퍼티를 인자로 지정할 수는 없다.</li>\n<li>프로퍼티를 인자로 사용하려면 const 변경자를 붙여야 한다.</li>\n</ul>\n</li>\n</ul>\n<h3 id=\"-1212-어노테이션이-참조할-수-있는-정확한-선언-지정-어노테이션-타깃\" style=\"position:relative;\"><a href=\"#-1212-%EC%96%B4%EB%85%B8%ED%85%8C%EC%9D%B4%EC%85%98%EC%9D%B4-%EC%B0%B8%EC%A1%B0%ED%95%A0-%EC%88%98-%EC%9E%88%EB%8A%94-%EC%A0%95%ED%99%95%ED%95%9C-%EC%84%A0%EC%96%B8-%EC%A7%80%EC%A0%95-%EC%96%B4%EB%85%B8%ED%85%8C%EC%9D%B4%EC%85%98-%ED%83%80%EA%B9%83\" aria-label=\" 1212 어노테이션이 참조할 수 있는 정확한 선언 지정 어노테이션 타깃 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>🔖 12.1.2 어노테이션이 참조할 수 있는 정확한 선언 지정: 어노테이션 타깃</h3>\n<ul>\n<li><strong>사용 지점 타깃</strong> 선언은 통해 어노테이션을 붙일 요소를 정할 수 있다.\n<ul>\n<li>@ 기호와 어노테이션 이름 사이에 붙으며 어노테이션 이름과는 콜론으로 분리된다.</li>\n<li><code>@get:JvmName(\"obtainCertificate\")</code></li>\n</ul>\n</li>\n</ul>\n<deckgo-highlight-code language=\"kotlin\" terminal=\"carbon\" theme=\"one-dark\"  >\n          <code slot=\"code\">@JvmName(&quot;performCalculation&quot;)\nfun calculate(): Int {\n    return (2 + 2) - 1\n}</code>\n        </deckgo-highlight-code>\n<ul>\n<li><code>calculate</code> 함수를 자바 쪽에서 <code>performCalculation()</code>로 호출하게 한다.</li>\n</ul>\n<deckgo-highlight-code language=\"kotlin\" terminal=\"carbon\" theme=\"one-dark\"  >\n          <code slot=\"code\">class CertificateManager {\n    @get:JvmName(&quot;obtainCertificate&quot;)\n    @set:JvmName(&quot;putCertificate&quot;)\n    var certificate: String = &quot;-----BEGIN CERTIFICATE-----&quot;\n}</code>\n        </deckgo-highlight-code>\n<ul>\n<li><code>certificate</code> 프로퍼티를 <code>obtainCertificate</code>, <code>putCertificate</code> 라는 이름으로 사용할 수 있다.</li>\n</ul>\n<p>사용 지점 타깃을 지정할 때 지원하는 타깃 목록</p>\n<ul>\n<li>property: 프로퍼티 전체</li>\n<li>field: 프로퍼티에 의해 생성되는 필드</li>\n<li>get: 프로퍼티 게터</li>\n<li>set: 프로퍼티 세터</li>\n<li>receiver: 확장 함수나 프로퍼티의 수신 객체 파라미터</li>\n<li>param: 생성자 파라미터</li>\n<li>setparam: 세터 파라미터</li>\n<li>delegate: 위임 프로퍼티의 위임 인스턴스를 담아둔 필드</li>\n<li>file: 파일 안에 선언된 최상위 함수와 프로퍼티를 담아두는 클래스</li>\n</ul>\n<p>코틀린에는 컴파일러 경고를 무시하기 위한 <code>@Suppress</code> 어노테이션이 있다.</p>\n<h3 id=\"-1213-어노테이션을-활용해-json-직렬화-제어\" style=\"position:relative;\"><a href=\"#-1213-%EC%96%B4%EB%85%B8%ED%85%8C%EC%9D%B4%EC%85%98%EC%9D%84-%ED%99%9C%EC%9A%A9%ED%95%B4-json-%EC%A7%81%EB%A0%AC%ED%99%94-%EC%A0%9C%EC%96%B4\" aria-label=\" 1213 어노테이션을 활용해 json 직렬화 제어 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>🔖 12.1.3 어노테이션을 활용해 JSON 직렬화 제어</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\">data class Person(val name: String, val age: Int)\n\nfun main() {\n    val person = Person(&quot;Alice&quot;, 29)\n    println(serialize(person))\n}</code>\n        </deckgo-highlight-code>\n<ul>\n<li>Person 인스턴스를 serialize 함수에 전달하면 JSON 표현이 담긴 문자열을 돌려받는다.</li>\n</ul>\n<deckgo-highlight-code language=\"kotlin\" terminal=\"carbon\" theme=\"one-dark\"  >\n          <code slot=\"code\">fun main() {\n    val json = &quot;&quot;&quot;{&quot;name&quot;: &quot;Alice&quot;, &quot;age&quot;: 29&quot;}&quot;&quot;&quot;\n    println(deserialize&lt;Person&gt;(json))\n}</code>\n        </deckgo-highlight-code>\n<ul>\n<li></li>\n<li>JSON 표현을 다시 코틀린 객체로 만들려면 deserialize 함수를 호출한다.</li>\n</ul>\n<deckgo-highlight-code language=\"kotlin\" terminal=\"carbon\" theme=\"one-dark\"  >\n          <code slot=\"code\">data class Person(\n    @JsonName(&quot;alias&quot;) val firstName: String,\n    @JsonExclude val age: Int? = null\n)</code>\n        </deckgo-highlight-code>\n<ul>\n<li><code>@JsonExclude</code> 어노테이션을 사용하면 직렬화나 역직렬화할 때 무시해야 하는 프로퍼티를 표시할 수 있다.\n<ul>\n<li>기본값 지정 필요</li>\n</ul>\n</li>\n<li><code>@JsonName</code> 어노테이션을 사용하면 프로퍼티를 표현하는 키/값 쌍의 키로 프로퍼티 이름 대신 어노테이션이 지정한 문자열을 쓰게 할 수 있다.</li>\n</ul>\n<h3 id=\"-1214-어노테이션-선언\" style=\"position:relative;\"><a href=\"#-1214-%EC%96%B4%EB%85%B8%ED%85%8C%EC%9D%B4%EC%85%98-%EC%84%A0%EC%96%B8\" aria-label=\" 1214 어노테이션 선언 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>🔖 12.1.4 어노테이션 선언</h3>\n<deckgo-highlight-code language=\"kotlin\" terminal=\"carbon\" theme=\"one-dark\"  >\n          <code slot=\"code\">annotation class JsonExclude</code>\n        </deckgo-highlight-code>\n<ul>\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\">annotation class JsonName(val name: String)</code>\n        </deckgo-highlight-code>\n<ul>\n<li>파라미터가 있는 어노테이션을 정의하려면 어노테이션 클래스의 주 생성자에 파라미터를 선언해야 한다.</li>\n<li>주 생성자 구문을 사용하며, <code>val</code>로 선언</li>\n</ul>\n<h3 id=\"-1215-메타어노테이션-어노테이션을-처리하는-방법-제어\" style=\"position:relative;\"><a href=\"#-1215-%EB%A9%94%ED%83%80%EC%96%B4%EB%85%B8%ED%85%8C%EC%9D%B4%EC%85%98-%EC%96%B4%EB%85%B8%ED%85%8C%EC%9D%B4%EC%85%98%EC%9D%84-%EC%B2%98%EB%A6%AC%ED%95%98%EB%8A%94-%EB%B0%A9%EB%B2%95-%EC%A0%9C%EC%96%B4\" aria-label=\" 1215 메타어노테이션 어노테이션을 처리하는 방법 제어 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>🔖 12.1.5 메타어노테이션: 어노테이션을 처리하는 방법 제어</h3>\n<ul>\n<li>메타어노테이션: 어노테이션 클래스에 적용할 수 있는 어노테이션</li>\n</ul>\n<deckgo-highlight-code language=\"kotlin\" terminal=\"carbon\" theme=\"one-dark\"  >\n          <code slot=\"code\">@Target(AnnotationTarget.PROPERTY)\nannotation class JsonExclude</code>\n        </deckgo-highlight-code>\n<ul>\n<li><code>@Target</code> 메타어노테이션은 어노테이션을 적용할 수 있는 요소의 유형을 지정</li>\n<li>어노테이션 클래스에 대해 구체적인 <code>@Target</code>을 지정하지 않으면 모든 선언에 적용할 수 있는 어노테이션이 된다.</li>\n</ul>\n<deckgo-highlight-code language=\"kotlin\" terminal=\"carbon\" theme=\"one-dark\"  >\n          <code slot=\"code\">@Target(AnnotationTarget.ANNOTATION_CLASS)\nannotation class BindingAnnotation\n\n@BindingAnnotation\nannotation class MyBinding</code>\n        </deckgo-highlight-code>\n<ul>\n<li>메타어노테이션을 직접 만들어야 한다면 <code>ANNOTATION_CLASS</code>를 타깃으로 지정하자.</li>\n</ul>\n<h3 id=\"-1216-어노테이션-파라미터로-클래스-사용\" style=\"position:relative;\"><a href=\"#-1216-%EC%96%B4%EB%85%B8%ED%85%8C%EC%9D%B4%EC%85%98-%ED%8C%8C%EB%9D%BC%EB%AF%B8%ED%84%B0%EB%A1%9C-%ED%81%B4%EB%9E%98%EC%8A%A4-%EC%82%AC%EC%9A%A9\" aria-label=\" 1216 어노테이션 파라미터로 클래스 사용 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>🔖 12.1.6 어노테이션 파라미터로 클래스 사용</h3>\n<deckgo-highlight-code language=\"kotlin\" terminal=\"carbon\" theme=\"one-dark\"  >\n          <code slot=\"code\">interface Company {\n    val name: String\n}\n\ndata class CompanyImpl(override val name: String) : Company\n\ndata class Person(\n    val name: String,\n    @DeserializeInterface(CompanyImpl::class) val company: Company\n)</code>\n        </deckgo-highlight-code>\n<ul>\n<li><code>@DeserializeInterface</code>는 인터페이스 타입인 프로퍼티에 대한 역직렬화를 제어할 때 쓰는 어노테이션이다.</li>\n</ul>\n<deckgo-highlight-code language=\"kotlin\" terminal=\"carbon\" theme=\"one-dark\"  >\n          <code slot=\"code\">annotation class DeserializeInterface(val targetClass: KClass&lt;out Any&gt;)</code>\n        </deckgo-highlight-code>\n<ul>\n<li>클래스 참조를 인자로 받는 어노테이션은 위와 같이 정의한다.</li>\n</ul>\n<h3 id=\"-1217-어노테이션-파라미터로-제네릭-클래스-받기\" style=\"position:relative;\"><a href=\"#-1217-%EC%96%B4%EB%85%B8%ED%85%8C%EC%9D%B4%EC%85%98-%ED%8C%8C%EB%9D%BC%EB%AF%B8%ED%84%B0%EB%A1%9C-%EC%A0%9C%EB%84%A4%EB%A6%AD-%ED%81%B4%EB%9E%98%EC%8A%A4-%EB%B0%9B%EA%B8%B0\" aria-label=\" 1217 어노테이션 파라미터로 제네릭 클래스 받기 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>🔖 12.1.7 어노테이션 파라미터로 제네릭 클래스 받기</h3>\n<ul>\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\">interface ValueSerializer&lt;T&gt; {\n    fun toJsonValue(value: T): Any?\n    fun fromJsonValue(jsonValue: Any?): T\n}</code>\n        </deckgo-highlight-code>\n<ul>\n<li>이 인터페이스는 코틀린 객체에서 JSON 표현으로의 변환을 제공하며, 반대의 경우도 제공한다.</li>\n</ul>\n<deckgo-highlight-code language=\"kotlin\" terminal=\"carbon\" theme=\"one-dark\"  >\n          <code slot=\"code\">data class Person(\n    val name: String,\n    @CustomSerializer(DateSerializer::class) val birthDate: Date\n)</code>\n        </deckgo-highlight-code>\n<deckgo-highlight-code language=\"kotlin\" terminal=\"carbon\" theme=\"one-dark\"  >\n          <code slot=\"code\">annotation class CustomSerializer(val serializerClass: KClass&lt;out ValueSerializer&lt;*&gt;&gt;)</code>\n        </deckgo-highlight-code>\n<ul>\n<li>클래스를 어노테이션 인자로 받아야 할 때마다 같은 패턴 사용이 가능하다.\n<ul>\n<li><code>KClass&#x3C;out 자신의 클래스 이름></code></li>\n</ul>\n</li>\n<li>자신의 클래스 이름 자체가 타입 인자를 받아야 한다면 <code>KClass&#x3C;out 자신의 클래스 이름&#x3C;*>></code> 처럼 타입인자를 *로 바꾼다.</li>\n</ul>\n<h2 id=\"-122-리플렉션-실행-시점에-코틀린-객체-내부-관찰\" style=\"position:relative;\"><a href=\"#-122-%EB%A6%AC%ED%94%8C%EB%A0%89%EC%85%98-%EC%8B%A4%ED%96%89-%EC%8B%9C%EC%A0%90%EC%97%90-%EC%BD%94%ED%8B%80%EB%A6%B0-%EA%B0%9D%EC%B2%B4-%EB%82%B4%EB%B6%80-%EA%B4%80%EC%B0%B0\" aria-label=\" 122 리플렉션 실행 시점에 코틀린 객체 내부 관찰 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>📖 12.2 리플렉션: 실행 시점에 코틀린 객체 내부 관찰</h2>\n<ul>\n<li>리플렉션은 실행 시점에 객체의 프로퍼티와 메서드에 접근할 수 있게 해주는 방법</li>\n</ul>\n<h3 id=\"-1221-코틀린-리플렉션-api-kclass-kcallable-kfunction-kproperty\" style=\"position:relative;\"><a href=\"#-1221-%EC%BD%94%ED%8B%80%EB%A6%B0-%EB%A6%AC%ED%94%8C%EB%A0%89%EC%85%98-api-kclass-kcallable-kfunction-kproperty\" aria-label=\" 1221 코틀린 리플렉션 api kclass kcallable kfunction kproperty 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>🔖 12.2.1 코틀린 리플렉션 API: KClass, KCallable, KFunction, KProperty</h3>\n<deckgo-highlight-code language=\"kotlin\" terminal=\"carbon\" theme=\"one-dark\"  >\n          <code slot=\"code\">interface KClass&lt;T : Any&gt; {\n    val simpleName: String?\n    val qualifiedName: String?\n    val members: Collection&lt;KCallable&lt;*&gt;&gt;\n    val constructors: Collection&lt;KFunction&lt;T&gt;&gt;\n    val nestedClasses: Collection&lt;KClass&lt;*&gt;&gt;\n    // ...\n}</code>\n        </deckgo-highlight-code>\n<ul>\n<li><code>KClass</code> 선언을 찾아보면 클래스 내부를 살펴볼 때 사용할 수 있는 다양하고 유용한 메서드를 볼 수 있다.</li>\n</ul>\n<deckgo-highlight-code language=\"kotlin\" terminal=\"carbon\" theme=\"one-dark\"  >\n          <code slot=\"code\">interface KCallable&lt;out R&gt; {\n    fun call(vararg args: Any?): R\n    // ...\n}</code>\n        </deckgo-highlight-code>\n<ul>\n<li><code>KCallable은</code> 함수와 프로퍼티를 아우르는 공통 상위 인터페이스다.</li>\n<li><code>call</code>을 사용할 때는 함수 인자를 <code>vararg</code> 리스트로 전달</li>\n</ul>\n<deckgo-highlight-code language=\"kotlin\" terminal=\"carbon\" theme=\"one-dark\"  >\n          <code slot=\"code\">fun foo(x: Int) = println(x)\n\nfun main() {\n    val kFunction = ::foo\n    kFunction.call(42)\n}</code>\n        </deckgo-highlight-code>\n<ul>\n<li><code>::foo</code> 식의 값 타입이 <code>KCallable</code> 클래스의 인스턴스이다.</li>\n</ul>\n<deckgo-highlight-code language=\"kotlin\" terminal=\"carbon\" theme=\"one-dark\"  >\n          <code slot=\"code\">fun sum(x: Int, y: Int) = x + y\n\nfun main() {\n    val kFunction = KFunction2&lt;Int, Int, Int&gt; = ::sum\n    println(kFunction.invoke(1, 2) + kFunction.invoke(3, 4))\n    kFunction(1)\n    // error\n}</code>\n        </deckgo-highlight-code>\n<ul>\n<li>invoke 메서드를 호출할 때는 인자 개수나 타입을 실수로 틀릴 수 없다. 컴파일이 안되기 때문</li>\n</ul>\n<deckgo-highlight-code language=\"kotlin\" terminal=\"carbon\" theme=\"one-dark\"  >\n          <code slot=\"code\">var counter = 0\n\nfun main() {\n    val kProperty = ::counter\n    kProperty.setter.call(21)\n    println(kProperty.get())\n}</code>\n        </deckgo-highlight-code>\n<ul>\n<li>최상위 읽기 전용과 가변 프로퍼티는 각각 <code>KProperty0</code>이나, <code>KMutableProperty0</code> 인터페이스의 인스턴스로 표현\n<ul>\n<li>인자 없는 get method 제공</li>\n</ul>\n</li>\n<li><code>KProperty1</code>은 제네릭 클래스이다.</li>\n</ul>\n<h3 id=\"-1222-리플렉션을-사용해-객체-직렬화-구현\" style=\"position:relative;\"><a href=\"#-1222-%EB%A6%AC%ED%94%8C%EB%A0%89%EC%85%98%EC%9D%84-%EC%82%AC%EC%9A%A9%ED%95%B4-%EA%B0%9D%EC%B2%B4-%EC%A7%81%EB%A0%AC%ED%99%94-%EA%B5%AC%ED%98%84\" aria-label=\" 1222 리플렉션을 사용해 객체 직렬화 구현 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>🔖 12.2.2 리플렉션을 사용해 객체 직렬화 구현</h3>\n<deckgo-highlight-code language=\"kotlin\" terminal=\"carbon\" theme=\"one-dark\"  >\n          <code slot=\"code\">fun serialize(obj: Any): String</code>\n        </deckgo-highlight-code>\n<ul>\n<li>제이키드의 직렬화 함수 선언이다.</li>\n<li>이 함수는 결과 JSON을 <code>StringBuilder</code> 인스턴스 안에 구축한다.</li>\n</ul>\n<deckgo-highlight-code language=\"kotlin\" terminal=\"carbon\" theme=\"one-dark\"  >\n          <code slot=\"code\">private fun StringBuilder.serializeObject(x: Any) {\n    append(/*...*/)\n}</code>\n        </deckgo-highlight-code>\n<ul>\n<li>별도로 수신 객체를 지정하지 않고 append 메서드를 편하게 사용할 수 있다.</li>\n</ul>\n<deckgo-highlight-code language=\"kotlin\" terminal=\"carbon\" theme=\"one-dark\"  >\n          <code slot=\"code\">fun serialize(obj: Any): String = buildString { serializeObject(obj) }</code>\n        </deckgo-highlight-code>\n<ul>\n<li>대부분의 작업을 <code>serializeObject</code>에 위임한다.</li>\n</ul>\n<deckgo-highlight-code language=\"kotlin\" terminal=\"carbon\" theme=\"one-dark\"  >\n          <code slot=\"code\">private fun StringBuilder.serializeObject(obj: Any) {\n    val kClass = obj::class as KClass&lt;Any&gt;\n    val properties = kClass.memberProperties\n\n    properties.joinToStringBuilder(\n        this, prefix = &quot;{&quot;, postfix = &quot;}&quot;\n    ) { prop -&gt;\n        serializeString(prop.name)\n        append(&quot;: &quot;)\n        serializePropertyValue(prop.get(obj))\n    }\n}</code>\n        </deckgo-highlight-code>\n<ul>\n<li>클래스의 각 프로퍼티를 차례로 직렬화 한다.</li>\n</ul>\n<h3 id=\"-1223-어노테이션을-활용해-직렬화-제어\" style=\"position:relative;\"><a href=\"#-1223-%EC%96%B4%EB%85%B8%ED%85%8C%EC%9D%B4%EC%85%98%EC%9D%84-%ED%99%9C%EC%9A%A9%ED%95%B4-%EC%A7%81%EB%A0%AC%ED%99%94-%EC%A0%9C%EC%96%B4\" aria-label=\" 1223 어노테이션을 활용해 직렬화 제어 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>🔖 12.2.3 어노테이션을 활용해 직렬화 제어</h3>\n<p>어노테이션을 지원하기 위해 <code>serializeObject</code>를 어떻게 수정할지 알아본다.</p>\n<deckgo-highlight-code language=\"kotlin\" terminal=\"carbon\" theme=\"one-dark\"  >\n          <code slot=\"code\">val properties = kClass.memberProperties.filter { it.findAnnotation&lt;JsonExclude&gt;() == null }</code>\n        </deckgo-highlight-code>\n<ul>\n<li>위와 같이 <code>@JsonExclude</code> 어노테이션이 붙지 않은 프로퍼티만 남길 수 있다.</li>\n</ul>\n<deckgo-highlight-code language=\"kotlin\" terminal=\"carbon\" theme=\"one-dark\"  >\n          <code slot=\"code\">val jsonNameAnn = prop.findAnnotation&lt;JsonName&gt;()\nval propName = jsonNameAnn?.name ?: prop.name</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\">private fun StringBuilder.serializeObject(obj: Any) {\n    (obj::class as KClass&lt;Any&gt;)\n        .memberProperties\n        .filter { it.findAnnotation&lt;JsonExclude&gt;() == null }\n        .joinToStringBuilder(this, prefix = &quot;{&quot;, postfix = &quot;}&quot;) {\n            serializeProperty(it, obj)\n        }\n}</code>\n        </deckgo-highlight-code>\n<ul>\n<li><code>@JsonExclude</code> 어노테이션한 프로퍼티를 제외시킨다.</li>\n</ul>\n<deckgo-highlight-code language=\"kotlin\" terminal=\"carbon\" theme=\"one-dark\"  >\n          <code slot=\"code\">private fun StringBuilder.serializeObject(prop: KProperty1&lt;Any, *&gt;, obj: Any) {\n    val jsonNameAnn = prop.findAnnotation&lt;JsonName&gt;()\n    val propName = jsonNameAnn?.name ?: prop.name\n    serializeString(propName)\n    append(&quot;: &quot;)\n    serializePropertyValue(prop.get(obj))\n}</code>\n        </deckgo-highlight-code>\n<ul>\n<li><code>@JsonName</code>에 따라 프로퍼티 이름을 처리한다.</li>\n</ul>\n<deckgo-highlight-code language=\"kotlin\" terminal=\"carbon\" theme=\"one-dark\"  >\n          <code slot=\"code\">fun KProperty&lt;*&gt;.getSerializer(): ValueSerializer&lt;Any?&gt;? {\n    val customSerializerAnn = findAnnotation&lt;CustomSerializer&gt;() ?: return null\n    val serializerClass = customSerializerAnn.serializerClass\n\n    val valueSerializer = serializerClass.objectInstance\n        ?: serializerClass.createInstance()\n    @Suppress(&quot;UNCHECKED_CAST&quot;)\n    return valueSerializer as ValueSerializer&lt;Any?&gt;\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\">private fun StringBuilder.serializeProperty(\n    prop: KProperty1&lt;Any, *&gt;, obj: Any\n) {\n    val jsonNameAnn = prop.findAnnotation&lt;JsonName&gt;()\n    val propName = jsonNameAnn?.name ?: prop.name\n    serializeString(propName)\n    append(&quot;: &quot;)\n\n    val value = prop.get(obj)\n    val jsonValue = prop.getSerializer()?.toJsonValue(value) ?: value\n    serializePropertyValue(jsonValue)\n}</code>\n        </deckgo-highlight-code>\n<ul>\n<li><code>serializeProperty</code> 구현 안에서 <code>getSerializer</code>를 사용할 수 있다.</li>\n</ul>\n<h3 id=\"-1224-json-파싱과-객체-역직렬화\" style=\"position:relative;\"><a href=\"#-1224-json-%ED%8C%8C%EC%8B%B1%EA%B3%BC-%EA%B0%9D%EC%B2%B4-%EC%97%AD%EC%A7%81%EB%A0%AC%ED%99%94\" aria-label=\" 1224 json 파싱과 객체 역직렬화 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>🔖 12.2.4 JSON 파싱과 객체 역직렬화</h3>\n<ul>\n<li>제이키드의 JSON 역직렬화기는 3단계로 구현돼 있다.\n<ol>\n<li>어휘 분석기(렉서)\n<ul>\n<li>토큰의 리스트로 변환(문자토큰, 값 토큰)</li>\n</ul>\n</li>\n<li>문법 분석기(파서)\n<ul>\n<li>토큰의 리스트를 구조화된 표현으로 변환</li>\n</ul>\n</li>\n<li>역직렬화 컴포넌트\n<ul>\n<li>필요한 클래스의 인스턴스를 생성해 반환</li>\n</ul>\n</li>\n</ol>\n</li>\n</ul>\n<h3 id=\"-1225-최종-역직렬화-단계-callby와-리플렉션을-사용해-객체-만들기\" style=\"position:relative;\"><a href=\"#-1225-%EC%B5%9C%EC%A2%85-%EC%97%AD%EC%A7%81%EB%A0%AC%ED%99%94-%EB%8B%A8%EA%B3%84-callby%EC%99%80-%EB%A6%AC%ED%94%8C%EB%A0%89%EC%85%98%EC%9D%84-%EC%82%AC%EC%9A%A9%ED%95%B4-%EA%B0%9D%EC%B2%B4-%EB%A7%8C%EB%93%A4%EA%B8%B0\" aria-label=\" 1225 최종 역직렬화 단계 callby와 리플렉션을 사용해 객체 만들기 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>🔖 12.2.5 최종 역직렬화 단계: callBy()와 리플렉션을 사용해 객체 만들기</h3>\n<deckgo-highlight-code language=\"kotlin\" terminal=\"carbon\" theme=\"one-dark\"  >\n          <code slot=\"code\">interface KCallable&lt;out R&gt; {\n    fun callBy(args: Map&lt;KParameter, Any?&gt;): R\n    // ...\n}</code>\n        </deckgo-highlight-code>\n<ul>\n<li><code>KCallable.call</code>은 디폴트 파라미터 값을 지원하지 않는다.</li>\n<li><code>KCallable.callBy</code>는 디폴트 파라미터 값을 지원한다.</li>\n</ul>\n<deckgo-highlight-code language=\"kotlin\" terminal=\"carbon\" theme=\"one-dark\"  >\n          <code slot=\"code\">object BooleanSerializer : ValueSerializer&lt;Boolean&gt; {\n    override fun fromJsonValue(jsonValue: Any?): Boolean {\n        if (jsonValue !is Boolean) throw JkidException(&quot;Boolean Expected&quot;)\n            return jsonValue\n    }\n    override fun toJsonValue(value: Boolean) = value\n}</code>\n        </deckgo-highlight-code>\n<ul>\n<li>Boolean 값을 위한 직렬화기</li>\n</ul>\n<deckgo-highlight-code language=\"kotlin\" terminal=\"carbon\" theme=\"one-dark\"  >\n          <code slot=\"code\">class ClassInfoCache {\n    private val cacheData = mutableMapOf&lt;KClass&lt;*&gt;, ClassInfo&lt;*&gt;&gt;()\n\n    @Suppress(&quot;UNCHECKED_CAST&quot;)\n    operator fun &lt;T : Any&gt; get(cls: KClass&lt;T&gt;): ClassInfo&lt;T&gt; =\n        cacheData.getOrPut(cls) { ClassInfo(cls) } as ClassInfo&lt;T&gt;\n}</code>\n        </deckgo-highlight-code>\n<ul>\n<li>리플렉션 데이터 캐시 저장소</li>\n<li>성능을 위해 검색 결과를 캐시에 넣어둔다.</li>\n</ul>","excerpt":"📖 12.1 어노테이션 선언과 적용 🔖 12.1.1 어노테이션을 적용해 선언해 표지 남기기 코틀린에서 어노테이션을 적용하려면 @와 어노테이션 이름을 선언 앞에 넣으면 된다. @Deprecated 어노테이션은 최대 3가지 파라미터를 받는다. message: 사용중단 예고 이유 replaceWith: 옛 버전을 대신할 수 있는 패턴 제시 level: 점진적인 사용 중단을 지원 제공 WARNING: 경고만 함 ERROR: 컴파일되지 못하게 막음. HIDDEN…","fields":{"slug":"/backend/kotlin-in-action/12장-어노테이션과_리플렉션/"},"frontmatter":{"title":"Kotlin in Action - 12장 애노테이션과 리플렉션","thumbnail":{"childImageSharp":{"fluid":{"src":"/static/f7f7ddfa31d1614405dc5af1487b9ec4/9c94d/kotlin-in-action.png"}}},"draft":false,"category":"Back-End","tags":["Kotlin"],"date":"May 25, 2025"}},"node":{"id":"b5f5340f-f158-5b1d-aff9-1d7991c14962","html":"<h2 id=\"-111-타입-인자를-받는-타입-만들기-제네릭-타입-파라미터\" style=\"position:relative;\"><a href=\"#-111-%ED%83%80%EC%9E%85-%EC%9D%B8%EC%9E%90%EB%A5%BC-%EB%B0%9B%EB%8A%94-%ED%83%80%EC%9E%85-%EB%A7%8C%EB%93%A4%EA%B8%B0-%EC%A0%9C%EB%84%A4%EB%A6%AD-%ED%83%80%EC%9E%85-%ED%8C%8C%EB%9D%BC%EB%AF%B8%ED%84%B0\" aria-label=\" 111 타입 인자를 받는 타입 만들기 제네릭 타입 파라미터 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>📖 11.1 타입 인자를 받는 타입 만들기: 제네릭 타입 파라미터</h2>\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\">val readers: MutableList&lt;String&gt; = mutableListOf()\nval readers = mutableListOf&lt;String&gt;()</code>\n        </deckgo-highlight-code>\n<ul>\n<li>위 두 선언은 동등하다.</li>\n<li>빈 리스트를 만들어야 한다면 타입 인자를 추론할 근거가 없기에 직접 명시해야 한다.</li>\n<li>코틀린에는 raw 타입이 없다.</li>\n</ul>\n<h3 id=\"-1111-제네릭-타입과-함께-동작하는-함수와-프로퍼티\" style=\"position:relative;\"><a href=\"#-1111-%EC%A0%9C%EB%84%A4%EB%A6%AD-%ED%83%80%EC%9E%85%EA%B3%BC-%ED%95%A8%EA%BB%98-%EB%8F%99%EC%9E%91%ED%95%98%EB%8A%94-%ED%95%A8%EC%88%98%EC%99%80-%ED%94%84%EB%A1%9C%ED%8D%BC%ED%8B%B0\" aria-label=\" 1111 제네릭 타입과 함께 동작하는 함수와 프로퍼티 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>🔖 11.1.1 제네릭 타입과 함께 동작하는 함수와 프로퍼티</h3>\n<ul>\n<li>제네릭 함수를 호출할 때는 반드시 구체적 타입으로 타입인자를 넘겨야 한다.</li>\n<li>함수의 타입 파라미터 T가 수신 객체와 반환 타입에 쓰인다.</li>\n</ul>\n<deckgo-highlight-code language=\"kotlin\" terminal=\"carbon\" theme=\"one-dark\"  >\n          <code slot=\"code\">fun main() {\n    val letters = (&#39;a&#39;..&#39;z&#39;).toList()\n    println(letters.slice&lt;Char&gt;(0..2))\n    println(letters.slice(10..13))\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 authors = listOf(&quot;Sveta&quot;, &quot;Seb&quot;, &quot;Roman&quot;, &quot;Dima&quot;)\n    val readers = mutableListOf&lt;String&gt;(&quot;Seb&quot;, &quot;Hadi&quot;)\n    println(readers.filter { it !in authors })\n}</code>\n        </deckgo-highlight-code>\n<ul>\n<li>it의 타입은 <code>String</code>이다.</li>\n<li>컴파일러는 <code>filter</code>가 <code>List&#x3C;T></code>타입의 리스트에 대해 호출될 수 있다는 사실과 <code>filter</code>의 수신 객체인 <code>reader</code>의 실제 타입이 <code>List&#x3C;String></code>이라는 사실을 알고 그로부터 추론한다.</li>\n<li>일반 프로퍼티는 타입 파라미터를 가질 수 없지만 확장 프로퍼티는 가질 수 있다.</li>\n</ul>\n<h3 id=\"-1112-제네릭-클래스를-홑화살괄호-구문을-사용해-선언한다\" style=\"position:relative;\"><a href=\"#-1112-%EC%A0%9C%EB%84%A4%EB%A6%AD-%ED%81%B4%EB%9E%98%EC%8A%A4%EB%A5%BC-%ED%99%91%ED%99%94%EC%82%B4%EA%B4%84%ED%98%B8-%EA%B5%AC%EB%AC%B8%EC%9D%84-%EC%82%AC%EC%9A%A9%ED%95%B4-%EC%84%A0%EC%96%B8%ED%95%9C%EB%8B%A4\" aria-label=\" 1112 제네릭 클래스를 홑화살괄호 구문을 사용해 선언한다 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>🔖 11.1.2 제네릭 클래스를 홑화살괄호 구문을 사용해 선언한다.</h3>\n<ul>\n<li>제네릭 클래스를 확장하는 클래스를 정의하려면 기반 타입의 제네릭 파라미터에 대해 타입 인자를 지정해야 한다.</li>\n</ul>\n<deckgo-highlight-code language=\"kotlin\" terminal=\"carbon\" theme=\"one-dark\"  >\n          <code slot=\"code\">class StringList : List&lt;String&gt; {\n    override fun get(index: Int): String = TODO()\n}\n\nclass ArrayList&lt;T&gt; : List&lt;T&gt; {\n    override fun get(index: Int): T = TODO()\n}</code>\n        </deckgo-highlight-code>\n<ul>\n<li>하위 클래스에서 상위 클래스에 정의된 함수를 오버라이드하거나 사용하려면 타입 인자 T를 구체적 타입 <code>String</code>으로 치환해야 한다.</li>\n<li><code>ArrayList</code> 클래스는 자신만의 타입 파라미터 T를 정의하면서 그 T를 기반 클래스의 타입 인자로 사용한다.</li>\n<li>클래스가 자신을 타입 인자로 참조할 수도 있다.</li>\n</ul>\n<h3 id=\"-1113-제네릭-클래스나-함수가-사용할-수-있는-타입-제한-타입-파라미터-제약\" style=\"position:relative;\"><a href=\"#-1113-%EC%A0%9C%EB%84%A4%EB%A6%AD-%ED%81%B4%EB%9E%98%EC%8A%A4%EB%82%98-%ED%95%A8%EC%88%98%EA%B0%80-%EC%82%AC%EC%9A%A9%ED%95%A0-%EC%88%98-%EC%9E%88%EB%8A%94-%ED%83%80%EC%9E%85-%EC%A0%9C%ED%95%9C-%ED%83%80%EC%9E%85-%ED%8C%8C%EB%9D%BC%EB%AF%B8%ED%84%B0-%EC%A0%9C%EC%95%BD\" aria-label=\" 1113 제네릭 클래스나 함수가 사용할 수 있는 타입 제한 타입 파라미터 제약 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>🔖 11.1.3 제네릭 클래스나 함수가 사용할 수 있는 타입 제한: 타입 파라미터 제약</h3>\n<deckgo-highlight-code language=\"kotlin\" terminal=\"carbon\" theme=\"one-dark\"  >\n          <code slot=\"code\">fun &lt;T: Number&gt; oneHalf(value: T): Double {\n    return value.toDouble()\n}</code>\n        </deckgo-highlight-code>\n<ul>\n<li>타입 파라미터 제약은 클래스나 함수에 사용할 수 있는 타입 인자를 제한하는 기능</li>\n<li>어떤 타입을 제네릭 타입의 타입 파라미터에 대한 <strong>상계</strong>로 지정하면 그 제네릭 타입을 인스턴스화할 때 사용하는 타입 인자는 반드시 그 상계 타입이거나 그 상계 타입의 하위 타입이어야 한다.</li>\n<li>제약을 가하려면 타입 파라미터 이름 뒤에 콜론(:)을 표시하고 그 뒤에 상계 타입을 적으면 된다.</li>\n</ul>\n<deckgo-highlight-code language=\"kotlin\" terminal=\"carbon\" theme=\"one-dark\"  >\n          <code slot=\"code\">fun &lt;T: Comparable&lt;T&gt;&gt; max(first: T, second: T): T {\n    return if (first &gt; second) first else second\n}\n\nfun main() {\n    println(max(&quot;kotlin&quot;, &quot;java&quot;))\n    // kotlin\n}</code>\n        </deckgo-highlight-code>\n<ul>\n<li><code>max</code>를 비교할 수 없는 값 사이에 호출하면 컴파일 오류 발생</li>\n</ul>\n<deckgo-highlight-code language=\"kotlin\" terminal=\"carbon\" theme=\"one-dark\"  >\n          <code slot=\"code\">fun &lt;T&gt; ensureTrailingPeriod(seq: T) where T: CharSequence, T: Appendable {\n    if (!seq.endsWith(&#39;.&#39;)) {\n        seq.append(&#39;.&#39;)\n    }\n}</code>\n        </deckgo-highlight-code>\n<ul>\n<li>타입 파라미터에 대해 둘 이상의 제약을 가해야하는 경우</li>\n</ul>\n<h3 id=\"-1114-명시적으로-타입-파라미터를-널이-될-수-없는-타입으로-표시해서-널이-될-수-있는-타입-인자-제외시키기\" style=\"position:relative;\"><a href=\"#-1114-%EB%AA%85%EC%8B%9C%EC%A0%81%EC%9C%BC%EB%A1%9C-%ED%83%80%EC%9E%85-%ED%8C%8C%EB%9D%BC%EB%AF%B8%ED%84%B0%EB%A5%BC-%EB%84%90%EC%9D%B4-%EB%90%A0-%EC%88%98-%EC%97%86%EB%8A%94-%ED%83%80%EC%9E%85%EC%9C%BC%EB%A1%9C-%ED%91%9C%EC%8B%9C%ED%95%B4%EC%84%9C-%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%9D%B8%EC%9E%90-%EC%A0%9C%EC%99%B8%EC%8B%9C%ED%82%A4%EA%B8%B0\" aria-label=\" 1114 명시적으로 타입 파라미터를 널이 될 수 없는 타입으로 표시해서 널이 될 수 있는 타입 인자 제외시키기 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>🔖 11.1.4 명시적으로 타입 파라미터를 널이 될 수 없는 타입으로 표시해서 널이 될 수 있는 타입 인자 제외시키기</h3>\n<deckgo-highlight-code language=\"kotlin\" terminal=\"carbon\" theme=\"one-dark\"  >\n          <code slot=\"code\">class Processor&lt;T&gt; {\n    fun process(value: T) {\n        value?.hashCode()\n    }\n}</code>\n        </deckgo-highlight-code>\n<ul>\n<li>아무런 상계를 정하지 않은 타입 파라미터는 <code>Any?</code>를 상계로 정한 파라미터와 같다.</li>\n<li>T 타입이 널이 될 수 있는 타입이 되지 못하게 막는 아무런 제약이 없기 때</li>\n</ul>\n<deckgo-highlight-code language=\"kotlin\" terminal=\"carbon\" theme=\"one-dark\"  >\n          <code slot=\"code\">class Processor&lt;T: Any&gt; {\n    fun process(value: T) {\n        value.hashCode()\n    }\n}</code>\n        </deckgo-highlight-code>\n<ul>\n<li>항상 널이 될 수 없는 타입만 타입 인자로 받게 만들려면 타입 파라미터에 제약을 가해야 한다.</li>\n</ul>\n<h2 id=\"-112-실행-시점-제네릭스-동작-소거된-타입-파라미터와-실체화된-타입-파라미터\" style=\"position:relative;\"><a href=\"#-112-%EC%8B%A4%ED%96%89-%EC%8B%9C%EC%A0%90-%EC%A0%9C%EB%84%A4%EB%A6%AD%EC%8A%A4-%EB%8F%99%EC%9E%91-%EC%86%8C%EA%B1%B0%EB%90%9C-%ED%83%80%EC%9E%85-%ED%8C%8C%EB%9D%BC%EB%AF%B8%ED%84%B0%EC%99%80-%EC%8B%A4%EC%B2%B4%ED%99%94%EB%90%9C-%ED%83%80%EC%9E%85-%ED%8C%8C%EB%9D%BC%EB%AF%B8%ED%84%B0\" aria-label=\" 112 실행 시점 제네릭스 동작 소거된 타입 파라미터와 실체화된 타입 파라미터 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>📖 11.2 실행 시점 제네릭스 동작: 소거된 타입 파라미터와 실체화된 타입 파라미터</h2>\n<ul>\n<li>JVM의 제네릭스는 보통 <strong>타입소거</strong>를 사용해 구현된다.\n<ul>\n<li>실행 시점에 제네릭 클래스의 인스턴스에 타입 인자 정보가 들어있지 않다.</li>\n</ul>\n</li>\n</ul>\n<h3 id=\"-1121-실행-시점에-제네릭-클래스의-타입-정보를-찾을-때-한계-타입-검사와-캐스팅\" style=\"position:relative;\"><a href=\"#-1121-%EC%8B%A4%ED%96%89-%EC%8B%9C%EC%A0%90%EC%97%90-%EC%A0%9C%EB%84%A4%EB%A6%AD-%ED%81%B4%EB%9E%98%EC%8A%A4%EC%9D%98-%ED%83%80%EC%9E%85-%EC%A0%95%EB%B3%B4%EB%A5%BC-%EC%B0%BE%EC%9D%84-%EB%95%8C-%ED%95%9C%EA%B3%84-%ED%83%80%EC%9E%85-%EA%B2%80%EC%82%AC%EC%99%80-%EC%BA%90%EC%8A%A4%ED%8C%85\" aria-label=\" 1121 실행 시점에 제네릭 클래스의 타입 정보를 찾을 때 한계 타입 검사와 캐스팅 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>🔖 11.2.1 실행 시점에 제네릭 클래스의 타입 정보를 찾을 때 한계: 타입 검사와 캐스팅</h3>\n<ul>\n<li>자바와 마찬가지로 코틀린 제네릭 타입 인자 정보는 런타임에 지워진다.</li>\n</ul>\n<h4 id=\"️-타입-소거로-인해-생기는-한계\" style=\"position:relative;\"><a href=\"#%EF%B8%8F-%ED%83%80%EC%9E%85-%EC%86%8C%EA%B1%B0%EB%A1%9C-%EC%9D%B8%ED%95%B4-%EC%83%9D%EA%B8%B0%EB%8A%94-%ED%95%9C%EA%B3%84\" aria-label=\"️ 타입 소거로 인해 생기는 한계 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>🛠️ 타입 소거로 인해 생기는 한계</h4>\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\">fun readNumbersOrWords(): List&lt;Any&gt; {\n    val input = readln()\n    val words: List&lt;String&gt; = input.split(&quot;,&quot;)\n    val numbers: List&lt;Int&gt; = words.mapNotNull { it.toIntOrNull() }\n    return numbers.ifEmpty { words }\n}\n\nfun printList(l: List&lt;Any&gt;) {\n    when(1) {\n        is List&lt;String&gt; -&gt; println(&quot;Strings: $l&quot;) // error\n        is List&lt;Int&gt; -&gt; println(&quot;Integers: $l&quot;) // error\n    }\n}</code>\n        </deckgo-highlight-code>\n<ul>\n<li>실행 시점에 어떤 값이 <code>List</code>인지 여부는 확실히 알아낼 수 있지만 그 리스트가 문자열, 사람 등 실제 어떤 타입의 원소가 들어있는 리스트인지는 알 수 없다.</li>\n</ul>\n<deckgo-highlight-code language=\"kotlin\" terminal=\"carbon\" theme=\"one-dark\"  >\n          <code slot=\"code\">fun printSum(c: Collection&lt;*&gt;) {\n    val intList = c as? List&lt;Int&gt; ?: throw IllegalArgumentException(&quot;List is expected&quot;)\n    println(intList.sum())\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\">fun printSum(c: Collection&lt;Int&gt;) {\n    when (c) {\n        is List&lt;Int&gt; -&gt; println(&quot;List sum: ${c.sum()}&quot;)\n        is Set&lt;Int&gt; -&gt; println(&quot;Set sum: ${c.sum()}&quot;)\n    }\n}</code>\n        </deckgo-highlight-code>\n<ul>\n<li>컴파일 시점에 타입 정보가 주어진 경우에는 is 검사 수행이 허용된다.</li>\n</ul>\n<h3 id=\"-1122-실체화된-타입-파라미터를-사용하는-함수는-타입-인자를-실행-시점에-언급할-수-있다\" style=\"position:relative;\"><a href=\"#-1122-%EC%8B%A4%EC%B2%B4%ED%99%94%EB%90%9C-%ED%83%80%EC%9E%85-%ED%8C%8C%EB%9D%BC%EB%AF%B8%ED%84%B0%EB%A5%BC-%EC%82%AC%EC%9A%A9%ED%95%98%EB%8A%94-%ED%95%A8%EC%88%98%EB%8A%94-%ED%83%80%EC%9E%85-%EC%9D%B8%EC%9E%90%EB%A5%BC-%EC%8B%A4%ED%96%89-%EC%8B%9C%EC%A0%90%EC%97%90-%EC%96%B8%EA%B8%89%ED%95%A0-%EC%88%98-%EC%9E%88%EB%8B%A4\" aria-label=\" 1122 실체화된 타입 파라미터를 사용하는 함수는 타입 인자를 실행 시점에 언급할 수 있다 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>🔖 11.2.2 실체화된 타입 파라미터를 사용하는 함수는 타입 인자를 실행 시점에 언급할 수 있다</h3>\n<ul>\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\">inline fun &lt;reified T&gt; isA(value: Any) = value is T\n\nfun main() {\n    println(isA&lt;String&gt;(&quot;abc&quot;))\n    println(isA&lt;String&gt;(123))\n}</code>\n        </deckgo-highlight-code>\n<ul>\n<li>타입 파라미터를 <code>reified</code>로 지정하면 <code>value</code>의 타입이 T의 인스턴스인지를 실행 시점에 검사할 수 있다.</li>\n</ul>\n<deckgo-highlight-code language=\"kotlin\" terminal=\"carbon\" theme=\"one-dark\"  >\n          <code slot=\"code\">fun main() {\n    val items = listOf(&quot;one&quot;, 2, &quot;three&quot;)\n    println(items.filterIsInstance&lt;String&gt;())\n}</code>\n        </deckgo-highlight-code>\n<deckgo-highlight-code language=\"kotlin\" terminal=\"carbon\" theme=\"one-dark\"  >\n          <code slot=\"code\">inline fun &lt;reified R, C : MutableCollection&lt;in R&gt;&gt; Iterable&lt;*&gt;.filterIsInstanceTo(destination: C): C {\n    for (element in this) if (element is R) destination.add(element)\n    return destination\n}</code>\n        </deckgo-highlight-code>\n<ul>\n<li><code>filterIsInstance</code> 함수는 인자로 받은 컬렉션에서 지정한 클래스의 인스턴스만을 모아 만든 리스트를 반환</li>\n</ul>\n<h3 id=\"-1123-클래스-참조를-실체화된-타입-파라미터로-대신함으로써-javalangclass-파라미터-피하기\" style=\"position:relative;\"><a href=\"#-1123-%ED%81%B4%EB%9E%98%EC%8A%A4-%EC%B0%B8%EC%A1%B0%EB%A5%BC-%EC%8B%A4%EC%B2%B4%ED%99%94%EB%90%9C-%ED%83%80%EC%9E%85-%ED%8C%8C%EB%9D%BC%EB%AF%B8%ED%84%B0%EB%A1%9C-%EB%8C%80%EC%8B%A0%ED%95%A8%EC%9C%BC%EB%A1%9C%EC%8D%A8-javalangclass-%ED%8C%8C%EB%9D%BC%EB%AF%B8%ED%84%B0-%ED%94%BC%ED%95%98%EA%B8%B0\" aria-label=\" 1123 클래스 참조를 실체화된 타입 파라미터로 대신함으로써 javalangclass 파라미터 피하기 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>🔖 11.2.3 클래스 참조를 실체화된 타입 파라미터로 대신함으로써 java.lang.Class 파라미터 피하기</h3>\n<deckgo-highlight-code language=\"kotlin\" terminal=\"carbon\" theme=\"one-dark\"  >\n          <code slot=\"code\">val serviceImpl = ServiceLoader.load(Service::class.java)</code>\n        </deckgo-highlight-code>\n<ul>\n<li><code>::class.java</code> 구문은 코틀린 클래스에 대응하는 <code>java.lang.Class</code> 참조를 얻는 방법을 보여준다.</li>\n</ul>\n<deckgo-highlight-code language=\"kotlin\" terminal=\"carbon\" theme=\"one-dark\"  >\n          <code slot=\"code\">val serviceImpl = loadService&lt;Service&gt;()\n\ninline fun &lt;reified T&gt; loadService() {\n    return ServiceLoader.load(T::class.java)\n}</code>\n        </deckgo-highlight-code>\n<ul>\n<li>클래스를 타입 인자로 지정하면 훨씬 이해하기 쉽다.</li>\n</ul>\n<h3 id=\"-1124-실체화된-타입-파라미터가-있는-접근자-정의\" style=\"position:relative;\"><a href=\"#-1124-%EC%8B%A4%EC%B2%B4%ED%99%94%EB%90%9C-%ED%83%80%EC%9E%85-%ED%8C%8C%EB%9D%BC%EB%AF%B8%ED%84%B0%EA%B0%80-%EC%9E%88%EB%8A%94-%EC%A0%91%EA%B7%BC%EC%9E%90-%EC%A0%95%EC%9D%98\" aria-label=\" 1124 실체화된 타입 파라미터가 있는 접근자 정의 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>🔖 11.2.4 실체화된 타입 파라미터가 있는 접근자 정의</h3>\n<deckgo-highlight-code language=\"kotlin\" terminal=\"carbon\" theme=\"one-dark\"  >\n          <code slot=\"code\">inline val &lt;reified T&gt; T.canonical: String\n    get() = T::class.java.canonicalName</code>\n        </deckgo-highlight-code>\n<ul>\n<li>이 프로퍼티는 제네릭 클래스의 표준적인 이름을 반환한다.</li>\n<li>프로퍼티를 <code>inline</code>으로 표시하고 타입 파라미터를 <code>reified</code>로 하면 타입인자에 쓰인 구체적인 클래스를 참조할 수 있다.</li>\n</ul>\n<h3 id=\"-1125-실체화된-타입-파라미터의-제약\" style=\"position:relative;\"><a href=\"#-1125-%EC%8B%A4%EC%B2%B4%ED%99%94%EB%90%9C-%ED%83%80%EC%9E%85-%ED%8C%8C%EB%9D%BC%EB%AF%B8%ED%84%B0%EC%9D%98-%EC%A0%9C%EC%95%BD\" aria-label=\" 1125 실체화된 타입 파라미터의 제약 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>🔖 11.2.5 실체화된 타입 파라미터의 제약</h3>\n<p>실체화된 타입 파라미터 사용할 수 있는 경우</p>\n<ul>\n<li>타입 검사와 캐스팅(is, !is, as, as?)</li>\n<li>10장에서 설명할 코틀린 리플렉션 API(::class)</li>\n<li>코틀린 타입에 대응하는 <code>Java.lang.Class</code>를 얻기(::class, java)</li>\n<li>다른 함수를 호출할 때 타입 인자로 사용</li>\n</ul>\n<p>실체화된 타입 파라미터 사용할 수 없는 경우</p>\n<ul>\n<li>타입 파라미터 클래스의 인스턴스 생성하기</li>\n<li>타입 파라미터 클래스의 동반 객체 메서드 호출하기</li>\n<li>실체화된 타입 파라미터를 요구하는 함수를 호출하면서 실체화하지 않은 타입 파라미터로 받은 타입을 타입 인자로 넘기기</li>\n<li>클래스, 프로퍼티, 인라인 함수가 아닌 함수의 타입 파라미터를 <code>reified</code>로 지정하기</li>\n</ul>\n<h2 id=\"-113-변성은-제네릭과-타입-인자-사이의-하위-타입-관계를-기술\" style=\"position:relative;\"><a href=\"#-113-%EB%B3%80%EC%84%B1%EC%9D%80-%EC%A0%9C%EB%84%A4%EB%A6%AD%EA%B3%BC-%ED%83%80%EC%9E%85-%EC%9D%B8%EC%9E%90-%EC%82%AC%EC%9D%B4%EC%9D%98-%ED%95%98%EC%9C%84-%ED%83%80%EC%9E%85-%EA%B4%80%EA%B3%84%EB%A5%BC-%EA%B8%B0%EC%88%A0\" aria-label=\" 113 변성은 제네릭과 타입 인자 사이의 하위 타입 관계를 기술 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>📖 11.3 변성은 제네릭과 타입 인자 사이의 하위 타입 관계를 기술</h2>\n<ul>\n<li>변성 개념은 기저 타입이 같고 타입 인자가 다른 여러 타입이 서로 어떤 관계가 있는지 설명하는 개념</li>\n</ul>\n<h3 id=\"-1131-변성은-인자를-함수에-넘겨도-안전한지-판단하게-해준다\" style=\"position:relative;\"><a href=\"#-1131-%EB%B3%80%EC%84%B1%EC%9D%80-%EC%9D%B8%EC%9E%90%EB%A5%BC-%ED%95%A8%EC%88%98%EC%97%90-%EB%84%98%EA%B2%A8%EB%8F%84-%EC%95%88%EC%A0%84%ED%95%9C%EC%A7%80-%ED%8C%90%EB%8B%A8%ED%95%98%EA%B2%8C-%ED%95%B4%EC%A4%80%EB%8B%A4\" aria-label=\" 1131 변성은 인자를 함수에 넘겨도 안전한지 판단하게 해준다 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>🔖 11.3.1 변성은 인자를 함수에 넘겨도 안전한지 판단하게 해준다</h3>\n<deckgo-highlight-code language=\"kotlin\" terminal=\"carbon\" theme=\"one-dark\"  >\n          <code slot=\"code\">fun printContents(list: List&lt;Any&gt;) {\n    println(list.joinToString())\n}\n\nfun main() {\n    printContents(listOf(&quot;abc&quot;, &quot;bac&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 addAnswer(list: MutableList&lt;Any&gt;) {\n    list.add(42)\n}\n\nfun main() {\n    val strings = mutableListOf(&quot;abc&quot;, &quot;bac&quot;)\n    addAnswer(strings) // error\n    println(strings.maxBy { it.length })\n}</code>\n        </deckgo-highlight-code>\n<ul>\n<li><code>List&#x3C;Any></code>타입의 파라미터를 받는 함수에 <code>List&#x3C;String></code>을 넘길 수 없다.</li>\n<li>다만, 원소 추가나 변경이 없는 경우에는 안전하다.</li>\n</ul>\n<h3 id=\"-1132-클래스-타입-하위-타입\" style=\"position:relative;\"><a href=\"#-1132-%ED%81%B4%EB%9E%98%EC%8A%A4-%ED%83%80%EC%9E%85-%ED%95%98%EC%9C%84-%ED%83%80%EC%9E%85\" aria-label=\" 1132 클래스 타입 하위 타입 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>🔖 11.3.2 클래스, 타입, 하위 타입</h3>\n<ul>\n<li>제네릭 클래스가 아닌 클래스에서는 클래스 이름을 바로 타입으로 쓸 수 있다.</li>\n<li>모든 클래스가 적어도 둘 이상의 타입을 구성할 수 있다.</li>\n<li>제네릭 클래스에서는 올바른 타입을 얻으려면 제네릭 타입의 타입 파라미터를 구체적인 타입 인자로 바꿔줘야 한다.</li>\n<li>어떤 타입 A의 값이 필요한 모든 장소에 어떤 타입 B의 값을 넣어도 아무 문제가 없다면 타입 B는 타입 A의 하위 타입이다.</li>\n<li>A 타입이 B 타입의 하위 타입이라면 B는 A의 상위 타입이다.</li>\n</ul>\n<deckgo-highlight-code language=\"kotlin\" terminal=\"carbon\" theme=\"one-dark\"  >\n          <code slot=\"code\">fun test(i: Int) {\n    val n: Number = i\n    \n    fun f(s: String) {\n        //..\n    }\n    f(i) // error\n}</code>\n        </deckgo-highlight-code>\n<ul>\n<li>어떤 값의 타입이 변수 타입의 하위 타입인 경우에만 값을 변수에 대입하도록 허용한다.</li>\n<li>하위 타입은 하위 클래스와 근본적으로 같다.</li>\n<li>널이 될 수 없는 타입은 널이 될 수 있는 타입의 하위 타입이다.</li>\n<li>어떤 제네릭 타입(<code>MutableList</code>)이 있는데 서로 다른 두 타입 A와 B에 대해 <code>MutableList&#x3C;A></code>가 항상 <code>MutableList&#x3C;B></code>의 하위 타입도 아니고 상위 타입도 아닌 경우에 이 제네릭 타입이 타입 파라미터에 대해 무공변이라고 말한다.\n<ul>\n<li>자바에서는 모든 클래스가 무공변이다.</li>\n</ul>\n</li>\n</ul>\n<h3 id=\"-1133-공변성은-하위-타입-관계를-유지한다\" style=\"position:relative;\"><a href=\"#-1133-%EA%B3%B5%EB%B3%80%EC%84%B1%EC%9D%80-%ED%95%98%EC%9C%84-%ED%83%80%EC%9E%85-%EA%B4%80%EA%B3%84%EB%A5%BC-%EC%9C%A0%EC%A7%80%ED%95%9C%EB%8B%A4\" aria-label=\" 1133 공변성은 하위 타입 관계를 유지한다 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>🔖 11.3.3 공변성은 하위 타입 관계를 유지한다</h3>\n<ul>\n<li>공변적인 클래스는 제네릭 클래스(<code>Producer&#x3C;T></code>)에 대해 A가 B의 하위 타입일 때 <code>Producer&#x3C;A></code>가 <code>Producer&#x3C;B></code>의 하위 타입인 경우를 말한다.\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\">interface Producer&lt;out T&gt; {\n    fun produce(): T\n}</code>\n        </deckgo-highlight-code>\n<ul>\n<li>코틀린에서 제네릭 클래스가 타입 파라미터에 대해 공변적임을 표시하려면 타입 파라미터 이름 앞에 <code>out</code>을 넣어야 한다.</li>\n<li>클래스의 타입 파라미터를 공변적으로 만들면 함수 정의에 사용한 파라미터 타입과 타입 인자의 타입이 정확히 일치하지 않더라도 그 클래스의 인스턴스를 함수 인자나 반환값으로 사용할 수 있다.</li>\n</ul>\n<deckgo-highlight-code language=\"kotlin\" terminal=\"carbon\" theme=\"one-dark\"  >\n          <code slot=\"code\">open class Animal {\n    fun feed() {\n        //\n    }\n}\n\nclass Herd&lt;T: Animal&gt; {\n    val size: Int get() = 5\n    operator fun get(i: Int): T {\n        //\n    }\n}\n\nfun feedAll(animals: Herd&lt;Animal&gt;) {\n    for (i in 0..&lt;animals.size) {\n        animals[i].feed()\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 Cat: Animal() {\n    fun cleanLitter() {\n        //\n    }\n}\n\nfun takeCareOfCats(cats: Herd&lt;Cat&gt;) {\n    for (i in 0..&lt;cats.size) {\n        cats[i].cleanLitter()\n    }\n    feedAll(cats) // error\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 Herd&lt;out T: Animal&gt; {\n    val size: Int get() = 5\n    operator fun get(i: Int): T {\n        //\n    }\n}\n\nfun takeCareOfCats(cats: Herd&lt;Cat&gt;) {\n  for (i in 0..&lt;cats.size) {\n    cats[i].cleanLitter()\n  }\n  feedAll(cats)\n}</code>\n        </deckgo-highlight-code>\n<ul>\n<li>캐스팅을 하지 않고도 에러가 발생하지 않는다.</li>\n<li>타입 파라미터를 공변적으로 지정하면 클래스 내부에서 그 파라미터를 사용하는 방법을 제한한다.</li>\n<li>타입 안전성을 보장하기 위해 공변적 파라미터는 항상 out 위치에만 있어야 한다.\n<ul>\n<li>클래스가 T 타입의 값을 생산할 수는 있지만 소비할 수 없다는 뜻</li>\n</ul>\n</li>\n<li>클래스 멤버를 선언할 때 타입 파라미터를 사용할 수 있는 지점은 모두 인과 아웃 위치로 나뉜다.\n<ul>\n<li>함수의 반환 타입: 아웃 위치</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\">interface MutableList&lt;T&gt; : List&lt;T&gt;, MutableCollection&lt;T&gt; {\n    override fun add(element: T): Boolean\n}</code>\n        </deckgo-highlight-code>\n<ul>\n<li><code>MutableList</code>는 T에 대해 공변적일 수 없다. T가 인 위치에 쓰이기 때문이다.</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 Herd&lt;T: Animal&gt;(var leadAnimal: T, vararg animals: T) {}</code>\n        </deckgo-highlight-code>\n<ul>\n<li><code>leadAnimal</code> 프로퍼티가 인 위치에 있기 때문에 T를 <code>out</code>으로 표시할 수 없다.</li>\n<li>이러한 위치 규칙은 외부에서 볼 수 있는 API에만 적용할 수 있다.</li>\n</ul>\n<deckgo-highlight-code language=\"kotlin\" terminal=\"carbon\" theme=\"one-dark\"  >\n          <code slot=\"code\">class Herd&lt;out T: Animal&gt;(private var leadAnimal: T, vararg animals: T) {}</code>\n        </deckgo-highlight-code>\n<ul>\n<li>private 메서드의 파라미터는 인도 아니고 아웃도 아닌 위치다.</li>\n<li><code>Herd</code>를 T에 대해 공변적으로 선언해도 안전하다.</li>\n</ul>\n<h3 id=\"-1134-반공변성은-하위-타입-관계를-뒤집는다\" style=\"position:relative;\"><a href=\"#-1134-%EB%B0%98%EA%B3%B5%EB%B3%80%EC%84%B1%EC%9D%80-%ED%95%98%EC%9C%84-%ED%83%80%EC%9E%85-%EA%B4%80%EA%B3%84%EB%A5%BC-%EB%92%A4%EC%A7%91%EB%8A%94%EB%8B%A4\" aria-label=\" 1134 반공변성은 하위 타입 관계를 뒤집는다 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>🔖 11.3.4 반공변성은 하위 타입 관계를 뒤집는다</h3>\n<ul>\n<li>반공변 클래스의 하위 타입 관계는 그 클래스의 타입 파라미터의 상하위 타입 관계와 반대다.</li>\n</ul>\n<deckgo-highlight-code language=\"kotlin\" terminal=\"carbon\" theme=\"one-dark\"  >\n          <code slot=\"code\">sealed class Fruit {\n    abstract val weight: Int\n}\n\ndata class Apple(override val weight: Int, val color: String) : Fruit()\n\ndata class Orange(override val weight: Int, val juicy: Boolean) : Fruit()\n\nfun main() {\n  val weightComparator = Comparator&lt;Fruit&gt; {\n      a, b -&gt; a.weight - b.weight\n  }\n  val fruits: List&lt;Fruit&gt; = listOf(Apple(100, &quot;green&quot;), Orange(180, true))\n  val apples: List&lt;Apple&gt; = listOf(Apple(50, &quot;red&quot;), Apple(120, &quot;green&quot;) ,Apple(155, &quot;yellow&quot;))\n  println(fruits.sortedWith(weightComparator))\n  println(apples.sortedWith(weightComparator))\n}</code>\n        </deckgo-highlight-code>\n<ul>\n<li><code>sortedWith</code> 함수는 <code>Comparator&#x3C;String></code>을 요구하므로 일반적인 타입을 비교할 수 있는 <code>Comparator</code>를 넘겨도 안전하다.</li>\n<li>어떤 타입의 객체를 <code>Comparator</code>로 비교해야 한다면 그 타입이나 그 타입의 조상 타입을 비교할 수 있는 <code>Comparator</code>를 사용할 수 있다.</li>\n<li>반공변성: 어떤 클래스(<code>Consumer&#x3C;T></code>)에 대해 타입 B가 타입 A의 하위 타입일 때 <code>Consumer&#x3C;A></code>가 <code>Consumer&#x3C;B></code>의 하위 타입인 관계가 성립하면 제네릭 클래스는 타입 인자 T에 대해 반공변이다.</li>\n<li>in 이라는 키워드는 그 키워드가 붙은 타입이 이 클래스의 메서드 안으로 전달돼 메서드에 의해 소비된다는 뜻</li>\n</ul>\n<h3 id=\"-1135-사용-지점-변성을-사용해-타입이-언급되는-지점에서-변성-지정\" style=\"position:relative;\"><a href=\"#-1135-%EC%82%AC%EC%9A%A9-%EC%A7%80%EC%A0%90-%EB%B3%80%EC%84%B1%EC%9D%84-%EC%82%AC%EC%9A%A9%ED%95%B4-%ED%83%80%EC%9E%85%EC%9D%B4-%EC%96%B8%EA%B8%89%EB%90%98%EB%8A%94-%EC%A7%80%EC%A0%90%EC%97%90%EC%84%9C-%EB%B3%80%EC%84%B1-%EC%A7%80%EC%A0%95\" aria-label=\" 1135 사용 지점 변성을 사용해 타입이 언급되는 지점에서 변성 지정 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>🔖 11.3.5 사용 지점 변성을 사용해 타입이 언급되는 지점에서 변성 지정</h3>\n<ul>\n<li>클래스를 선언하면서 변성을 지정하면 그 클래스를 사용하는 모든 장소에 변성 지정자가 영향을 끼치므로 편리하다.\n<ul>\n<li>선언 지점 변성이라고 함.</li>\n</ul>\n</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\">fun &lt;T&gt; copyData(source: MutableList&lt;T&gt;, destination: MutableList&lt;T&gt;) {\n    for (item in source) destination.add(item)\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\">fun &lt;T: R, R&gt; copyData(source: MutableList&lt;T&gt;, destination: MutableList&lt;R&gt;) {\n    for (item in source) destination.add(item)\n}\n\nfun main() {\n    val ints = mutableListOf(1, 2, 3)\n    val anyItems = mutableListOf&lt;Any&gt;()\n    copyData(ints, anyItems)\n    println(anyItems)\n}</code>\n        </deckgo-highlight-code>\n<ul>\n<li><code>int</code>가 <code>Any</code>의 하위 타입이기 때문에 정상 동작</li>\n</ul>\n<deckgo-highlight-code language=\"kotlin\" terminal=\"carbon\" theme=\"one-dark\"  >\n          <code slot=\"code\">fun &lt;T&gt; copyData(source: MutableList&lt;out T&gt;, destination: MutableList&lt;T&gt;) {\n  for (item in source) destination.add(item)\n}</code>\n        </deckgo-highlight-code>\n<ul>\n<li>out 키워드를 타입을 사용하는 위치 앞에 붙이면 T 타입을 in 위치에 사용하는 메서드를 호출하지 않는다는 뜻</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\">fun main() {\n    val list: MutableList&lt;out Number&gt; = mutableListOf()\n    list.add(42) // 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 &lt;T&gt; copyData(source: MutableList&lt;T&gt;, destination: MutableList&lt;in T&gt;) {\n    for (item in source) destination.add(item)\n}</code>\n        </deckgo-highlight-code>\n<ul>\n<li>원본 리스트 원소 타입의 상위 타입을 대상 리스트 원소 타입으로 허용</li>\n</ul>\n<h3 id=\"-1136-스타-프로젝션-제네릭-타입-인자에-대한-정보가-없음을-표현하고자--사용\" style=\"position:relative;\"><a href=\"#-1136-%EC%8A%A4%ED%83%80-%ED%94%84%EB%A1%9C%EC%A0%9D%EC%85%98-%EC%A0%9C%EB%84%A4%EB%A6%AD-%ED%83%80%EC%9E%85-%EC%9D%B8%EC%9E%90%EC%97%90-%EB%8C%80%ED%95%9C-%EC%A0%95%EB%B3%B4%EA%B0%80-%EC%97%86%EC%9D%8C%EC%9D%84-%ED%91%9C%ED%98%84%ED%95%98%EA%B3%A0%EC%9E%90--%EC%82%AC%EC%9A%A9\" aria-label=\" 1136 스타 프로젝션 제네릭 타입 인자에 대한 정보가 없음을 표현하고자  사용 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>🔖 11.3.6 스타 프로젝션: 제네릭 타입 인자에 대한 정보가 없음을 표현하고자 * 사용</h3>\n<ul>\n<li><code>MutableList&#x3C;*></code>는 <code>MutableList&#x3C;Any?></code>와 같지 않다.\n<ul>\n<li><code>MutableList&#x3C;*></code>는 어떤 정해진 구체적인 타입의 원소만을 담는 리스트지만 그 원소의 타입을 정확히 모른다는 사실을 표현</li>\n<li><code>MutableList&#x3C;Any?></code>는 모든 타입의 원소를 담을 수 있음을 알 수 있는 리스트다.</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 list: MutableList&lt;Any?&gt; = mutableListOf(&#39;a&#39;, 1, &quot;qwe&quot;)\n  val chars = mutableListOf(&#39;a&#39;, &#39;b&#39;, &#39;c&#39;)\n  val unknownElements: MutableList&lt;*&gt; = if (Random.nextBoolean()) list else chars\n  println(unknownElements.first())\n  unknownElements.add(42) // error\n}</code>\n        </deckgo-highlight-code>\n<ul>\n<li>이 맥락에서 <code>MutableList&#x3C;*></code>는 <code>MutableList&#x3C;out Any?></code>처럼 프로젝션된다.</li>\n<li>원소 타입을 모르더라도 안전하게 원소를 꺼내올 수는 있지만 타입을 모르는 리스트에 원소를 마음대로 넣을 수는 없다.</li>\n</ul>\n<deckgo-highlight-code language=\"kotlin\" terminal=\"carbon\" theme=\"one-dark\"  >\n          <code slot=\"code\">fun printFirst(list: List&lt;*&gt;) {\n    if (list.isNotEmpty()) {\n        println(list.first())\n    }\n}\n\nfun main() {\n    printFirst(listOf(&quot;Sveta&quot;, &quot;Seb&quot;, &quot;Dima&quot;, &quot;Roman&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 &lt;T&gt; printFirst(list: List&lt;T&gt;) {\n    if (list.isNotEmpty()) {\n        println(list.first())\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\">interface FieldValidator&lt;in T&gt; {\n    fun validate(input: T): Boolean\n}\n\nobject DefaultStringValidator : FieldValidator&lt;String&gt; {\n    override fun validate(input: String) = input.isNotEmpty()\n}\n\nobject DefaultIntValidator : FieldValidator&lt;Int&gt; {\n    override fun validate(input: Int) = input &gt;= 0\n}\n\nfun main() {\n  val validator = mutableMapOf&lt;KClass&lt;*&gt;, FieldValidator&lt;*&gt;&gt;()\n  validator[String::class] = DefaultStringValidator\n  validator[Int::class] = DefaultIntValidator\n}</code>\n        </deckgo-highlight-code>\n<ul>\n<li>String 타입의 필드를 <code>FieldValidator&#x3C;*></code> 타입의 검증기로 검증할 수 없기 때문에 문제가 생긴다.</li>\n</ul>\n<deckgo-highlight-code language=\"kotlin\" terminal=\"carbon\" theme=\"one-dark\"  >\n          <code slot=\"code\">    val stringValidator = validator[String::class] as FieldValidator&lt;String&gt;</code>\n        </deckgo-highlight-code>\n<ul>\n<li>명시적 타입 캐스팅을 사용하면 사용할 수 있지만 warning이 발생한다.</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\">fun main() {\n    val validator = mutableMapOf&lt;KClass&lt;*&gt;, FieldValidator&lt;*&gt;&gt;()\n    val stringValidator = validator[Int::class] as FieldValidator&lt;String&gt;\n    stringValidator.validate(&quot;&quot;) // 실행 시점 에러\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\">object Validators {\n  private val validators = mutableMapOf&lt;KClass&lt;*&gt;, FieldValidator&lt;*&gt;&gt;()\n\n  fun &lt;T : Any&gt; registerValidator(kClass: KClass&lt;T&gt;, fieldValidator: FieldValidator&lt;T&gt;) {\n    validators[kClass] = fieldValidator\n  }\n\n  @Suppress(&quot;UNCHECKED_CAST&quot;)\n  operator fun &lt;T : Any&gt; get(kClass: KClass&lt;T&gt;): FieldValidator&lt;T&gt; =\n    validators[kClass] as? FieldValidator&lt;T&gt; ?: throw IllegalArgumentException(\n      &quot;No validator for ${kClass.simpleName}&quot;\n    )\n}\n\nfun main() {\n  Validators.registerValidator(String::class, DefaultStringValidator)\n  Validators.registerValidator(Int::class, DefaultIntValidator)\n}</code>\n        </deckgo-highlight-code>\n<ul>\n<li>타입 안전성을 보장하는 API이다.</li>\n<li>이 패턴을 모든 커스텀 제네릭 클래스를 저장할 때 사용할 수 있게 확장할 수도 있다.</li>\n</ul>\n<h3 id=\"-1137-타입-설명\" style=\"position:relative;\"><a href=\"#-1137-%ED%83%80%EC%9E%85-%EC%84%A4%EB%AA%85\" aria-label=\" 1137 타입 설명 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>🔖 11.3.7 타입 설명</h3>\n<ul>\n<li>타입 별명은 기존 타입에 대해 다른 이름을 부여한다.</li>\n<li><code>typealias</code> 키워드 뒤에 별명을 적어 타입 별명 선언을 시작할 수 있다.</li>\n<li>긴 제네릭 타입을 짧게 부를 때 유용하다.</li>\n</ul>\n<deckgo-highlight-code language=\"kotlin\" terminal=\"carbon\" theme=\"one-dark\"  >\n          <code slot=\"code\">typealias NameCombiner = (String, String, String, String) -&gt; String\n\nval authorsCombiner: NameCombiner = { a, b, c, d -&gt; &quot;$a et al.&quot; }\nval bandCombiner: NameCombiner = { a, b, c, d -&gt; &quot;$a, $b &amp; The Gang&quot; }\n\nfun combineAuthors(combiner: NameCombiner) {\n    println(combiner(&quot;Sveta&quot;, &quot;Seb&quot;, &quot;Dima&quot;, &quot;Roman&quot;))\n}\n\nfun main() {\n    combineAuthors(bandCombiner)\n    combineAuthors(authorsCombiner)\n    combineAuthors { a, b, c, d -&gt; &quot;$d, $c &amp; Co.&quot; }\n}</code>\n        </deckgo-highlight-code>\n<ul>\n<li>타입 별명을 도입하면 코드를 읽을 때 좀 더 쉽게 이해가 되도록 함수형 타입에 새로운 맥락을 부여할 수 있다.</li>\n</ul>","excerpt":"📖 11.1 타입 인자를 받는 타입 만들기: 제네릭 타입 파라미터 제네릭스를 사용하면 타입 파라미터를 받는 타입을 정의할 수 있다. 제네릭 타입의 인스턴스가 만들어질 때는 타입 파라미터를 구체적인 타입 인자로 치환한다. 코틀린 컴파일러는 보통 타입과 마찬가지로 타입 인자도 추론할 수 있다. 위 두 선언은 동등하다. 빈 리스트를 만들어야 한다면 타입 인자를 추론할 근거가 없기에 직접 명시해야 한다. 코틀린에는 raw 타입이 없다. 🔖 11.1.…","fields":{"slug":"/backend/kotlin-in-action/11장-제네릭스/"},"frontmatter":{"title":"Kotlin in Action - 11장 제네릭스","thumbnail":{"childImageSharp":{"fluid":{"src":"/static/f7f7ddfa31d1614405dc5af1487b9ec4/9c94d/kotlin-in-action.png"}}},"draft":false,"category":"Back-End","tags":["Kotlin"],"date":"May 18, 2025"}}}},"staticQueryHashes":["2374173507","2996537568","3691437124"]}