Skip to content

Commit

Permalink
1.2.1. 제네릭 함수와 프로퍼티
Browse files Browse the repository at this point in the history
  • Loading branch information
assu10 committed Oct 13, 2024
1 parent 91996b3 commit 82191db
Show file tree
Hide file tree
Showing 3 changed files with 143 additions and 0 deletions.
97 changes: 97 additions & 0 deletions _posts/2024-03-17-kotlin-advanced-2.md
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,8 @@ tags: kotlin generics filterIsInstance() typeParameter typeErasure reified kClas
* [1. 제네릭스](#1-제네릭스)
* [1.1. `Any`](#11-any)
* [1.2. 제네릭스 정의: `<>`](#12-제네릭스-정의-)
* [1.2.1. 제네릭 함수와 프로퍼티](#121-제네릭-함수와-프로퍼티)
* [1.2.2. 제네릭 클래스 선언](#122-제네릭-클래스-선언)
* [1.3. 타입 정보 보존](#13-타입-정보-보존)
* [1.4. 제네릭 확장 함수](#14-제네릭-확장-함수)
* [1.5. 타입 파라메터 제약: `filterIsInstance()`](#15-타입-파라메터-제약-filterisinstance)
Expand Down Expand Up @@ -221,6 +223,101 @@ fun basicGenerics() {

---

### 1.2.1. 제네릭 함수와 프로퍼티

리스트를 다루는 함수 작성 시 특정 타입을 저장하는 리스트 뿐 아니라 모든 리스트 (= 제네릭 리스트) 를 다룰 수 있는 함수가 더 유용하다.
이럴 때 제네릭 함수를 사용한다.

**제네릭 함수를 호출할 때는 반드시 구체적 타입으로 타입 인자**를 넘겨야 한다.

대부분의 컬렉션 라이브러리 함수는 제네릭 함수이다.
아래는 `slice()` 의 시그니처이다.

```kotlin
public fun <T> List<T>.slice(indices: IntRange): List<T>
```

`slice()` 함수는 구체적인 범위 안에 든 원소만을 포함하는 새로운 리스트를 반환한다.

![제네릭 함수 `slice()``T` 를 타입 파라메터로 받음](/assets/img/dev/2024/0317/type.png)

위를 보면 수신 객체와 반환 타입은 모두 `List<T>` 로, 함수의 타입 파라메터 `T` 가 수신 객체와 반환 타입으로 사용되고 있다.

아래는 `filter()` 의 시그니처이다.

```kotlin
public inline fun <T> Iterable<T>.filter(predicate: (T) -> Boolean): List<T>
```

`filter()``(T) -> Boolean` 타입의 함수를 파라메터로 받는다.

아래는 제네릭 고차 함수를 호출하는 예시이다.

> 고차 함수에 관한 내용은 [1. 고차 함수 (high-order function)](https://assu10.github.io/dev/2024/02/17/kotlin-funtional-programming-2/#1-%EA%B3%A0%EC%B0%A8-%ED%95%A8%EC%88%98-high-order-function) 를 참고하세요.
```kotlin
package com.assu.study.kotlin2me.chap09

fun main() {
val authors = listOf("Assu", "Silby")
val readers = mutableListOf<String>()

readers.add("Jaehoon")
readers.add("Assu")

val result = readers.filter { it !in authors }

// [Jaehoon]
println(result)
}
```

위에서 람다 파라메터에 대해 자동으로 만들어진 변수 `it` 의 타입은 `T` 라는 제네릭 타입이다.
(여기서 `T` 는 함수 파라메터 타입인 `(T) -> Boolean` 에서 온 타입임)

---

제네릭 함수를 정의할 때와 마찬가지 방법으로 **제네릭 확장 프로퍼티**를 선언할 수 있다.

아래는 리스트의 마지막 원소 바로 앞에 있는 원소를 반환하는 확장 프로퍼티 예시이다.

```kotlin
package com.assu.study.kotlin2me.chap09

// 제네릭 확장 프로퍼티
// 모든 리스트 타입에 이 제네릭 확장 프로퍼티 사용 가능
val <T> List<T>.pre: T
get() = this[size - 2]

fun main() {
// 제네릭 확장 프로퍼티 사용
// 이 호출에서 타입 파라메터 T 는 int 로 추론됨
println(listOf(1, 2, 3).pre)
}
```

확장이 아닌 일반 프로퍼티는 타입 파라메터를 가질 수 없다.
즉, **확장 프로퍼티만 제네릭하게 만들 수 있다**는 의미이다.

클래스 프로퍼티에 여러 타입의 값을 저장할 수는 없으므로 제네릭한 일반 프로퍼티는 말이 되지 않는다.

확장 프로퍼티가 아닌 일반 프로퍼티를 제네릭하게 선언하면 아래와 같은 오류가 발생한다.

```kotlin
// 컴파일 오류
// Type parameter of a property must be used in its receiver type

// 확장 프로퍼티가 아닌 일반 프로터티는 제네릭하게 만들 수 없음

//val <T> x: T = TODO()
```

---

### 1.2.2. 제네릭 클래스 선언

---

## 1.3. 타입 정보 보존

[1.6. 타입 소거 (type erasure)](#16-타입-소거-type-erasure) 에 나오는 내용이지만, **제네릭 클래스나 제네릭 함수의 내부 코드는 T 타입에 대해 알 수 없다.**
Expand Down
46 changes: 46 additions & 0 deletions assets/img/dev/2024/0317/type.drawio
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
<mxfile host="Electron" agent="Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) draw.io/24.7.17 Chrome/128.0.6613.36 Electron/32.0.1 Safari/537.36" version="24.7.17">
<diagram name="Page-1" id="BcevsgVmTdfs1AKpiNvQ">
<mxGraphModel dx="1114" dy="821" grid="1" gridSize="10" guides="1" tooltips="1" connect="1" arrows="1" fold="1" page="1" pageScale="1" pageWidth="827" pageHeight="1169" math="0" shadow="0">
<root>
<mxCell id="0" />
<mxCell id="1" parent="0" />
<mxCell id="T4-VnGGA1DKJHZXx6yKn-1" value="&lt;div style=&quot;background-color:#ffffff;color:#080808&quot;&gt;&lt;pre style=&quot;font-family:&#39;IBM Plex Mono&#39;,monospace;font-size:9.8pt;&quot;&gt;&lt;span style=&quot;color:#0033b3;&quot;&gt;public fun &lt;/span&gt;&lt;span style=&quot;color:#3f9101;&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span style=&quot;color:#007e8a;&quot;&gt;T&lt;/span&gt;&lt;span style=&quot;color:#3f9101;&quot;&gt;&amp;gt; &lt;/span&gt;&lt;span style=&quot;color:#000000;&quot;&gt;List&lt;/span&gt;&lt;span style=&quot;color:#3f9101;&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span style=&quot;color:#007e8a;&quot;&gt;T&lt;/span&gt;&lt;span style=&quot;color:#3f9101;&quot;&gt;&amp;gt;&lt;/span&gt;.&lt;span style=&quot;color:#00627a;&quot;&gt;slice&lt;/span&gt;&lt;span style=&quot;color:#3f9101;&quot;&gt;(&lt;/span&gt;&lt;span style=&quot;color:#000000;&quot;&gt;indices&lt;/span&gt;: &lt;span style=&quot;color:#000000;&quot;&gt;IntRange&lt;/span&gt;&lt;span style=&quot;color:#3f9101;&quot;&gt;)&lt;/span&gt;: &lt;span style=&quot;color:#000000;&quot;&gt;List&lt;/span&gt;&lt;span style=&quot;color:#3f9101;&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span style=&quot;color:#007e8a;&quot;&gt;T&lt;/span&gt;&lt;span style=&quot;color:#3f9101;&quot;&gt;&amp;gt;&lt;/span&gt;&lt;/pre&gt;&lt;/div&gt;" style="text;whiteSpace=wrap;html=1;" vertex="1" parent="1">
<mxGeometry x="100" y="190" width="470" height="50" as="geometry" />
</mxCell>
<mxCell id="T4-VnGGA1DKJHZXx6yKn-2" value="" style="shape=curlyBracket;whiteSpace=wrap;html=1;rounded=1;labelPosition=left;verticalLabelPosition=middle;align=right;verticalAlign=middle;rotation=-90;" vertex="1" parent="1">
<mxGeometry x="189.5" y="224.5" width="20" height="25" as="geometry" />
</mxCell>
<mxCell id="T4-VnGGA1DKJHZXx6yKn-3" value="타입 파라메터 선언" style="text;html=1;align=center;verticalAlign=middle;whiteSpace=wrap;rounded=0;fillColor=#f5f5f5;fontColor=#333333;strokeColor=none;" vertex="1" parent="1">
<mxGeometry x="144.5" y="250" width="110" height="20" as="geometry" />
</mxCell>
<mxCell id="T4-VnGGA1DKJHZXx6yKn-6" value="" style="shape=curlyBracket;whiteSpace=wrap;html=1;rounded=1;labelPosition=left;verticalLabelPosition=middle;align=right;verticalAlign=middle;rotation=90;" vertex="1" parent="1">
<mxGeometry x="251.5" y="184" width="20" height="25" as="geometry" />
</mxCell>
<mxCell id="T4-VnGGA1DKJHZXx6yKn-7" value="" style="shape=curlyBracket;whiteSpace=wrap;html=1;rounded=1;labelPosition=left;verticalLabelPosition=middle;align=right;verticalAlign=middle;rotation=90;" vertex="1" parent="1">
<mxGeometry x="518" y="186" width="20" height="25" as="geometry" />
</mxCell>
<mxCell id="T4-VnGGA1DKJHZXx6yKn-8" value="타입 파라메터가 수신 객체와 반환 타입에 사용됨" style="text;html=1;align=center;verticalAlign=middle;whiteSpace=wrap;rounded=0;fillColor=#f5f5f5;fontColor=#333333;strokeColor=none;" vertex="1" parent="1">
<mxGeometry x="254.5" y="130" width="270" height="30" as="geometry" />
</mxCell>
<mxCell id="T4-VnGGA1DKJHZXx6yKn-9" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;entryX=0.083;entryY=0.867;entryDx=0;entryDy=0;entryPerimeter=0;curved=1;" edge="1" parent="1" source="T4-VnGGA1DKJHZXx6yKn-6" target="T4-VnGGA1DKJHZXx6yKn-8">
<mxGeometry relative="1" as="geometry">
<Array as="points">
<mxPoint x="262" y="180" />
<mxPoint x="277" y="180" />
</Array>
</mxGeometry>
</mxCell>
<mxCell id="T4-VnGGA1DKJHZXx6yKn-10" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;curved=1;" edge="1" parent="1">
<mxGeometry relative="1" as="geometry">
<mxPoint x="528" y="191.5" as="sourcePoint" />
<mxPoint x="504" y="153" as="targetPoint" />
<Array as="points">
<mxPoint x="528" y="183" />
<mxPoint x="504" y="183" />
</Array>
</mxGeometry>
</mxCell>
</root>
</mxGraphModel>
</diagram>
</mxfile>
Binary file added assets/img/dev/2024/0317/type.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.

0 comments on commit 82191db

Please sign in to comment.