<?xml version="1.0" encoding="utf-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom">
    <channel>
        <title>sungjun-jin.log</title>
        <link>https://velog.io/</link>
        <description>주니어 개발쟈🤦‍♂️</description>
        <lastBuildDate>Sun, 21 Nov 2021 09:05:38 GMT</lastBuildDate>
        <docs>https://validator.w3.org/feed/docs/rss2.html</docs>
        <generator>https://github.com/jpmonette/feed</generator>
        <copyright>Copyright (C) 2019. sungjun-jin.log. All rights reserved.</copyright>
        <atom:link href="https://v2.velog.io/rss/sungjun-jin" rel="self" type="application/rss+xml"/>
        <item>
            <title><![CDATA[ControllerAdvice - kotlin]]></title>
            <link>https://velog.io/@sungjun-jin/ControllerAdvice-kotlin</link>
            <guid>https://velog.io/@sungjun-jin/ControllerAdvice-kotlin</guid>
            <pubDate>Sun, 21 Nov 2021 09:05:38 GMT</pubDate>
            <description><![CDATA[<p>스프링 애플리케이션에서 Controller 내에서 글로벌하게 발생하는 예외를  <code>ControllerAdvice</code>를 사용해 핸들링 할 수 있다. </p>
<p>우선 <em>ControllerAdvice</em>란?</p>
<blockquote>
<p>특정 패키지나 컨트롤러, 나아가서 컨트롤러 단에서 글로벌하게 발생하는 예외를 공통적으로 처리할 수 있는 어노테이션이다. </p>
</blockquote>
<p>우선 실습을 위해 컨트롤러를 생성해보자.</p>
<pre><code class="language-kotlin">@RestController
@RequestMapping(&quot;/api/exception&quot;)
class ExceptionApiController {

    @GetMapping(&quot;/test&quot;)
    fun controllerAdvice() {
        val list = mutableListOf&lt;String&gt;()
        val temp = list[0]
    }
}</code></pre>
<p>일부로 예외를 내도록 빈 리스트에서 0번째 인덱스를 가져오게 해보자. <code>IndexOutOfBoundsException</code> exception이 발생함을 예측할 수 있다.</p>
<p>로컬에서 서버를 구동시킨 다음에 Postman으로 방금 작성한 api를 호출해보자.</p>
<p><img src="https://images.velog.io/images/sungjun-jin/post/86fa9dff-6569-4d66-90ad-62857a00ea43/image.png" alt=""></p>
<p>역시나 기본 <code>500, Internal Server Error</code>가 발생한다. 로그를 확인해보면 전형적으로 핸들링 처리가 되지 않은 인덱스 에러가 발생함을 확인할 수 있다.</p>
<p><img src="https://images.velog.io/images/sungjun-jin/post/671c4f95-eeb7-4d35-afc0-2ab1d6f294b2/image.png" alt=""></p>
<p>이제 ControllerAdvice 어노테이션을 사용해 발생한 위 예외를 핸들링해보자.</p>
<p><code>advice</code> 패키지를 생성해 <code>GlobalControllerAdvice</code> 클래스를 생성한다</p>
<pre><code class="language-kotlin">@RestControllerAdvice
class GlobalControllerAdvice {
    @ExceptionHandler(value = [IndexOutOfBoundsException::class])
    fun indexOutOfBoundException(e: IndexOutOfBoundsException): String {
        return &quot;Index Error 발생&quot;
    }
}</code></pre>
<ul>
<li><code>@RestControllerAdvice</code> 어노테이션을 달아주면 해당 클래스는 <code>ControllerAdvice</code>의 역할을 하게 된다. </li>
<li>클래스 내의 함수를 작성하고 <code>@ExceptionHandler</code> 어노테이션을 사용한다 <code>value</code> 안에는 따로 핸들링하고 싶은  <code>Exception</code>을 <code>Array</code>로 받는다. 여기서는 <code>IndexOutOfBoundsException</code>만 필요하므로 위 같이 작성해준다. </li>
<li>두 종류 이상의 <code>Exception</code>을 처리하고 싶다면 <code>@ExceptionHandler</code>를 아래와 같이 작성할 수 있다.<pre><code class="language-kotlin">@ExceptionHandler(value = [IndexOutOfBoundsException::class, RuntimeException::class])</code></pre>
</li>
</ul>
<p>서버를 다시 재가동 시킨 후 똑같이 api를 호출해보자.</p>
<p><img src="https://images.velog.io/images/sungjun-jin/post/bcd9cb6d-c3b1-4534-a7f8-3c9e3b813222/image.png" alt=""></p>
<p><code>IndexOutOfBoundsException</code>가 따로 핸들링 되었다. 하지만 200 응답을 내주는건 조금 이상하니까 따로 500용 <code>ResponseEntity</code>를 반환해서 처리해주자.</p>
<pre><code class="language-kotlin">@RestControllerAdvice
class GlobalControllerAdvice {
    @ExceptionHandler(value = [IndexOutOfBoundsException::class])
    fun indexOutOfBoundException(e: IndexOutOfBoundsException): ResponseEntity&lt;String&gt;{
        return ResponseEntity.internalServerError().body(&quot;Index Error 발생&quot;)
    }
}</code></pre>
<p>그리고 다시 호출해보면 아래와 같이 500 response code와 함께 핸들링한 메세지로 응답이 오는 부분을 확인할 수 있다.</p>
<p><img src="https://images.velog.io/images/sungjun-jin/post/36032cf4-ca4a-4610-8e3b-8c010f4b4600/image.png" alt=""></p>
<p>위 처럼 글로벌하게 정의해 애플리케이션의 컨트롤러에서 발생하는 모든 예외를 처리할수도 있지만 특정 패키지나, 컨트롤러에 한해서만 에러핸들링 처리가 가능하다.</p>
<p><code>@ResControllerAdvice</code> 어노테이션에서 <code>basePackageClasses</code>를 활용해주면 된다.</p>
<p>예시) <code>PutApiController</code> 내부에서만 에러 핸들링을 제한해보기</p>
<pre><code class="language-kotlin">@RestControllerAdvice(basePackageClasses = [PutApiController::class])
class PutApiControllerAdvice</code></pre>
]]></description>
        </item>
        <item>
            <title><![CDATA[educative - kotlin - 16]]></title>
            <link>https://velog.io/@sungjun-jin/educative-kotlin-16</link>
            <guid>https://velog.io/@sungjun-jin/educative-kotlin-16</guid>
            <pubDate>Sun, 04 Jul 2021 11:12:59 GMT</pubDate>
            <description><![CDATA[<p><img src="https://images.velog.io/images/sungjun-jin/post/e45fa6f9-7de3-4b53-9627-79e4c53ebb63/image.png" alt=""></p>
<h2 id="programming-spring-applications-with-kotlin">Programming Spring Applications with Kotlin</h2>
<h3 id="creating-a-starter-project">Creating a Starter Project</h3>
<p>코틀린 + Spring을 사용하여 첫 프로젝트의 국룰인 RESTful한 TODO 리스트를 만들어보자. </p>
<p><code>task</code> 엔드포인트에서 구현해야할 HTTP method는 다음과 같다</p>
<ul>
<li><code>GET</code>: 모든 TODO 리스트를 가져옴</li>
<li><code>POST</code>: 새로운 TODO 리스트를 추가함</li>
<li><code>DELETE</code>: 해당 TODO 리스트를 제거함</li>
</ul>
<p>우선 코틀린의 main() 메소드로 엔트리 포인트를 만들어준다. </p>
<pre><code class="language-kotlin">import org.springframework.boot.autoconfigure.SpringBootApplication
import org.springframework.boot.runApplication

@SpringBootApplication
class TodoApplication

fun main(args: Array&lt;String&gt;) {
    runApplication&lt;TodoApplication&gt;(*args)
}</code></pre>
<p>위에서 <code>@SpringBootApplication</code> 어노테이션은 스프링 부트의 가장 기본적인 설정을 선언해주는 역할을 한다.</p>
<p>프로젝트 빌드가 제대로 되어있는지를 확인해보기 위해 서버를 돌려보자. Maven, Gradle의 사용 여부에 따라서 커맨드는 달라진다</p>
<ul>
<li>Maven<pre><code class="language-shell">./mvnw clean install
java -jar target/todo-0.0.1-SNAPSHOT.jar
</code></pre>
</li>
</ul>
<pre><code>
- Gradle
```shell
./gradlew build
java -jar build/libs/todo-0.0.1-SNAPSHOT.jar</code></pre><h3 id="creating-a-controller">Creating a Controller</h3>
<p>구현하고자 하는 <code>task</code> 엔드포인트의 첫번째 함수 <code>GET</code>의 초안을 작성해보자. 이 함수는 간단하게 호출 시 <code>&quot;to be implemented&quot;</code> 라는 문자열 응답을 준다. </p>
<pre><code class="language-kotlin">package com.agiledeveloper.todo

import org.springframework.web.bind.annotation.* 
import org.springframework.http.ResponseEntity

@RestController 
@RequestMapping(&quot;/task&quot;) 
class TaskController {
    @GetMapping
    fun tasks() = &quot;to be implemented&quot; 
}</code></pre>
<p>각각의 어노테이션에 대한 설명이다</p>
<ul>
<li><code>@RestController</code>은 요청에 대한 응답을 JSON reponse로 처리해준다.</li>
<li><code>@RequestMapping</code>은 엔드포인트의 URI를 정의해준다.</li>
<li><code>@GetMapping</code> 어노테이션은 해당 엔드포인트의 URI를 지정해주는 역할을 한다.</li>
</ul>
<p>curl을 사용해 request를 날려보자</p>
<pre><code class="language-shell">curl -w &quot;\n&quot; http://localhost:8080/task</code></pre>
<h3 id="creating-an-entity-class">Creating an Entity Class</h3>
<p>TODO 리스트의 데이터를 책임질 엔티티를 생성해주자. </p>
<pre><code class="language-kotlin">package com.agiledeveloper.todo

import javax.persistence.*

@Entity
data class Task(@Id @GeneratedValue val id: Long, val description: String)</code></pre>
<p>첫번째로 해당 엔티티의 pk인 <code>id</code> 프로퍼티를 정의해준다. <code>@Id</code>와 <code>@GeneratedValue</code> 어노테이션은 해당 프로퍼티를 pk로 선정하고 unique value를 DB에서 자동 생성되게 해준다.</p>
<p>두번째 프로퍼티인 <code>description</code>의 타입은 <code>String</code>이다. </p>
<p>해당 엔티티를 애초에 데이터 클래스로 구현했으므로 <code>equals()</code>, <code>hashCode()</code>, <code>toString()</code>과 같은 함수들은 자동적으로 구현이 되어 있다.</p>
<h3 id="creating-a-repository-interface">Creating a Repository Interface</h3>
<p>코틀린 스프링에서 Repository란 여러 data source들을 관리하는데 생성하는 클래스이다.
<code>CrudRepository</code>는 엔티티 클래스에 대한 CRUD 기능을 제공해준다. 이걸 사용해보자.</p>
<pre><code class="language-kotlin">package com.agiledeveloper.todo

import org.springframework.data.repository.CrudRepository 

interface TaskRepository : CrudRepository&lt;Task, Long&gt;</code></pre>
<p><code>CrudRepository&lt;Task, Long&gt;</code>을 상속받은 <code>TaskRepository</code> 인터페이스의 첫번째 parametric type인 <code>Task</code>는 엔티티 타입을 지정해주고, 두번째 parametric type인 <code>Long</code>은 PK에 대한 타입을 지정해준다. </p>
<h3 id="creating-a-service">Creating a Service</h3>
<p>이제 엔티티 정의가 모두 마무리되었으니 서비스 레이어를 작업해보자.</p>
<p><code>TaskService</code> 클래스를 정의해준다. 서비스 클래스를 담당하고 있으므로 <code>Service</code> 어노테이션을 달아준다. <code>Transactional</code> 어노테이션은 장고의 <code>@transcation.atomic</code>과 비슷하다고 볼 수 있다. </p>
<pre><code class="language-kotlin">package com.agiledeveloper.todo

import org.springframework.stereotype.Service
import org.springframework.transaction.annotation.Transactional

@Service
@Transactional
class TaskService(val repository: TaskRepository) {
  fun getAll() = repository.findAll()

  fun save(task: Task) = repository.save(task)

  fun delete(id: Long):  Boolean {
    val found = repository.existsById(id)

    if (found) {
      repository.deleteById(id)
    }

    return found
  }
}</code></pre>
<p>전체적으로 <code>CrudRepository</code>를 상속받은 CRUD 함수들이므로 많은 추가구현은 필요없다. 각 함수에 대한 요약이다.</p>
<ul>
<li><p><code>getAll()</code> 함수는 <code>CrudRepository</code>의 <code>findAll()</code> 함수를 호출해 전체 TODO 리스트를 가지고 온다.</p>
</li>
<li><p><code>save()</code> 함수는 <code>CrudRepository</code>에서 기본적으로 제공하는 <code>save()</code> 메소드를 사용해준다. </p>
</li>
<li><p><code>delete()</code> 함수는 매개변수로 들어온 id에 해당하는 로우를 <code>deleteById()</code>를 통해 제거한다.</p>
</li>
</ul>
<h3 id="integrating-the-service-with-controller">Integrating the Service with Controller</h3>
<p>이제 모든 CRUD를 구현했으므로 앞서 테스트용으로 구현했던 컨트롤러로 다시 돌아가 완성을 시켜주자.</p>
<pre><code class="language-kotlin">package com.agiledeveloper.todo

import org.springframework.web.bind.annotation.*
import org.springframework.http.ResponseEntity

@RestController
@RequestMapping(&quot;/task&quot;)

class TaskController(val service: TaskService) {

  @GetMapping
  fun tasks() = ResponseEntity.ok(service.getAll())

  @PostMapping
  fun create(@RequestBody task: Task): ResponseEntity&lt;String&gt; {
    val result = service.save(task)

    return ResponseEntity.ok(
      &quot;added task with description ${result.description}&quot;)
  }

  @DeleteMapping(&quot;/{id}&quot;)
  fun delete(@PathVariable id: Long) = if (service.delete(id)) {
    ResponseEntity.ok(&quot;Task with id $id deleted&quot;)
  } else {
    ResponseEntity.status(404).body(&quot;Task with id $id not found&quot;)
  }</code></pre>
<p>각각의 메소드에 해당하는 어노테이션 <code>@GetMapping</code>, <code>@PostMapping</code>, <code>@DeleteMapping</code>을 구현한 메소드에 매핑시켜준다.</p>
<p><code>get()</code> 메소드는 서비스단에서 앞서 구현한 <code>getAll()</code> 함수를 호출해 모든 TODO 리스트를 가져온다. </p>
<p><code>create()</code> 메소드는 <code>@RequestBody</code> 어노테이션으로 <code>Task</code> 형의 post body를 가져와 <code>save()</code> 메소드를 호출한다. </p>
<p><code>delete()</code> 메소드는 <code>@PathVariable</code> 어노테이션으로 매핑된 url parameter인 <code>id</code>를 가지고 해당 <code>id</code>에 대한 <code>delete()</code> 메소드를 수행한다. 삭제 성공 여부에 따라 <code>200</code>, <code>404</code> 응답을 리턴해준다. </p>
<h3 id="taking-it-for-a-ride">Taking It for a Ride</h3>
<p>이제 구현한 모든 HTTP 메소드를 호출해보자</p>
<ul>
<li>GET</li>
</ul>
<pre><code class="language-shell">curl -w &quot;\n&quot; -X GET http://localhost:8080/task 

[]</code></pre>
<ul>
<li>POST</li>
</ul>
<pre><code class="language-shell">curl -w &quot;\n&quot; -X POST \
-H &quot;Content-Type: application/json&quot; \
-d &#39;{&quot;description&quot;: &quot;write code&quot;}&#39; http://localhost:8080/task

curl -w &quot;\n&quot; -X POST \
-H &quot;Content-Type: application/json&quot; \
-d &#39;{&quot;description&quot;: &quot;test&quot;}&#39; http://localhost:8080/task 


added task with description write code
added task with description test</code></pre>
<ul>
<li>DELETE</li>
</ul>
<pre><code class="language-shell">curl -w &quot;\n&quot; -X DELETE http://localhost:8080/task/1

curl -w &quot;\n&quot; -X DELETE http://localhost:8080/task/10

Task with id 1 deleted
Task with id 10 not found</code></pre>
<p>마지막으로 GET</p>
<pre><code class="language-shell">curl -w &quot;\n&quot; -X GET http://localhost:8080/task

[{&quot;id&quot;:2,&quot;description&quot;:&quot;test&quot;}]</code></pre>
]]></description>
        </item>
        <item>
            <title><![CDATA[educative - kotlin - 15]]></title>
            <link>https://velog.io/@sungjun-jin/educative-kotlin-15</link>
            <guid>https://velog.io/@sungjun-jin/educative-kotlin-15</guid>
            <pubDate>Fri, 02 Jul 2021 09:12:20 GMT</pubDate>
            <description><![CDATA[<p><img src="https://images.velog.io/images/sungjun-jin/post/a72af2ae-6671-43ae-8159-306c4940d0fd/image.png" alt=""></p>
<h2 id="empirical-tests-경험적-테스트">Empirical tests (경험적 테스트)</h2>
<p>경험적 테스팅은 개발자가 코드를 발전시켜나감과 동시에, 해당 메소드를 호출하고 의도를 검증하는 방식이다.  </p>
<p> Airport라는 데이터 클래스에 3가지 샘플 프로퍼티를 먼저 정의한다</p>
<pre><code class="language-kotlin"> package come.agiledeveloper.airportstatus

 data class Airport(val code: String, val name: String, val delay: Boolean) {
     companion object {
         fun sort(airports: List&lt;Airport&gt;) : List&lt;Airport&gt; {
             return airports
         }
     }
 }</code></pre>
<h2 id="testing-for-methods">Testing for methods</h2>
<p>빈 리스트가 인풋으로 들어감에 따라 아웃풋 또한 빈 리스트로 나오는 유닛 테스트 코드를 작성한다. 먼저 클래스 내부에서 정의된 <code>sort()</code> 메소드는 인자로 들어오는 Airport 리스트를 그대로 반환해준다. (일단은 이름만 <code>sort()</code> ....)</p>
<pre><code class="language-kotlin"> &quot;sort empty list should return an empty list&quot;
 Airport.sort(listOf&lt;Airport&gt;()) shouldBe listOf&lt;Airport&gt;()</code></pre>
<p>코틀린에서 동일함을 체크해주는 matcher는 <code>shouldBe</code>이다. 장고의 <code>assertEqual()</code>과 같다고 보면 된다. </p>
<p>빈 리스트 말고 3개의 Aiport 인스턴스를 생성한다. </p>
<pre><code class="language-kotlin"> val iah = Airport(&quot;IAH&quot;, &quot;Houston&quot;, true)
 val iad = Airport(&quot;IAD&quot;, &quot;Dulles&quot; , false)
 val ord = Airport(&quot;ORD&quot;, &quot;Chicaco O&#39;Hare&quot;, true)</code></pre>
<p> 본격적으로 sort() 메소드의 인풋 아웃풋을 검증해보자</p>
<ul>
<li><p>리스트가 1개일때 </p>
<pre><code class="language-kotlin">&quot;sort list with one Aiport should return the given Airport&quot; {
   Airport.sort(listOf(iah)) shouldBe listOf(iah)
}</code></pre>
<p>여기서 <code>&quot;sort list with one Aiport should return the given Airport&quot;</code>는 단순하게 테스트 케이스를 설명하는 description이다. </p>
</li>
<li><p>리스트가 n개일때</p>
<pre><code class="language-kotlin">&quot;sort pre-sorted list should return the given list&quot; {
   Airport.sort(listOf(iah, iad, ord)) shouldBe listOf(iah, iad, ord)
}</code></pre>
</li>
</ul>
<p>이제 진짜 sort() 메소드의 순기능을 정의해보자. 클래스의 name프로퍼티 순으로 정렬해준다. </p>
<pre><code class="language-kotlin">fun sort(airports: List&lt;Airport&gt;) : List&lt;Airport&gt; {
    return airports.sortedBy {airport -&gt; airport.name}
}</code></pre>
<p>프로젝트의 dependency 체크를 위해 코틀린은 canary test라는 테스트 케이스를 맨 먼저 세워준다. 대게는 바디에 <code>true shouldBe true</code>를 사용한다. </p>
<p>예시)</p>
<pre><code class="language-kotlin">&quot;canary test&quot; {
    true shouldBe true
}</code></pre>
<h2 id="writing-data-driven-tests">Writing Data-Driven Tests</h2>
<p>좋은 테스트는 빠르고, 자동적이고, 독립적이여야 한다. 테스트의 상호의존성을 최소화하기 위해서는 서로 독립적인 테스트에 같은 assertion을 놓는 것을 피해야 한다. </p>
<p>KotlinTest data-driven test를 위해 코틀린에서 사용하는 대표적인 라이브러리이다. 한 로우에 대한 assertion이 돌아가고, 해당 로우가 실패했을때는 실행을 멈추지 않고 따로 구분시키는 특성을 가지고 있다. </p>
<p>KotlinTest의 <code>forAll()</code>을 사용하면 각 로우에 대한 assertion 여부를 검증할 수 있다. </p>
<p><code>forAll()</code>에는 2개의 파라미터가 들어간다</p>
<ol>
<li>테스트 로우(row)</li>
<li>메소드 실행을 위한 람다 표현식</li>
</ol>
<p>여기서 첫번째 인자로 들어가는 로우는 람다에서 실행되는 sort()에 들어간다. 두번째 인자인 람다 표현식은 KotlinTest에 의해 각 로우마다 실행된다. </p>
<pre><code class="language-kotlin"> import io.kotlintest.data.forall
 import io.kotlintest.tables.row

 &quot;sort airports by name&quot; {
     forall(
        row(listOf(), listOf()),
        row(listOf(iad), listOf(iad)),
        row(listOf(iad, iah, ord), listOf(iad, iah, ord)),
        row(listOf(iad, iah, ord), listOf(ord, iad, iah))) { input, result -&gt;
            Airport.sort(input) shouldBe result
        }
     )
 }</code></pre>
<p>결과</p>
<pre><code class="language-shell">BUILD SUCCESSFUL in 1m 33s
5 actionable tasks: 5 executed
EDSTATUSCODE 1
Build Completed
root@educative:/usercode# </code></pre>
<h2 id="using-mock">Using mock</h2>
<p>MockK는 코틀린을 위한 Mock 프레임워크 이다. 자바에서 주로 사용하는 Mockito와 비슷하다. </p>
<pre><code class="language-kotlin">import io.kotlintest.TestCase 
import io.kotlintest.TestResult 
import io.mockk.*

override fun beforeTest(testCase: TestCase) {
    mockkObject(Airport)
}

override fun afterTest(testCase: TestCase, result: TestResult) {
    clearAllMocks()
}</code></pre>
<p><code>beforeTest()</code> 함수에서는 <code>mockkObject()</code> 함수를 사용해 <code>Airport</code> 싱글톤 인스턴스를 생성해준다. <code>afterTest()</code> 함수에서는 생성한 모든 mocking을 해제한다. </p>
<pre><code class="language-kotlin">&quot;getAirportData invokes fetchData&quot; {
    every { Airport.fetchData(&quot;IAD&quot;) } returns
    &quot;&quot;&quot;{&quot;IATA&quot;:&quot;IAD&quot;, &quot;Name&quot;: &quot;Dulles&quot;, &quot;Delay&quot;: false}&quot;&quot;&quot;

    Airport.getAirportData(&quot;IAD&quot;)

    verify { Airport.fetchData(&quot;IAD&quot;) }
}</code></pre>
<p>실제 비즈니스 로직에서 <code>fetchData()</code> 함수는 외부 API를 사용하기 때문에 <code>every()</code> 함수를 사용해 <code>fetchData()</code>의 Airport 객체를 모킹해준다. 생성된 객체는 공항 코드가 &quot;IAD&quot; 이면 해당 JSON response 리턴한다. </p>
<p><code>verify()</code> 함수는 <code>fetchData()</code> 함수의 호출여부를 검증해준다.</p>
<pre><code class="language-kotlin">package com.agiledeveloper.airportstatus

import com.beust.klaxon.*

data class Airport(
  @Json(name = &quot;IATA&quot;) val code: String,
  @Json(name = &quot;Name&quot;) val name: String,
  @Json(name = &quot;Delay&quot;) val delay: Boolean) {

  companion object {
    fun sort(airports: List&lt;Airport&gt;) : List&lt;Airport&gt; {
      return airports.sortedBy { airport -&gt; airport.name }
    }

    fun getAirportData(code: String) =
      Klaxon().parse&lt;Airport&gt;(fetchData(code)) as Airport

    fun fetchData(code: String): String {
      throw RuntimeException(&quot;Not Implemented Yet for $code&quot;)
    }
  }
}</code></pre>
<p>다음과 같이 오류 응답을 mocking을 해보자.</p>
<pre><code class="language-kotlin">&quot;getAirportData handles error fetching data&quot; {
    every { Airport.fetchData(&quot;ERR&quot;) } returns &quot;{}&quot;

    Airport.getAirportData(&quot;ERR&quot;) shouldBe
    Airport(&quot;ERR&quot;, &quot;Invalid Airport&quot;, false)

    verify { Airport.fetchData(&quot;ERR&quot;) }
}</code></pre>
<p>여기서 &quot;ERR&quot;로 fetchData()에 인자를 보내면 예외가 발생하기 때문에 본 함수에서 따로 예외처리를 해줘야 한다. </p>
<pre><code class="language-kotlin">fun getAirportData(code: String) = 
  try {
    Klaxon().parse&lt;Airport&gt;(fetchData(code)) as Airport      
  } catch(ex: Exception) {
    Airport(code, &quot;Invalid Airport&quot;, false)
  }</code></pre>
<h2 id="testing-top-level-functions">Testing Top-level functions</h2>
<p>위에서는 하나의 공항코드에 대한 응답을 반환하는 방식만 테스트 했다. 이번엔 새로운 파일(AirportStatus.kt)을 생성해 리스트 단위로 공항의 정보를 가져오는 top-level function을 작성하고 테스트해보자. </p>
<p>우선 getAirportStatus() top-level function을 작성하자</p>
<pre><code class="language-kotlin">fun getAirportStatus(airportCodes: List&lt;String&gt;): List&lt;Airport&gt; = 
  airportCodes.map { code -&gt; Airport.getAirportData(code) }</code></pre>
<pre><code class="language-kotlin">package com.agiledeveloper.airportstatus

import io.kotlintest.specs.StringSpec
import io.kotlintest.shouldBe
import io.kotlintest.data.forall
import io.kotlintest.tables.row
import io.kotlintest.TestCase
import io.kotlintest.TestResult
import io.mockk.*

class AirportStatusTest : StringSpec() {
  init {
    &quot;getAirportStatus returns status for airports in sorted order&quot; {
      forall(
        row(listOf&lt;String&gt;(), listOf&lt;Airport&gt;())
      ) { input, result -&gt;
          getAirportStatus(input) shouldBe result
        }
    }
  }
}</code></pre>
<p>row() 함수를 사용해 input, output을 특정해주고 다음으로 람다식에서 getAirportStatus() 함수가 호출되도록 해준다. </p>
<p>이제 IAD, IAH, inv 공항 코드에 대한 응답을 모킹해준다. </p>
<pre><code class="language-kotlin">val iad = Airport(&quot;IAD&quot;, &quot;Dulles&quot;, true)
val iah = Airport(&quot;IAH&quot;, &quot;Houston&quot;, false)
val inv = Airport(&quot;inv&quot;, &quot;Invalid Airport&quot;, false)

override fun beforeTest(testCase: TestCase) {
    mockkObject(Airport)
    every { Airport.getAirportData(&quot;IAD&quot;) } returns iad
    every { Airport.getAirportData(&quot;IAH&quot;) } returns iah
    every { Airport.getAirportData(&quot;inv&quot;) } returns inv
}

override fun afterTest(testCase: TestCase, result: TestResult) {
    clearAllMocks()
}</code></pre>
<p>실행결과</p>
<pre><code class="language-shell">BUILD SUCCESSFUL in 1m 46s
5 actionable tasks: 5 executed
EDSTATUSCODE 1
Build Completed
root@educative:/usercode# </code></pre>
]]></description>
        </item>
        <item>
            <title><![CDATA[educative - kotlin - 14]]></title>
            <link>https://velog.io/@sungjun-jin/educative-kotlin-14-612ta1sy</link>
            <guid>https://velog.io/@sungjun-jin/educative-kotlin-14-612ta1sy</guid>
            <pubDate>Sat, 15 May 2021 16:46:04 GMT</pubDate>
            <description><![CDATA[<h2 id="asynchronous-programming-비동기-프로그래밍">Asynchronous Programming (비동기 프로그래밍)</h2>
<p><img src="https://images.velog.io/images/sungjun-jin/post/e391fb06-f4ff-46fb-a492-95c7b27bf0c3/image.png" alt=""></p>
<p>현대 어플리케이션에서는 원격 서비스의 호출, DB 업데이트, 검색 등 많은 작업들이 내부적으로 계속 돌아간다. 프로그램의 효율화를 위해서는 각각의 작업들을 비동기, non-blocking(I/O 작업이 완료될 때 까지 기다리지 않고 다른 작업을 수행하는 개념) 방식으로 처리해야한다. </p>
<h3 id="starting-sequentially">Starting sequentially</h3>
<p>특정 공항의 온도와 연착여부를 알아보기 위해 기상정보를 수집하는 코드를 짜보자. 사용하는 API와 JSON parser는 FAA(미국 연방항공국)에서 제공해준다.</p>
<pre><code class="language-kotlin">import java.net.URL
import com.beust.klaxon.*

class Weather(@Json(name = &quot;Temp&quot;) val temperature: Array&lt;String&gt;)

class Airport(
  @Json(name = &quot;IATA&quot;) val code: String,
  @Json(name = &quot;Name&quot;) val name: String,
  @Json(name = &quot;Delay&quot;) val delay: Boolean,
  @Json(name = &quot;Weather&quot;) val weather: Weather) {

  companion object {
    fun getAirportData(code: String): Airport? {
      val url = &quot;https://soa.smext.faa.gov/asws/api/airport/status/$code&quot;
      return Klaxon().parse&lt;Airport&gt;(URL(url).readText())
    }
  }
}</code></pre>
<ul>
<li>Weather, Airport 클래스에선 전부 <code>@Json</code> annotation을 통해 JSON response를 매핑해준다.</li>
<li><code>getAirportdata()</code> 메소드에서는 API 응답을 파싱해서 Airport 인스턴스를 생성한다. </li>
</ul>
<p>메인 메소드에서는 우선 순차적으로 데이터를 가져오도록 하고 실행시간을 측정해주는  <code>measureTimeMillis()</code> 함수도 짜준다. </p>
<pre><code class="language-kotlin">fun main() {                  
  val format = &quot;%-10s%-20s%-10s&quot;
  println(String.format(format, &quot;Code&quot;, &quot;Temperature&quot;, &quot;Delay&quot;))

  val time = measureTimeMillis {
    val airportCodes = listOf(&quot;LAX&quot;, &quot;SFO&quot;, &quot;PDX&quot;, &quot;SEA&quot;)

    val airportData: List&lt;Airport&gt; = 
      airportCodes.mapNotNull { anAirportCode -&gt;
        Airport.getAirportData(anAirportCode)
      }         

    airportData.forEach { anAirport -&gt; 
      println(String.format(format, anAirport.code,
        anAirport.weather.temperature.get(0), anAirport.delay))
    }
  }

  println(&quot;Time taken $time ms&quot;)
}</code></pre>
<p>실행결과 네트워크 상태에 따라 다르겠지만 대략 2초의 시간이 걸렸다. </p>
<pre><code>Code Temperature       Delay
LAX  68.0 F (20.0 C)   false
SFO  50.0 F (10.0 C)   true
PDX  56.0 F (13.3 C)   false
SEA  55.0 F (12.8 C)   true 
Time taken 2112 ms</code></pre><h3 id="making-asynchronous">Making asynchronous</h3>
<p>여태까지 구현한 <code>getAirportData()</code> 함수는 모두 blocking calls 방식이다. 즉, 요청이 발생하고 완료될 때까지 모든 일을 중단한다. 요약하면 다음과 같다</p>
<ol>
<li>LAX 공항 정보에 대한 요청을 보냄</li>
<li>그 다음 순서인 SFO 공항은 위에서 보낸 요청이 처리되기 전까지 대기</li>
</ol>
<p>이 방식을 이전 공항에 대한 요청이 처리될때까지 기다리지 않고, 동시에 다른 요청을 같이 처리할 수 있는 non blocking calls 방식으로 바꿔보자. </p>
<pre><code class="language-kotlin">import kotlin.system.*
import kotlinx.coroutines.*

fun main() = runBlocking {                  
  val format = &quot;%-10s%-20s%-10s&quot;
  println(String.format(format, &quot;Code&quot;, &quot;Temperature&quot;, &quot;Delay&quot;))

  val time = measureTimeMillis {
    val airportCodes = listOf(&quot;LAX&quot;, &quot;SFO&quot;, &quot;PDX&quot;, &quot;SEA&quot;)

    val airportData: List&lt;Deferred&lt;Airport?&gt;&gt; = 
      airportCodes.map { anAirportCode -&gt;
        async(Dispatchers.IO) { 
          Airport.getAirportData(anAirportCode)
        }
      }         

    airportData
      .mapNotNull { anAirportData -&gt; anAirportData.await() }
      .forEach { anAirport -&gt; 
        println(String.format(format, anAirport.code,
        anAirport.weather.temperature.get(0), anAirport.delay))
    }
  }

  println(&quot;Time taken $time ms&quot;)
}</code></pre>
<p>비동기 처리의 떠한 코드 블록 내 작업이 완료 되기를 기다리는 <code>runBlocking()</code> 함수를 활용해. 메인 함수의 부분을 <code>runBlocking()</code>으로 감싸줬다.  </p>
<p>여기서는 코루틴 블록의 결과를 가지고 와야하기 때문에 <code>launch()</code> 보다는 <code>async()</code> 함수를 사용한다. <code>async()</code> 함수를 사용해 CouroutineContext를 인자로 넘겨줘서 각자의 thread pool에서 <code>getAirportData()</code> 메소드를 실행시키도록 할 수 있다.</p>
<p>여기서 <code>airportData</code>의 타입이 <code>List&lt;Deferred&lt;Airport?&gt;&gt;</code> 인 이유는 <code>async()</code> 함수로 시작된 코루틴 블록은 <code>Deferred</code> 객체를 반환하기 때문이다. </p>
<p>맵을 돌면서 <code>getAirportData()</code>를 호출하는 첫번째 iteration에서 async() 함수는 람다 표현식으로 받은 인자를 실행시키고 <code>Deferred&lt;Airport?&gt;</code> 객체를 반환하여 리스트에 저장한다. </p>
<p>두번째 iteration부터는 <code>getAirportData()</code> 실행된 결과를 가지고 오기 위해 await() 메소드를 호출하고 가공한다. </p>
<p>위 작업들이 async() 함수를 사용했으므로 각자 메인 스레드가 아닌 다른 스레드에서 이루어진다. 실행결과를 보자, 실행시간은 1.6초로 전에 비해서 조금 줄었다. 물론 네트워크 컨디션에 따라서 실행시간은 달라질 수 있다</p>
<pre><code class="language-plain">Code Temperature       Delay
LAX  68.0 F (20.0 C)   false
SFO  50.0 F (10.0 C)   true
PDX  56.0 F (13.3 C)   false
SEA  55.0 F (12.8 C)   true 
Time taken 1676 ms</code></pre>
<h3 id="exception-handling">Exception Handling</h3>
<p><code>launch()</code>와 <code>async()</code>를 사용해 코루틴을 실행하면 예외처리도 깔끔하게 해줘야 한다. </p>
<p>Be Mindful of Structured Concurrency </p>
<blockquote>
<p>명시적으로 스코프를 지정해주지 않는 이상, 코루틴은 부모 코루틴의 context에서 돌아가는 계층적 구조를 가지고 있다. 이를 structured concurrency(구조화 된 동시성)이라고 한다. 이 계층 구조를 활용하면 부모 코루틴의 자동으로 자식 코루틴의 수명주기를 제어할 수 있다. 반대로 자식 코루틴에서 예외가 발생하면 부모 코루틴에서도 예외가 발생할 수 있다. </p>
</blockquote>
<p>여기서는 자식 코루틴에서 문제가 생겼을때 부모 코루틴의 실행을 취소하지 않는 방법을 알아보자.</p>
<h3 id="launch-and-exceptions">launch and exceptions</h3>
<p><code>launch()</code> 함수를 사용하다가 발생한 예외는 호출한 쪽에서 받아볼 수 없다. 이런 예외를 핸들링 하기 위해 전에 있던 공항 예제를 다시 가져와보자.</p>
<pre><code class="language-kotlin">import kotlinx.coroutines.*

fun main() = runBlocking {
  try {
    val airportCodes = listOf(&quot;LAX&quot;, &quot;SF-&quot;, &quot;PD-&quot;, &quot;SEA&quot;)

    val jobs: List&lt;Job&gt; = airportCodes.map { anAirportCode -&gt;
      launch(Dispatchers.IO + SupervisorJob()) {
        val airport = Airport.getAirportData(anAirportCode)
        println(&quot;${airport?.code} delay: ${airport?.delay}&quot;)
      }
    }

    jobs.forEach { it.join() }
    jobs.forEach { println(&quot;Cancelled: ${it.isCancelled}&quot;) }
  } catch(ex: Exception) {
    println(&quot;ERROR: ${ex.message}&quot;)
  }
}</code></pre>
<p>우선 첫번째로 메인 함수를 try-catch 블록으로 한번 감싸주자 </p>
<p>코루틴 예외 처리 과정을 보기 위해 여기서 <code>async()</code> 함수 대신 <code>launch()</code> 함수로 교체했다. <code>launch()</code> 함수를 사용하면 코루틴의 흐름을 제어하는 <code>Job</code> 객체를 반환한다. <code>Job</code> 객체의 <code>isCancelled</code> 프로퍼티를 사용하면 코루틴의 성공여부를 확인해볼 수 있다. </p>
<p>코드를 실행시켜보자</p>
<pre><code class="language-kotlin">LAX delay: false
SEA delay: true
Exception in thread &quot;DefaultDispatcher-worker-1&quot; Exception in ...</code></pre>
<p>메인 함수에서 try-catch문로 예외 핸들링을 했음에도 불구하고 코루틴에서 발생한 예외를 잡지 못했다. 이유는  <code>launch()</code> 함수로 실행된 코루틴은 예외를 호출부로 던지지 않는 특성 때문이다. </p>
<p>따라서 <code>launch()</code> 함수를 사용한다면 <code>CoroutineExceptionHandler</code>를 사용해 코루틴에서 발생한 예외를 처리해줘야 한다. 그리고 <code>launch()</code> 함수에 등록을 시켜줘야 한다. </p>
<pre><code class="language-kotlin">fun main() = runBlocking {
  // 핸들러 정의 
  val handler = CoroutineExceptionHandler { context, ex -&gt;
    println(
      &quot;Caught: ${context[CoroutineName]} ${ex.message?.substring(0..28)}&quot;)
  }

  try {
    val airportCodes = listOf(&quot;LAX&quot;, &quot;SF-&quot;, &quot;PD-&quot;, &quot;SEA&quot;)

    val jobs: List&lt;Job&gt; = airportCodes.map { anAirportCode -&gt;
      // 코루틴 네임 할당, handler 등록
      launch(Dispatchers.IO + CoroutineName(anAirportCode) +
        handler + SupervisorJob()) {
        val airport = Airport.getAirportData(anAirportCode)
        println(&quot;${airport?.code} delay: ${airport?.delay}&quot;)
      }
    }

    jobs.forEach { it.join() }
    jobs.forEach { println(&quot;Cancelled: ${it.isCancelled}&quot;) }
  } catch(ex: Exception) {
    println(&quot;ERROR: ${ex.message}&quot;)
  }
}</code></pre>
<p>실행결과 </p>
<pre><code>Caught: CoroutineName(PD-) Unable to instantiate Airport 
Caught: CoroutineName(SF-) Unable to instantiate Airport 
SEA delay: true
LAX delay: false
Cancelled: false
Cancelled: true
Cancelled: true
Cancelled: false</code></pre><h3 id="async-and-exceptions">async and exceptions</h3>
<p><code>async()</code> 함수는 <code>launch()</code>와 다르게 <code>Deferred&lt;T&gt;</code> 객체를 반환한다. 이 객체는 코루틴에서 발생하는 예외를 호출부로 던져주는 특성이 있다. 따라서 <code>launch()</code> 처럼 따로 코루틴 예외 핸들러를 만들어서 넘겨줘도 무시한다. 결론은 <code>async()</code> 함수를 사용하면 간단하게 try-catch문으로 감싸주면 핸들링이 된다.</p>
<pre><code class="language-kotlin">import kotlinx.coroutines.*

fun main() = runBlocking {
  val airportCodes = listOf(&quot;LAX&quot;, &quot;SF-&quot;, &quot;PD-&quot;, &quot;SEA&quot;)

  val airportData = airportCodes.map { anAirportCode -&gt;
    async(Dispatchers.IO + SupervisorJob()) {
      Airport.getAirportData(anAirportCode)
    }
  }

  for (anAirportData in airportData) {
    try {
      val airport = anAirportData.await()

      println(&quot;${airport?.code} ${airport?.delay}&quot;)
    } catch(ex: Exception) {
      println(&quot;Error: ${ex.message?.substring(0..28)}&quot;)
    }
  }
}</code></pre>
<p>실행결과</p>
<pre><code>LAX false
Error: Unable to instantiate Airport
Error: Unable to instantiate Airport
SEA true</code></pre>]]></description>
        </item>
        <item>
            <title><![CDATA[educative - kotlin - 14]]></title>
            <link>https://velog.io/@sungjun-jin/educative-kotlin-14</link>
            <guid>https://velog.io/@sungjun-jin/educative-kotlin-14</guid>
            <pubDate>Thu, 06 May 2021 16:38:51 GMT</pubDate>
            <description><![CDATA[<h2 id="coroutines-and-concurrency">Coroutines and Concurrency</h2>
<p><strong><em>Coroutine</em></strong>은 동시성 프로그래밍을 가능하도록 만든 개념이다. 스레드와 기능적으로 같지만, 좀 더 가볍고 유연한 백그라운드 스레드에서 코드를 처리할 때 사용하는 하나의 방법이다. </p>
<p><strong><em>Concurrency(동시성)</em></strong>는 하나의 작업을 일정량 처리하고 다음 작업을 처리하는 여러 개의 스레드가 번갈아가면서 실행되는 방식이다. 각 스레드들이 병렬적으로 실행되는 것처럼 보이지만 사실은 번갈아가면서 조금씩 실행되고 있는 것이다.</p>
<h3 id="parallel-vs-concurrent">Parallel vs. concurrent</h3>
<p>병렬성 처리와 동시성 처리의 차이점에 대한 좋은 그림이다. 
<img src="https://images.velog.io/images/sungjun-jin/post/89ee6343-3bb0-4754-949d-a49637ed8fe0/image.png" alt=""></p>
<p>여기서 피자를 먹으면서 귀로 상대방의 목소리를 듣는것은 병렬성 처리에 해당된다. 반대로 피자를 먹다가 말을 하는 것은 동시성 처리에 해당된다. </p>
<h3 id="coroutines-as-cooperating-functions">Coroutines as cooperating functions</h3>
<p>서브루틴이란 caller에게 리턴하기 전에 완료되는 방식이다. 호출중간에 상태값을 가지지 않는 특성이 있다. 일반적인 목적의 프로그래밍에서 서브루틴은 코루틴보다 더 흔하게 사용된다. 서브루틴은 단 하나의 entry point를 가지는 반면 코루틴은 여러개의 entry point를 가지고 있다. 이 특성을 가지고 우리는 하나의 Producer, Consumer 관계의 cooperating function을 만들 수 있다.</p>
<p>Producer와 Consumer가 코루틴을 통해 작업을 실행하면서 상태를 공유하고 있다.
<img src="https://images.velog.io/images/sungjun-jin/post/d23ea86e-99d2-47e9-b82f-7d011fdad6f0/image.png" alt=""></p>
<h3 id="starting-with-sequential-execution">Starting with sequential execution</h3>
<p>먼저 코틀린에서 코드를 실행할때 순차적으로 실행될 것인지, 병렬로 실행된 것일지를 지정하는 방법부터 알아보자. 첫번째로 순차적으로 함수 호출을 실행하는 부분의 예시다.</p>
<pre><code class="language-kotlin">fun task1() {
  println(&quot;start task1 in Thread ${Thread.currentThread()}&quot;)
  println(&quot;end task1 in Thread ${Thread.currentThread()}&quot;)
}

fun task2() {
  println(&quot;start task2 in Thread ${Thread.currentThread()}&quot;)
  println(&quot;end task2 in Thread ${Thread.currentThread()}&quot;)
}

println(&quot;start&quot;)

run {
  task1()
  task2() 

  println(&quot;called task1 and task2 from ${Thread.currentThread()}&quot;)
}

println(&quot;done&quot;)</code></pre>
<p><code>task1()</code>과 <code>task2()</code> 함수는 각각 스레드의 실행정보를 출력하는 함수이다.<code>run()</code> 함수는 <code>Any</code> 클래스의 람다 표현식을 인자로 받는 확장함수이다. </p>
<p>실행결과는 당연히 <code>task1</code>이 완료되고 순차적으로 <code>task2</code>가 실행되는 모습을 확인할 수 있다.</p>
<pre><code class="language-shell">start
start task1 in Thread Thread[main,5,main]
end task1 in Thread Thread[main,5,main]
start task2 in Thread Thread[main,5,main]
end task2 in Thread Thread[main,5,main]
called task1 and task2 from Thread[main,5,main]
done</code></pre>
<h3 id="creating-a-coroutine">Creating a coroutine</h3>
<p>다시 위에있는 <code>task1()</code>과 <code>task2()</code> 함수를 동시성(concurrently)으로 실행되게 바꿔보자.먼저 <code>kotlinx.coroutines</code> 패키지를 import하고 <code>run()</code>함수를 <code>runBlocking()</code> 함수로 대체했다. <code>runBlocking()</code> 함수는 <code>kotlinx.coroutines</code> 안에 있고 람다를 인자로 받고 동시성으로 실행되게 한다.</p>
<pre><code class="language-kotlin">import kotlinx.coroutines.*

fun task1() {
  println(&quot;start task1 in Thread ${Thread.currentThread()}&quot;)
  println(&quot;end task1 in Thread ${Thread.currentThread()}&quot;)
}

fun task2() {
  println(&quot;start task2 in Thread ${Thread.currentThread()}&quot;)
  println(&quot;end task2 in Thread ${Thread.currentThread()}&quot;)
}

println(&quot;start&quot;)

runBlocking {
  task1()
  task2() 

  println(&quot;called task1 and task2 from ${Thread.currentThread()}&quot;)
}

println(&quot;done&quot;)</code></pre>
<p>실행결과는 위에 결과랑 똑같다. 코루틴은 동시에 코드를 실행시키고 메인 스레드에서 실행되는 람다가<code>runBlocking()</code> 호출 전후에 실행되기 때문이다.</p>
<pre><code class="language-shell">start
start task1 in Thread Thread[main,5,main]
end task1 in Thread Thread[main,5,main]
start task2 in Thread Thread[main,5,main]
end task2 in Thread Thread[main,5,main]
called task1 and task2 from Thread[main,5,main]
done</code></pre>
<h3 id="launching-a-task">Launching a task</h3>
<p>이번엔 <code>task1()</code>과 <code>task2()</code>의 함수가 <code>launch()</code> 함수에 의해 각각 다른 코루틴에서 실행되게 해보자. <code>launch()</code> 함수는 새로운 코루틴을 인자로 받아 실행시켜준다.</p>
<pre><code class="language-kotlin">runBlocking {
  launch { task1() }
  launch { task2() }
  println(&quot;called task1 and task2 from ${Thread.currentThread()}&quot;) 
}
println(&quot;done&quot;)</code></pre>
<p>실행결과가 살짝 달라졌다. 시작 메세지와 함께 task1, task2가 수행되고 마지막 종료 메세지가 나온다. 모든 코드는 메인 스레드에서 실행되지만 마지막 출력 함수가 task1, task2 이전에 실행되는 모습을 확인할 수 있다.</p>
<pre><code class="language-shell">start
called task1 and task2 from Thread[main,5,main]
start task1 in Thread Thread[main,5,main]
end task1 in Thread Thread[main,5,main]
start task2 in Thread Thread[main,5,main]
end task2 in Thread Thread[main,5,main]
done</code></pre>
<h3 id="interleaving-calls-with-suspension-points">Interleaving calls with suspension points</h3>
<p>Suspension point랑 함수가 실행하는 중간에 작업을 잠시 중단(suspend)하고고 다른 task를 수행하는 지점을 뜻한다. <code>kotlinx.coroutine</code> 라이브러리의 <code>delay()</code>와 <code>yield()</code> 함수로 구현할 수 있다. <code>delay()</code>함수는 실행중인 작업을 잠깐 멈추고 <code>yield()</code>는 명시적으로 작업을 멈추는것이 아니라는 차이점이 있다.</p>
<p><code>suspend</code> 키워드를 사용하는 예시이다.</p>
<pre><code class="language-kotlin">import kotlinx.coroutines.*

suspend fun task1() {
  println(&quot;start task1 in Thread ${Thread.currentThread()}&quot;)
  yield()
  println(&quot;end task1 in Thread ${Thread.currentThread()}&quot;)
}

suspend fun task2() {
  println(&quot;start task2 in Thread ${Thread.currentThread()}&quot;)
  yield()
  println(&quot;end task2 in Thread ${Thread.currentThread()}&quot;)
}

println(&quot;start&quot;)

runBlocking {
  launch { task1() }
  launch { task2() }

  println(&quot;called task1 and task2 from ${Thread.currentThread()}&quot;)
}

println(&quot;done&quot;)</code></pre>
<p>실행결과 확실한 동시성 방식으로 코드가 돌아가는것을 확인할 수 있다. </p>
<pre><code class="language-shell">start
called task1 and task2 from Thread[main,5,main]
start task1 in Thread Thread[main,5,main]
start task2 in Thread Thread[main,5,main]
end task1 in Thread Thread[main,5,main]
end task2 in Thread Thread[main,5,main]
done</code></pre>
<h3 id="explicitly-setting-a-context">Explicitly setting a context</h3>
<p><code>launch()</code> 나 <code>runBlocking()</code> 함수에 <code>CoroutineContext</code>를 넘겨줄 수 있다. 여기서 <code>CoroutineContext</code>의 인자인 <code>Dispatchers.Default</code>는 스레드에서 실행되는 코루틴에게 <code>DefaultDispatch pool</code>에서 실행되도록 지시하는 역할을 한다. </p>
<p><code>Dispatchers.IO</code>의 값들은 IO 작업이 집중된 코루틴 풀에서 사용할 수 있다.
<code>Dispatchers.Main</code>은 Swing UI를 사용하는 안드로이드 디바이스에서 사용할 수 있다.</p>
<pre><code class="language-kotlin">runBlocking {
  launch(Dispatchers.Default) { task1() } 
  launch { task2() }
  println(&quot;called task1 and task2 from ${Thread.currentThread()}&quot;) 
}</code></pre>
<p>위 코드에서 실행하면 <code>task1()</code>은 다른 스레드에서 실행될 것이다. 즉, 병렬로 처리된다. 
실행결과</p>
<pre><code class="language-shell">start
start task1 in Thread Thread[DefaultDispatcher-worker-1,5,main] end task1 in Thread Thread[DefaultDispatcher-worker-2,5,main] called task1 and task2 from Thread[main,5,main]
start task2 in Thread Thread[main,5,main]
end task2 in Thread Thread[main,5,main]
done</code></pre>
<h3 id="running-in-a-custom-pool">Running in a custom pool</h3>
<p>이번엔 커스텀 pool에서 작업이 돌아가게 만들어보자. 먼저 thread executor를 생성하는 방법은 다음과 같다. </p>
<ol>
<li><code>java.util.concurrent</code> 패키지에서 <code>Executors</code> API를 사용한다. </li>
<li>확장함수인 <code>asCoroutineDispatcher()</code>를 사용해 <code>CoroutineContext</code>를 가져온다.</li>
</ol>
<pre><code class="language-kotlin">// single.kts
import kotlinx.coroutines.*
import java.util.concurrent.Executors
//...task1 and task2 function definitions as before...

Executors.newSingleThreadExecutor().asCoroutineDispatcher().use { context -&gt;
  println(&quot;start&quot;)

  runBlocking {
    launch(context) { task1() }
    launch { task2() }

    println(&quot;called task1 and task2 from ${Thread.currentThread()}&quot;)
  }

  println(&quot;done&quot;)  
}</code></pre>
<p>실행결과</p>
<pre><code class="language-kotlin">start
start task1 in Thread Thread[pool-1-thread-1,5,main] 
end task1 in Thread Thread[pool-1-thread-1,5,main] 
called task1 and task2 from Thread[main,5,main] 
start task2 in Thread Thread[main,5,main]
end task2 in Thread Thread[main,5,main]
done</code></pre>
<p>1개의 thread pool 대신 다수의 thread를 사용하고 싶다면 다음과 같이 코드를 바꿔주면 된다</p>
<pre><code class="language-kotlin">Executors.newFixedThreadPool(Runtime.getRuntime().availableProcessors()) 
  .asCoroutineDispatcher().use { context -&gt;</code></pre>
<h3 id="switching-threads-after-suspension-points">Switching threads after suspension points</h3>
<p>다른 suspension point로 전환하고 싶으면 <code>CoroutineContext</code> 인자와 <code>CoroutineStart</code> 인자를 사용할 수 있다. </p>
<p><code>launch()</code> 함수의 두번째 인자를 CoroutineStart의 DEFAULT 인자로 지정해준다. &#39;<code>start()</code> 함수가 실행되기 전까지 기다리고 싶으면 LAZY, 
_ATOMIC은 취소가 불가능한 상태, 
suspension point 이후 다른 스레드의 전환은 UNDISPATCHED를 사용해준다.</p>
<pre><code class="language-kotlin">import kotlinx.coroutines.*
import java.util.concurrent.Executors

suspend fun task1() {
  println(&quot;start task1 in Thread ${Thread.currentThread()}&quot;)
  yield()
  println(&quot;end task1 in Thread ${Thread.currentThread()}&quot;)
}
suspend fun task2() {
  println(&quot;start task2 in Thread ${Thread.currentThread()}&quot;)
  yield()
  println(&quot;end task2 in Thread ${Thread.currentThread()}&quot;)
}

Executors.newFixedThreadPool(Runtime.getRuntime().availableProcessors())
  .asCoroutineDispatcher().use { context -&gt;
  println(&quot;start&quot;)

  runBlocking {
    @UseExperimental(ExperimentalCoroutinesApi::class)
    launch(context = context, start = CoroutineStart.UNDISPATCHED) { task1() }
    launch { task2() }

    println(&quot;called task1 and task2 from ${Thread.currentThread()}&quot;)
  }

  println(&quot;done&quot;)</code></pre>
<p><code>CoroutineStart.UNDISPATCHED</code>는 <code>@UseExperimental</code> 어노테이션과 함께 사용해준다.</p>
<p>실행결과. task1()가 pool-1&#39;s 스레드 대신 메인 스레드에서 실행됬다. <code>yield()</code>가 있는 suspension point 도달 후, pool-1 스레드로 옮겨 실행됐다. </p>
<pre><code class="language-kotlin">start
start task1 in Thread Thread[main,5,main]
end task1 in Thread Thread[pool-1-thread-1,5,main] called task1 and task2 from Thread[main,5,main] start task2 in Thread Thread[main,5,main]
end task2 in Thread Thread[main,5,main]
done</code></pre>
<h3 id="changing-the-coroutine-context">Changing the coroutine context</h3>
<p><code>withContext()</code> 함수를 사용하면 코루틴 실행 중간에 다른 context로 바꿀 수 있다.</p>
<pre><code class="language-kotlin">runBlocking { 
  println(&quot;starting in Thread ${Thread.currentThread()}&quot;)
  withContext(Dispatchers.Default) { task1() }

  launch { task2() }

  println(&quot;ending in Thread ${Thread.currentThread()}&quot;)
}</code></pre>
<p>실행결과</p>
<pre><code class="language-shell">starting in Thread Thread[main,5,main]
start task1 in Thread Thread[DefaultDispatcher-worker-1,5,main] 
end task1 in Thread Thread[DefaultDispatcher-worker-1,5,main] 
ending in Thread Thread[main,5,main]
start task2 in Thread Thread[main,5,main]
end task2 in Thread Thread[main,5,main]</code></pre>
<h3 id="running-in-debug-mode">Running in debug mode</h3>
<p>코틀린 파일을 실행할때 <code>-Dkotlinx.coroutines.debug</code> 옵션을 주면 command-line에서 디버깅 옵션으로 실행된다.</p>
<pre><code class="language-shell">kotlinc-jvm -Dkotlinx.coroutines.debug \
-classpath /opt/kotlin/kotlinx-coroutines-core-1.2.2.jar \-script withcontext.kts</code></pre>
<p>실행결과 조금 더 디테일한 결과를 확인할 수 있다. 각각의 코루틴에 대해 식별자(identifier)를 지정해준다. 용이할수 있지만 디버깅 관점에서는 식별자에 숫자보다는 논리적인 이름(<code>searching for meaning of life</code>)을 코루틴에 달아주는게 더 가독성이 좋다.</p>
<pre><code class="language-shell">starting in Thread Thread[main @coroutine#1,5,main]
start task1 in Thread Thread[DefaultDispatcher-worker-1 @coroutine#1,5,main] 
end task1 in Thread Thread[DefaultDispatcher-worker-3 @coroutine#1,5,main] 
ending in Thread Thread[main @coroutine#1,5,main]
start task2 in Thread Thread[main @coroutine#2,5,main]
end task2 in Thread Thread[main @coroutine#2,5,main]</code></pre>
<h3 id="assigning-names-to-coroutines">Assigning names to coroutines</h3>
<p>따라서 코루틴에 논리적인 이름을 할당해주는 방법은 <code>CoroutineName()</code> 함수를 사용해주면 된다.</p>
<pre><code class="language-kotlin">runBlocking(CoroutineName(&quot;top&quot;)) {
  println(&quot;running in Thread ${Thread.currentThread()}&quot;) 
  withContext(Dispatchers.Default) { task1() }
  launch(Dispatchers.Default + CoroutineName(&quot;task runner&quot;)) { task2() } 
  println(&quot;running in Thread ${Thread.currentThread()}&quot;)
}</code></pre>
<p>실행결과 top, task runnner와 같이 코루틴에 이름이 할당된 모습을 확인할 수 있다.</p>
<pre><code class="language-shell">running in Thread Thread[main @top#1,5,main]
start task1 in Thread Thread[DefaultDispatcher-worker-1 @top#1,5,main]
end task1 in Thread Thread[DefaultDispatcher-worker-3 @top#1,5,main]
start task2 in Thread Thread[DefaultDispatcher-worker-3 @task runner#2,5,main] 
end task2 in Thread Thread[DefaultDispatcher-worker-3 @task runner#2,5,main] 
running in Thread Thread[main @top#1,5,main]</code></pre>
<h3 id="async-and-await">async and await</h3>
<p><code>launch()</code> 함수는 코루틴의 중단을 위해 사용되는 Job Oject를 리턴한다. 그러나 <code>launch()</code> 함수를 사용해 시작된 코루틴의 결과를 리턴하는 방법은 없다. <code>launch()</code> 대신 <code>async()</code>를 사용하면 이 문제가 해결된다.</p>
<p><code>async()</code> 함수는 <code>launch()</code> 함수와 똑같은 인자를 받는다. 다른점은 <code>async()</code> 함수는 <code>await()</code> 함수를 가지고 있는  <code>Deferred&lt;T&gt;</code> 를 리턴한다는 점이다. 이 객체는 코루틴의 상태를 확인하거나 취소를 할 수 있다. <code>await()</code> 함수를 호출하면 코드의 실행흐름을 제어할 수 있다.</p>
<h3 id="using-async--await">Using async &amp; await</h3>
<p>다음 코드에서 우리는 실행가능한 비동기 코어의 개수를 가져올 것이다. <code>Dispatchers.Default</code> 인자는 optional하고 생략 시 상속된 Dispatcher를 실행한다. </p>
<pre><code class="language-kotlin">import kotlinx.coroutines.*

runBlocking {
  val count: Deferred&lt;Int&gt; = async(Dispatchers.Default) { 
    println(&quot;fetching in ${Thread.currentThread()}&quot;)
    Runtime.getRuntime().availableProcessors()
  }

  println(&quot;Called the function in ${Thread.currentThread()}&quot;)

  println(&quot;Number of cores is ${count.await()}&quot;)
}</code></pre>
<p>Dispatcher의 요청 후 메인 스레드에서 <code>async()</code> 호출 후 <code>print()</code>문을 실행한다. <code>await()</code> 호출은 코루틴의 종료를 기다리고 마지막 출력 구문은 코루틴의 response를 출력할 것이다.</p>
<p>실행결과</p>
<pre><code class="language-shell">Called the function in Thread[main,5,main]
fetching in Thread[DefaultDispatcher-worker-1,5,main] 
Number of cores is 8 // 코어의 갯수</code></pre>
<h3 id="preserving-the-state">Preserving the state</h3>
<p>스레드 간의 상태 보존은 어떻게 이루어질까? 아래 Compute 클래스는 2개의 메소드로 구성되어 있다. input의 제곱값을 리턴해주는 <code>compute1()</code>, <code>compute2()</code> 메소드도 비슷하지만 실행 흐름을 잠깐 멈추고 잠재적으로 스레드를 바꿀수 있다.</p>
<pre><code class="language-kotlin">import kotlinx.coroutines.*

class Compute {
  fun compute1(n: Long): Long = n * 2
  suspend fun compute2(n: Long): Long {
    val factor = 2
    println(&quot;$n received : Thread: ${Thread.currentThread()}&quot;)
    delay(n * 1000)
    val result = n * factor
    println(&quot;$n, returning $result: Thread: ${Thread.currentThread()}&quot;)
    return result
  }
}</code></pre>
<p><code>main()</code> 함수를 통해 실행시켜보자.</p>
<pre><code class="language-kotlin">import kotlinx.coroutines.*

fun main() = runBlocking&lt;Unit&gt; {
  val compute = Compute()

  launch(Dispatchers.Default) {
    compute.compute2(2)
  }
  launch(Dispatchers.Default) {
    compute.compute2(1)
  }
}</code></pre>
<p>실행결과</p>
<pre><code class="language-shell">2 received : Thread: Thread[DefaultDispatcher-worker-1,5,main]
1 received : Thread: Thread[DefaultDispatcher-worker-2,5,main]
1, returning 2: Thread: Thread[DefaultDispatcher-worker-2,5,main] 
2, returning 4: Thread: Thread[DefaultDispatcher-worker-4,5,main]</code></pre>
<h3 id="what-are-continuations">What are continuations</h3>
<p>코틀린에서 분할된 함수를 호출할때마다 특정 코드를 실행시켜 상태를 유지하는 것을 Continuation이라고 한다. 위 예제의 바이트코드를 살펴보자</p>
<pre><code class="language-java">public final long compute1(long);
public final java.lang.Object compute2(long,
  kotlin.coroutines.Continuation&lt;? super java.lang.Long&gt;);</code></pre>
<p>소스코드 단에서는 <code>compute1()</code>이나 <code>compute2()</code>나 비슷했지만 바이트 코드 레벨에서는 많은 차이가 있다. <code>compute2()</code>에서는 심지어 파라미터의 개수도 다르다, 그리고 <code>long</code> 타입 대신 <code>Object</code>를 리턴하는 차이도 보인다. </p>
<p><code>Continuation&lt;?superLong&gt;</code>은 부분 실행된 함수의 결과를 캡슐화하고 호출부로 전달해준다. 컴파일러는 
상태를 보존하는 continuation 작업을 알아서 실행해 코드를 짜는 프로그래머는 이를 활용하는데 더 집중할 수 있다.</p>
<h3 id="using-sequence">Using sequence</h3>
<p>코틀린에서 제공하는 <code>sequence</code> 라이브러리는 일련의 값들을 생성하는데 사용된다. 여기서 primes() 함수를 만들어 시작 숫자부터 무한대로 <code>Sequence&lt;Int&gt;</code>를 통해 다음 소수를 반환하는 코드를 짜보자. </p>
<pre><code class="language-kotlin">fun primes(start: Int): Sequence&lt;Int&gt; = sequence {
  println(&quot;Starting to look&quot;)
  var index = start

  while (true) {
    if (index &gt; 1 &amp;&amp; (2 until index).none { i -&gt; index % i == 0 }) {
      yield(index)
      println(&quot;Generating next after $index&quot;)
    }

    index++
  }
}

for (prime in primes(start = 17)) {
  println(&quot;Received $prime&quot;)
  if (prime &gt; 30) break
}

&quot;&quot;&quot;
실행결과

Starting to look
Received 17
Generating next after 17
Received 19
Generating next after 19
Received 23
Generating next after 23
Received 29
Generating next after 29
&quot;&quot;&quot;</code></pre>
<p><code>sequence</code> 함수는 다음과 같은 장점을 가지고 있다.</p>
<ul>
<li>컬렉션을 미리 생성해 놓을 필요가 없다. 즉 얼만큼의 값을 미리 만들어놔야 하는지 알필요가 없다.</li>
<li>시간에 따른 값 생성 비용을 감안할 필요 없이 이미 생성된 값을 사용하면 된다</li>
<li>lazy 하게 값이 필요할때마다 연산이 이루어지기 때문에 미리 계산해둘 필요가 없다.</li>
</ul>
<p><code>Sequence&lt;T&gt;</code> 와 비슷하게 iterator() 함수를 사용해 비슷한 예제를 만들 수 있다.</p>
<h3 id="using-iterator">Using iterator()</h3>
<pre><code class="language-kotlin">operator fun ClosedRange&lt;String&gt;.iterator(): Iterator&lt;String&gt; = iterator {
  val next = StringBuilder(start)
  val last = endInclusive

  while (last &gt;= next.toString() &amp;&amp; last.length &gt;= next.length) {
    val result = next.toString()

    val lastCharacter = next.last()

    if (lastCharacter &lt; Char.MAX_VALUE) {
      next.setCharAt(next.length - 1, lastCharacter + 1)        
    } else {
      next.append(Char.MIN_VALUE)        
    }

    yield(result)
  }
}

for (word in &quot;hell&quot;..&quot;help&quot;) { print(&quot;$word, &quot;) }</code></pre>
<p><code>iterator()</code> 함수는 <code>Iterator&lt;T&gt;</code>를 반환한다. 함수의 인자로 넘겨진 람다 표현식에서 다음 문자열을 생성하고 <code>yield()</code> 함수를 호출한다. </p>
]]></description>
        </item>
        <item>
            <title><![CDATA[educative - kotlin - 13]]></title>
            <link>https://velog.io/@sungjun-jin/educative-kotlin-13</link>
            <guid>https://velog.io/@sungjun-jin/educative-kotlin-13</guid>
            <pubDate>Mon, 19 Apr 2021 01:09:19 GMT</pubDate>
            <description><![CDATA[<h2 id="programming-recursion-and-memoization">Programming Recursion and Memoization</h2>
<p>재귀(recursion)을 활용하면 분할정복 문제를 해결할 수 있다. 
퀵정렬 예시</p>
<pre><code class="language-kotlin">fun sort(numbers: List&lt;Int&gt;): List&lt;Int&gt; =
  if (numbers.isEmpty())
    numbers
  else {
    val pivot = numbers.first()
    val tail = numbers.drop(1)
    val lessOrEqual = tail.filter { e -&gt; e &lt;= pivot }
    val larger = tail.filter { e -&gt; e &gt; pivot }

    sort(lessOrEqual) + pivot + sort(larger)
  }

println(sort(listOf(12, 5, 15, 12, 8, 19))) //[5, 8, 12, 12, 15, 19]</code></pre>
<p>코틀린은 재귀함수를 지원하지만 해당 함수에 해단 return type을 반드시 명시해줘야 한다(타입 추론 불가)</p>
<h3 id="recursive-vs-iterative">Recursive vs iterative</h3>
<p>재귀를 사용하려면 어느정도의 진입장벽은 있지만 한번 공부하면 유용하게 사용할 수 있다.</p>
<pre><code class="language-kotlin">import java.math.BigInteger

fun factorialRec(n: Int): BigInteger =
  if (n &lt;= 0) 1.toBigInteger() else n.toBigInteger() * factorialRec(n - 1)

println(factorialRec(5)) //120</code></pre>
<p>위 <code>factorialRec()</code> 함수는 인자가 0이하로 들어오면 1을 반환한다. 인자가 0보다 크면 input과 자기자신을 재귀호출하는 값을 반환한다.</p>
<p>만약 재귀함수를 사용하지 않고 일반 interative한 함수를 짠다면 다음과 같다.</p>
<pre><code class="language-kotlin">fun factorialIterative(n: Int) =
  (1..n).fold(BigInteger(&quot;1&quot;)) { product, e -&gt; product * e.toBigInteger()}</code></pre>
<p><code>fold()</code> 함수는 앞서 정리했던 <code>reduce()</code>함수와 비슷하다. <code>reduce()</code>와는 초기 값과 람다 즉, 2개의 인자를 받는다는 점이 다르다. 위 예시에서 가독성은 재귀를 활용한 방법이 좀 더 좋은것 같다.</p>
<h3 id="drawback-of-recursion">Drawback of recursion</h3>
<p>재귀가 일반적인 접근법이랑 다른점은 메모리 사용량이 많아 스택을 키운다는 것이다, 따라서 계산 스택이 위험레벨에 도달하면 프로그램이 터질 수 있다. 예를들어 위에서 작성한 함수에 인자를 50000으로 넘겨줬을 때</p>
<p>iterative한 방법을 사용하면 별 문제가 없지만</p>
<pre><code class="language-kotlin">println(factorialIterative(50000))</code></pre>
<p>재귀를 사용했을때 <code>StackOverflowError</code>를 발생시킬 수 있다.</p>
<pre><code class="language-kotlin">println(factorialRec(50000))</code></pre>
<p>실행결과</p>
<pre><code class="language-shell">java.lang.StackOverflowError
        at java.base/java.math.BigInteger.valueOf(BigInteger.java:1182) 
        at Largerecursive.factorialRec(largerecursive.kts:4)</code></pre>
<h3 id="tail-call-optimization-꼬리-호출-최적화">Tail Call Optimization (꼬리 호출 최적화)</h3>
<p>Tail Call이란 함수가 제일 마지막에 하는 일이 자기 자신을 호출하는 것이다. 코틀린에서 함수 앞에 <code>tailrec</code>이라는 키워드를 사용하면 코틀린이 마지막에 꼬리호출을 제거해준다. </p>
<pre><code class="language-kotlin">tailrec fun factorialRec(n: Int): BigInteger =
  if (n &lt;= 0) 1.toBigInteger() else n.toBigInteger() * factorialRec(n - 1)</code></pre>
<p>함수 안에 <code>n.toBigInteger() * factorialRec(n - 1)</code>에서 <code>factorialRec</code> 함수가 가장 마지막에서 호출된다고 생각하겠지만, function call이 반환되기 전에 곱셈 연산부터 수행된다. 따라서 <code>factorialRec()</code>에 대한 호출이 끝날때 까지 기다리게 되는데 여기서 스택에 무리를 주게 된다. </p>
<p>따라서 다음과 같이 코드를 살짝 수정하면 50000을 인자로 넘겨줘도 잘 동작한다.</p>
<pre><code class="language-kotlin">import java.math.BigInteger

tailrec fun factorial(n: Int, 
  result: BigInteger = 1.toBigInteger()): BigInteger =
    if (n &lt;= 0) result else factorial(n - 1, result * n.toBigInteger())

println(factorial(5)) //120

println(factorial(50000)) //No worries</code></pre>
<p>좀 더 내부적으로 확인해보기 위해 Factorial 객체를 만들고 재귀호출 방식의 함수와 꼬리 재귀 호출방식의 함수를 만들어보자.</p>
<pre><code class="language-kotlin">import java.math.BigInteger

object Factorial {
  fun factorialRec(n: Int): BigInteger =
    if (n &lt;= 0) 1.toBigInteger() else n.toBigInteger() * factorialRec(n - 1)

  tailrec fun factorial(n: Int, 
    result: BigInteger = 1.toBigInteger()): BigInteger =
      if (n &lt;= 0) result else factorial(n - 1, result * n.toBigInteger())
}</code></pre>
<p>코드를 컴파일하고 바이트 코드를 살펴보자</p>
<pre><code class="language-shell">kotlinc-jvm Factorial.kt
javap -c -p Factorial.class</code></pre>
<pre><code>Compiled from &quot;Factorial.kt&quot; public final class Factorial {
public final java.math.BigInteger factorialRec(int); Code:
...
      38: invokevirtual #23
                 // Method factorialRec:(I)Ljava/math/BigInteger;
...
44: invokevirtual #27
// Method java/math/BigInteger.multiply:(...)
47: dup
48: ldc
#29 // String this.multiply(other)
...
public final java.math.BigInteger factorial(int, java.math.BigInteger); Code:
...
       7: ifgt          14
      10: aload_2
      11: goto          76
...
56: invokevirtual #27
// Method java/math/BigInteger.multiply:(...) ...
      73: goto          0
      76: areturn</code></pre><p><code>factorialRec</code> 의 바이트코드를 확인해보면 <code>invokevirtual</code>이 재귀적으로 <code>fatorialRec()</code>을 호출하고 <code>multiply()</code>를 호출한다. </p>
<p><code>factorial()</code>의 바이트코드를 확인하면 <code>invokevirtual</code> 재귀 호출 없이 <code>ifgt</code>와 <code>goto</code>를 호출해 함수내에서 이동을 하고 있다. 재귀 호출의 과정이 iterative하고 돌고 있다는 증거다.</p>
<h3 id="memoization">Memoization</h3>
<blockquote>
<p>메모이제이션 (Memoization)은 연속해서 사용되는 연산 값을 함수 레벨에서 캐싱하는것 을 지칭하기 위해 사용하는 단어이다. 피보나치 수열에서 하위 계산값에 대한 메모이제이션을 사용하는 여부에 따라 시간복잡도가 천차만별로 달라지는 예시가 있다.</p>
</blockquote>
<h4 id="repetitive-computation">Repetitive computation</h4>
<p>위에서 예시로 든 피보나치에서 메모이제이션을 사용하지 않는다면?</p>
<pre><code class="language-kotlin">import kotlin.system.measureTimeMillis

fun fib(n: Int) : Long = when (n) {
  0, 1 -&gt; 1L
  else -&gt; fib(n - 1) + fib(n - 2)  
}

println(measureTimeMillis { fib(40) }) //About 3 millisconds
println(measureTimeMillis { fib(45) }) //More than 4 seconds</code></pre>
<p>피보나치에서 <code>f(n)</code>에 대한 값을 구하려면 <code>f(n-1)</code>, <code>f(n-2)</code>에 대한 연산결과가 필요하다. 따라서 n의 값이 증가할수록 하위 계산값들이 필요하기 때문에 연산시간은 기하급수적으로 증가한다. </p>
<p>예시로 <code>f(40)</code>의 실행시간은 0.003초이지만 <code>f(45)</code>를 돌려볼 경우 실행시간이 4초로 늘어난다. 그렇다면 <code>f(100)</code>은 안해볼련다.</p>
<p>문제를 해결하기 위해 메모이제이션을 확장해서 사용해보자. <code>memoize()</code> 함수를 사용해준다.</p>
<pre><code class="language-kotlin">fun &lt;T, R&gt; ((T) -&gt; R).memoize(): ((T) -&gt; R) {
  val original = this
  val cache = mutableMapOf&lt;T, R&gt;()

  return { n: T -&gt; cache.getOrPut(n) { original(n) } }
}</code></pre>
<p>원래 함수는 <code>this</code> 키워드를 사용해 <code>original</code>이라는 변수에 담고, 빈 값의 <code>cache</code>를 정의해준다. 마지막으로 <code>T</code>를  인자로 받고 <code>R</code> 타입을 반환하는 람다 표현식을 반환한다. 여기서 우리는 캐시를 확인해 값이 있으면 연산과정을 생략하고 저장된 값을 반환한다. 반대로 캐시안에 값이 없으면 해당 연산을 수행하고 값을 저장한다.</p>
<p>이제 피보나치 수열에서 메모이제이션을 구현해보자</p>
<pre><code class="language-kotlin">lateinit var fib: (Int) -&gt; Long

fib = { n: Int -&gt; 
  when (n) {
    0, 1 -&gt; 1L
    else -&gt; fib(n - 1) + fib(n - 2)
  }
}.memoize()</code></pre>
<p>맨 윗줄에서 <code>lateinit</code> 키워드를 사용하면 nullable하지 않은 프로퍼티 초기화를 미룰 수 있다. 이를 <code>늦은 초기화</code>라고 부른다. </p>
<p>여태까지 구현한걸 다 조합해서 실행시간을 비교해보자</p>
<pre><code class="language-kotlin">import kotlin.system.measureTimeMillis

fun &lt;T, R&gt; ((T) -&gt; R).memoize(): ((T) -&gt; R) {
  val original = this
  val cache = mutableMapOf&lt;T, R&gt;()

  return { n: T -&gt; cache.getOrPut(n) { original(n) } }
}

lateinit var fib: (Int) -&gt; Long

fib = { n: Int -&gt; 
  when (n) {
    0, 1 -&gt; 1L
    else -&gt; fib(n - 1) + fib(n - 2)
  }
}.memoize()

println(measureTimeMillis { fib(40) })
println(measureTimeMillis { fib(45) })
println(measureTimeMillis { fib(500) })</code></pre>
<p>실행결과</p>
<pre><code class="language-shell">0 
0 
1</code></pre>
<p>깔끔하다.</p>
<p><code>memoize()</code> 함수는 1개이상의 매개변수를 가진 모든 함수들에 대해서 사용할 수 있다. 람다 표현식을 memoization 버전으로 바꿀 수 있고 간결하다는 장점이 있다. 하지만 <code>var fib</code>를 위에서 정의해주고 다시 람다식을 할당하면 compliation error가 발생할 수 있다는 단점이 있다. 또한 <code>fib</code>가 <code>var</code>이라는 점이 찝찝하다. </p>
<h3 id="memoization-as-delegate">Memoization as delegate</h3>
<pre><code class="language-kotlin">import kotlin.system.measureTimeMillis
import kotlin.reflect.*

class Memoize&lt;T, R&gt;(val func: (T) -&gt; R) {
  val cache = mutableMapOf&lt;T, R&gt;()

  operator fun getValue(thisRef: Any?, property: KProperty&lt;*&gt;) = { n: T -&gt; 
    cache.getOrPut(n) { func(n) } }
}

val fib: (Int) -&gt; Long by Memoize {n: Int -&gt;
  when (n) {
    0, 1 -&gt; 1L
    else -&gt; fib(n - 1) + fib(n - 2)
  }
}

println(measureTimeMillis { fib(40) })
println(measureTimeMillis { fib(45) })
println(measureTimeMillis { fib(500) })</code></pre>
<p>앞서 달라진 점은 <code>fib</code>를 <code>val</code>로 선언했다는 점과 <code>getValue()</code>를 사용해 호출하는 함수를 사용해 캐싱을 해준다는 점이 있다. 조금 더 간결해졌다. </p>
<h3 id="dynamic-programming">Dynamic programming</h3>
<p>Dynamic Programming (동적 프로그래밍)은 재귀의 효율성을 극대화시키기 위해 메모이제이션을 사용하는 알고리즘 기법이다. 반복적인 재귀호출을 줄여 연산의 부담을 줄여준다.</p>
<p>앞서 사용했던 메모이제이션 위임 기법을 잘 알려진 DP 문제에 대입해보자.</p>
<p>유명한 막대 자리그(rod cutting) 문제이다</p>
<pre><code>막대 자르기 문제는 다음과 같습니다.

길이가 n인 막대와 i = 1,2,3,...,n에 대한 가격 p_i의 표가 주어지면 해당 막대를 잘라서 판매했을떄 얻을 수 있는 최대수익 r_n을 결정하는 것 입니다. 길이가 n인 막대의 가격 p_n이 충분히 비싸면 최적해는 자르지 않은 것일 수도 있습니다.</code></pre><p><img src="https://images.velog.io/images/sungjun-jin/post/22100b06-207c-4f9b-bc24-8a587fa79c6b/image.png" alt=""></p>
<p>Sudo Code 풀이</p>
<pre><code>maxPrice(length) =
  max {
                maxPrice(1) + maxPrice(length - 1), 
                maxPrice(2) + maxPrice(length - 2), 
                ...,
                maxPrice(length - 1) + maxPrice(1), price[length]
}</code></pre><p>메모이제이션을 적용한 문제풀이</p>
<pre><code class="language-kotlin">val prices = mapOf(1 to 2, 2 to 4, 3 to 6, 4 to 7, 5 to 10, 6 to 17, 7 to 17)

val maxPrice: (Int) -&gt; Int by Memoize { length: Int -&gt;
  val priceAtLength = prices.getOrDefault(length, 0)

  (1 until length).fold(priceAtLength) { max, cutLength -&gt;
    val cutPrice =  maxPrice(cutLength) + maxPrice(length - cutLength)

    Math.max(cutPrice, max)
  }
}

for (i in 1..7) {
  println(&quot;For length $i max price is ${maxPrice(i)}&quot;)
}</code></pre>
<p>실행결과</p>
<pre><code class="language-shell">For length 1 max price is 2
For length 2 max price is 4
For length 3 max price is 6
For length 4 max price is 8
For length 5 max price is 10
For length 6 max price is 17
For length 7 max price is 19</code></pre>
]]></description>
        </item>
        <item>
            <title><![CDATA[educative - kotlin - 12]]></title>
            <link>https://velog.io/@sungjun-jin/educative-kotlin-12</link>
            <guid>https://velog.io/@sungjun-jin/educative-kotlin-12</guid>
            <pubDate>Sun, 11 Apr 2021 11:09:29 GMT</pubDate>
            <description><![CDATA[<h2 id="fluency-in-kotlin">Fluency in Kotlin</h2>
<h4 id="need-for-operator-overloading">Need for operator overloading</h4>
<h4 id="연산자-오버로딩operator-overloading이란">연산자 오버로딩(operator overloading)이란?</h4>
<blockquote>
<p>연산자 오버로딩(operator overloading)은 객체 지향 컴퓨터 프로그래밍에서 다형성의 특정 경우로 다른 연산자들이 함수 인자를 통해서 구현을 할 때를 말한다. 연산자 오버로딩은 일반적으로 언어, 프로그래머, 또는 두 가지 모두에 의해 정의된다. 연산자 오버로딩은 사용자 정의 타입과 비슷한 수준을 허락하기 때문에 언어에 내장된 형식으로 구문을 지원한다. 그것은 쉽게 함수 호출을 사용하여 모방할 수 있다; 예를 들어, 정수 a, b, c를 생각하면 아래와 같은 식으로 작성이 가능하다. </p>
</blockquote>
<p>예시)
<code>a + b * c</code> -&gt; <code>add (a, multiply (b,c))</code></p>
<p>자바는 연산자 오버로딩을 제공하지 않지만, 코틀린은 연산자 오버로딩을 지원한다. </p>
<pre><code class="language-kotlin">bigInteger1.multiply(bigInteger2)

bigInteger1 * bigInteger2</code></pre>
<p>코틀린 연산자 오버로딩의 장점은 위 처럼 숫자 타입의 데이터 뿐만이 아닌 사용자가 직접 정의한 데이터타입의 연산도 가능하다는 점이다. 예를 들면 <code>today + 2</code>이다. </p>
<h3 id="how-is-it-done">How is it done?</h3>
<p>연산자 오버로딩 함수를 정의하기 위해서는 <code>operator</code> 키워드를 사용한다. 예시로 2개의 Pair 클래스에서 각각의 요소를 더해주는 + 연산자를 오버로딩 해준다, 그리고 실행시켜 보자</p>
<pre><code class="language-kotlin">operator fun Pair&lt;Int, Int&gt;.plus(other: Pair&lt;Int, Int&gt;) = 
  Pair(first + other.first, second + other.second)

val firstPair = Pair(0,0)
val secondPair = Pair(1,1)
println(firstPair + secondPair) // (1,1)</code></pre>
<p>복소수 연산을 활용한 다른 예제를 보자</p>
<pre><code class="language-kotlin">import kotlin.math.abs

data class Complex(val real: Int, val imaginary: Int) {
  operator fun times(other: Complex) =
    Complex(real * other.real - imaginary * other.imaginary,
        real * other.imaginary + imaginary * other.real)

  private fun sign() = if (imaginary &lt; 0) &quot;-&quot; else &quot;+&quot;                                         

  override fun toString() = &quot;$real ${sign()} ${abs(imaginary)}i&quot; 
}                 

println(Complex(4, 2) * Complex(-3, 4)) //-20 + 10i
println(Complex(1, 2) * Complex(-3, 4)) //-11 - 2i</code></pre>
<p>연산자와 해당 메소드를 정리한 테이블이다. </p>
<p><img src="https://images.velog.io/images/sungjun-jin/post/dc21903d-6227-4b4d-a647-d9fd29fdba5b/image.png" alt="">
<img src="https://images.velog.io/images/sungjun-jin/post/1e04deae-be59-4c08-a03f-8d27e18f612f/image.png" alt=""></p>
<h4 id="rules-to-follow">Rules to follow</h4>
<p>단항 연산자 오버로딩을 통해 예시를 들어보자. <code>+</code>는 <code>inc()</code>, <code>-</code>는 <code>dec()</code> 메소드를 사용한다. </p>
<pre><code class="language-kotlin">class Counter(val value: Int) {
  operator fun inc() = Counter(value + 1)

  operator fun dec() = Counter(value - 1)

  override fun toString() = &quot;$value&quot;
}

var counter = Counter(2)
println(counter)    //2
println(++counter)  //3
println(counter)    //3
println(counter++)  //3
println(counter)    //4</code></pre>
<p>여기서 <code>inc()</code> 메소드를 사용하면 새로운 값을 할당한 객체 반환하게 된다. 전위 연산자(++counter)를 사용하게 되면 할당전에 연산을 하게 되므로 연산을 적용시킨 후 객체를 반환한다. 후위 연산자(counter++)를 사용하게 되면 이전 값을을 반환 후 연산을 수행한다. 4번째 <code>println()</code>문의 결과가 3인 이유다.</p>
<p>연산자 오버로딩은 분명 좋은 기능이지만 남용할 경우 예상치 못한 side effect를 초래한다. 따라서 관련 코드를 작성할때는 코드를 읽는 제 3자에게 최대한 간결하고 정확하게, 오버로딩의 문맥에 맞게 <strong>변수 naming</strong>에 신경을 많이 써야 한다. </p>
<h3 id="injecting-using-extension-functions-and-properties">Injecting Using Extension Functions and Properties</h3>
<p>2개의 데이터 클래스(원, 좌표)가 있다고 가정해보자</p>
<pre><code class="language-kotlin">data class Point(val x: Int, val y: Int)
data class Circle(val cx: Int, val cy: Int, val radius: Int)</code></pre>
<p>원안에 위치하고 있는 좌표를 구해야 한다고 가정해보자. 각각의 클래스 안에 메소드를 정의해도 되지만 클래스 바깥쪽에서 <code>contains()</code>라는 메소드를 정의해보자. </p>
<pre><code class="language-kotlin">fun Circle.contains(point: Point) = 
  (point.x - cx) * (point.x - cx) + (point.y - cy) * (point.y - cy) &lt; 
    radius * radius</code></pre>
<p><code>Circle</code> 클래스의 외부임에도 불구하고, <code>Circle</code> 클래스의 내부 메소드인것 처럼 멤버변수(cx, cy)에 접근할 수 있다 (신기하네)</p>
<p>인스턴스를 생성해 실행시켜 보자.</p>
<pre><code class="language-kotlin">val circle = Circle(100, 100, 25)
val point1 = Point(110, 110)
val point2 = Point(10, 100)

println(circle.contains(point1)) //true
println(circle.contains(point2)) //false</code></pre>
<p>Circle 클래스 내부에서 생성된 메소드가 아님에도 불구하고 위처럼 해당 인스턴스에서 바로 <code>contains()</code> 메소드를 호출 할 수 있다.</p>
<p>패키지 내부에 확장 함수가 존재하면 코틀린 컴파일러는 해당 확장함수의 객체를 함수 첫번째 인자로 전달한다. 위에서는 <code>circle</code> 인스턴스를 <code>contains()</code>의 첫번째 인자로 전달한 셈이 된다. </p>
<p>여기서 확장 함수의 제약사항은 </p>
<ul>
<li>확장 함수와 클래스 내부 인스턴스 메소드의 이름이 동일하면 인스턴스 메소드가 우선이다</li>
<li>확장 메소드는 클래스에서 visible한 멤버에만 접근할 수 있다.</li>
</ul>
<h4 id="injecting-operators-using-extension-function">Injecting operators using extension function</h4>
<p>확장 함수를 연산자 오버로딩에서도 사용할 수 있다. <code>in</code> 연산자는 <code>contains()</code> 메소드에 대응한다는 점을 참고해서 예시를 보자</p>
<pre><code class="language-kotlin">operator fun Circle.contains(point: Point) =
  (point.x - cx) * (point.x - cx) + (point.y - cy) * (point.y - cy) &lt;
    radius * radius

println(circle.contains(point1)) //true 
println(point1 in circle) //true 
println(point2 in circle) //false    </code></pre>
<h4 id="injecting-properties-using-extension-properties">Injecting properties using extension properties</h4>
<p>확장 property도 구현이 가능하다. 물론 확장함수는 backing field를 사용할 수 없지만 property를 통해 클래스 멤버에 접근이 가능하다. 아래 예시에서는 getter를 사용한다.</p>
<pre><code class="language-kotlin">val Circle.area: Double
  get() = kotlin.math.PI * radius * radius

val circle = Circle(100, 100, 25)
println(&quot;Area is ${circle.area}&quot;) //1963.49....</code></pre>
<h4 id="injecting-into-third-party-classes">Injecting into third-party classes</h4>
<p>third-party 클래스에서도 확장함수 적용이 가능하다. 회문(palindrome) 여부에 따라 true, false를 반환하는 예시를 보자.</p>
<pre><code class="language-kotlin">fun String.isPalindrome(): Boolean {
  return reversed() == this
}

fun String.shout() = toUpperCase()

val str = &quot;dad&quot; 
println(str.isPalindrome()) //true 
println(str.shout()) //DAD</code></pre>
<p>위 예시에서도 볼 수 있듯이 확장함수는 여러곳에서 유연하게 적용이 가능하지만 아래처럼 이미 구현되어 있는, 자주 사용하고 있고 잘 알려진 내장 메소드를 확장 함수를 통해서 수정하는 것은 금기이다. 딱 봐도 이상하다 </p>
<pre><code class="language-kotlin">fun String.toLowerCase() = toUpperCase() //BAD CODE

val str = &quot;Please Don&#39;t&quot; 
println(str.toLowerCase()) //PLEASE DON&#39;T</code></pre>
<p>확장 함수를 정의해 다음 에러를 해결해보자</p>
<pre><code class="language-kotlin">for (word in &quot;hell&quot;..&quot;help&quot;) { print(&quot;$word, &quot;) } //ERROR 
//for-loop range must have an &#39;iterator()&#39; method</code></pre>
<p><code>ClosedRange&lt;T&gt;</code> 클래스에 iterator가 없어서 생기는 문제이다. 확장 함수와 연산자 오버로딩을 통해 문제를 해결할 수 있다.</p>
<ul>
<li><p>익명 객체로 iterator를 생성한다..</p>
</li>
<li><p><code>ClosedRange&lt;T&gt;</code>의 프로퍼티(start, endInclusive)를 사용해 iterator의 범위를 지정해줄 수 있다.</p>
</li>
<li><p><code>&gt;=</code>를 <code>compareTo()</code>를 통해 오버로딩 해준다.</p>
</li>
<li><p>가변 문자열은 <code>StringBuilder</code> 클래스에 담아준다.</p>
</li>
<li><p><code>kotlin.Char</code>의 + 연산은 알바펫 순서상 다음 문자를 반환한다. </p>
</li>
</ul>
<pre><code class="language-kotlin">operator fun ClosedRange&lt;String&gt;.iterator() =
  object: Iterator&lt;String&gt; {
    private val next = StringBuilder(start)
    private val last = endInclusive

    override fun hasNext() = 
      last &gt;= next.toString() &amp;&amp; last.length &gt;= next.length

    override fun next(): String {
      val result = next.toString()

      val lastCharacter = next.last()

      if (lastCharacter &lt; Char.MAX_VALUE) {
        next.setCharAt(next.length - 1, lastCharacter + 1)        
      } else {
        next.append(Char.MIN_VALUE)        
      }

      return result
    }
  }

for (word in &quot;hell&quot;..&quot;help&quot;) { print(&quot;$word, &quot;) }
// hell, helm, heln, helo, help,</code></pre>
<h4 id="injecting-static-methods">Injecting static methods</h4>
<p>static 메소드에 확장함수를 적용해본 예시이다.</p>
<pre><code class="language-kotlin">fun String.Companion.toURL(link: String) = java.net.URL(link)

val url: java.net.URL = String.toURL(&quot;https://pragprog.com&quot;)</code></pre>
<h4 id="injecting-from-within-a-class">Injecting from within a class</h4>
<p>여태까지는 클래스 내부가 아닌 top-level에서 확장함수를 사용해봤지만 클래스 내부에서도 확장함수를 사용할 수 있다.</p>
<pre><code class="language-kotlin">class Point(x: Int, y: Int) {
  private val pair = Pair(x, y)

  private val firstsign = if (pair.first &lt; 0) &quot;&quot; else &quot;+&quot;
  private val secondsign = if (pair.second &lt; 0) &quot;&quot; else &quot;+&quot;

  override fun toString() = pair.point2String()

  fun Pair&lt;Int, Int&gt;.point2String() =
    &quot;(${firstsign}${first}, ${this@Point.secondsign}${this.second})&quot;
}

println(Point(1, -3)) //(+1, -3)
println(Point(-3, 4)) //(-3, +4)

fun Pair&lt;Int, Int&gt;.point2String() =
  &quot;(${firstsign}${first}, ${this@Point.secondsign}${this.second})&quot;</code></pre>
<p>앞서 작성했던 <code>Point</code> 클래스와는 다르게 x,y 좌표를 <code>pair</code>라는 멤버 변수에 저장했다. <code>firstsing, secondsign</code>이라는 private 프로퍼티도 추가했다.</p>
<p>point2String() 이라는 확장 함수를 오버라이딩한 toString()을 내부에서 호출하는 구조다. 확장 함수가 클래스 내부에서 생성되어 2개의 receiver(this, this@Point)가 있다. 각각 extension receiver, dispatch receiver라고 한다. </p>
<p><img src="https://images.velog.io/images/sungjun-jin/post/b11808cc-38e2-416a-934a-e8c98a75a7d6/image.png" alt=""></p>
<h3 id="extending-functions">Extending Functions</h3>
<p>코틀린에서 함수는 객체로 취급된다. <code>andThen()</code> 메소드를 사용해 확장함수를 구현한 예제를 보자. </p>
<pre><code class="language-kotlin">fun &lt;T, R, U&gt; ((T) -&gt; R).andThen(next: (R) -&gt; U): (T) -&gt; U =
  { input: T -&gt; next(this(input)) }</code></pre>
<p><code>andThen()</code>함수는 매개변수화 타입인 T를 가지고 R 타입을 리턴한다. <code>andThen()</code> 함수로 넘어가는 인자는 R 타입을 인자로 받는 함수여야 한다. 그리고 이 함수는 U라는 매개변수 타입을 반환한다. </p>
<p>다른 예제</p>
<pre><code class="language-kotlin">fun increment(number: Int): Double = number + 1.toDouble()
fun double(number: Double) = number * 2

val incrementAndDouble = ::increment.andThen(::double)

println(incrementAndDouble(5)) //12.0</code></pre>
<h4 id="the-infix-notation">The infix notation</h4>
<p>infix notation(중위 표기법)이란 두 개의 피연산자 사이에 연산자가 존재하는 표현 방식이다
ex) <code>X+Y, X-Y</code></p>
<p>중괄호와 dot notation은 평상시 코드를 칠때도 많이 사용하지만 없을때 코드가 더 깔끔해지는 경우도 있다</p>
<pre><code class="language-java">if(obj.instanceOf(String)) {} // 노 깔끔

if(obj instanceof String) {} // 깔끔</code></pre>
<p>더 깔끔해진 밑에 코드는 중위 표기법을 사용한 예시가 된다. 
<code>피연산자(obj, String) 연산자(instanceof)</code></p>
<p>비슷한 예시</p>
<pre><code class="language-kotlin">println(circle.contains(point1)) //true
println(point1 in circle) //true</code></pre>
<p><code>infix</code> 키워드를 사용하면 아래와 같이 연산자를 오버로딩하는 것 처럼 사용하면 중위 표기법을 오버라이딩 할 수 있다.</p>
<pre><code class="language-kotlin">operator infix fun Circle.contains(point: Point) =
  (point.x - cx) * (point.x - cx) + (point.y - cy) * (point.y - cy) &lt;
    radius * radius

println(circle contains point1) //true</code></pre>
<h4 id="behavior-of-the-four-methods">Behavior of the four methods</h4>
<p><code>also(), apply(), let(), run()</code></p>
<p>코틀린에서는 이렇게 생긴 확장함수들이 있다. 4개의 함수 모두 람다 표현식을 인자로 받고 그에 따른 결과를 반환한다. </p>
<pre><code class="language-kotlin">println(String.format(&quot;%-10s%-10s%-10s%-10s%-10s&quot;,
&quot;Method&quot;, &quot;Argument&quot;, &quot;Receiver&quot;, &quot;Return&quot;, &quot;Result&quot;))
println(&quot;===============================================&quot;)

val result1 = str.let { arg -&gt;                        
    print(String.format(format, &quot;let&quot;, arg, this, result))
    result
}
println(String.format(&quot;%-10s&quot;, result1))

val result2 = str.also { arg -&gt;                        
    print(String.format(format, &quot;also&quot;, arg, this, result))
    result
}
println(String.format(&quot;%-10s&quot;, result2))

val result3 = str.run {
    print(String.format(format, &quot;run&quot;, &quot;N/A&quot;, this, result))
    result
}
println(String.format(&quot;%-10s&quot;, result3))

val result4 = str.apply {
    print(String.format(format, &quot;apply&quot;, &quot;N/A&quot;, this, result))
    result
}
println(String.format(&quot;%-10s&quot;, result4))</code></pre>
<p>실행결과</p>
<pre><code class="language-shell">Method    Argument  Receiver  Return    Result    
===============================================
let       context   lexical   RESULT    RESULT    
also      context   lexical   RESULT    context   
run       N/A       context   RESULT    RESULT    
apply     N/A       context   RESULT    context</code></pre>
<p><code>let()</code> 함수는 자신이 호출된 context를 람다 표현식으로 전달한다. 람다의 this, 즉 receiver의 scope는 lexical 하다. </p>
<p><code>also()</code> 함수도 자신이 호출된 context를 람다 표현식으로 전달한다. receiver의 scope또한 lexical하지만 람다식의 결과를 무시하고 객체의 context를 결과로 반환한다.  이는 <code>also()</code>의 리턴 타입이 Unit이기 때문이다.</p>
<p><code>run()</code> 함수는 인자를 넘겨주지 않고 receiver의 context가 리턴된다.</p>
<p><code>apply()</code>는 <code>run()</code>과 동일하나 context가 리턴된다는 차이점이 있다.</p>
<p>요약하자면,</p>
<ul>
<li>위 4개의 메소드는 인자로 넘어가는 람다를 실행한다.</li>
<li>let()과 run()은 람다의 결과를 caller에 반환한다.</li>
<li>also()와 apply()는 람다의 결과를 무시하고 객체의 context를 반환한다</li>
<li>run()과 apply()는 호출한 context에서 람다를 실행한다.</li>
</ul>
<h4 id="from-a-verbose-and-noisy-code">From a verbose and noisy code</h4>
<p><code>Mailer</code> 라는 클래스를 정의해 실습해보자</p>
<pre><code class="language-kotlin">class Mailer {
    val details = StringBuilder()
    fun from(addr: String) = details.append(&quot;from $addr...\n&quot;)
    fun to(addr: String) = details.append(&quot;to $addr...\n&quot;)
    fun subject(line: String) = details.append(&quot;subject $line...\n&quot;) 
    fun body(message: String) = details.append(&quot;body $message...\n&quot;) 
    fun send() = &quot;...sending...\n$details&quot;
}</code></pre>
<p>각 메소드를 실행시켜보자</p>
<pre><code class="language-kotlin">val mailer = Mailer() 
mailer.from(&quot;builder@agiledeveloper.com&quot;) 
mailer.to(&quot;venkats@agiledeveloper.com&quot;) 
mailer.subject(&quot;Your code sucks&quot;) 
mailer.body(&quot;...details...&quot;)
val result = mailer.send() 
println(result)</code></pre>
<p>실행결과</p>
<pre><code class="language-shell">...sending...
from builder@agiledeveloper.com... 
to venkats@agiledeveloper.com... 
subject Your code sucks...
body ...details......</code></pre>
<h4 id="removing-repetitive-references-with-apply">Removing repetitive references with apply()</h4>
<p>apply()를 사용해 더 간결하게 작성할 수 있다. 호출하는 부분의 람다를 실행하고 context 객체를 caller에게 반환한다. 아래처럼 메소드 chaining이 가능하다. 2개의 방식으로 작성할 수 있다.</p>
<pre><code class="language-kotlin">val mailer = 
    Mailer()
        .apply { from(&quot;builder@agiledeveloper.com&quot;) }
        .apply { to(&quot;venkats@agiledeveloper.com&quot;) }
        .apply { subject(&quot;Your code sucks&quot;) }
        .apply { body(&quot;details&quot;) }

val result = mailer.send()

val mailer = Mailer().apply { 
    from(&quot;builder@agiledeveloper.com&quot;)
    to(&quot;venkats@agiledeveloper.com&quot;)
    subject(&quot;Your code sucks&quot;)
    body(&quot;details&quot;) 
}

val result = mailer.send()
println(result)</code></pre>
<h4 id="getting-results-using-run">Getting results using run()</h4>
<p>run()은 apply()와는 다르게 람다의 결과를 반환한다. target object의 context로써 람다식을 실행한다. 람다 내부에서 마지막 표현식의 결과를 가지고 싶을때 사용해준다.</p>
<pre><code class="language-kotlin">val result = Mailer().run { 
    from(&quot;builder@agiledeveloper.com&quot;)
    to(&quot;venkats@agiledeveloper.com&quot;)
    subject(&quot;Your code sucks&quot;)
    body(&quot;details&quot;) 
    send()
}

println(result)</code></pre>
<h4 id="passing-an-object-as-argument-using-let">Passing an object as argument using let</h4>
<p>인스턴스를 다른 메소드에 인자로 전달할때 사용한다.</p>
<pre><code class="language-kotlin">fun createMailer() = Mailer()

fun prepareAndSend(mailer: Mailer) = mailer.run {
    from(&quot;builder@agiledeveloper.com&quot;)
    to(&quot;venkats@agiledeveloper.com&quot;)
    subject(&quot;Your code suks&quot;)
    body(&quot;details&quot;)
    send()
  }</code></pre>
<h4 id="chaining-void-functions-using-also">Chaining void functions using also</h4>
<p>Unit 객체를 return 해주는 void 메소드를 체이닝하는 경우 사용해준다.</p>
<pre><code class="language-kotlin">fun prepareMailer(mailer: Mailer):Unit {
  mailer.run {
    from(&quot;builder@agiledeveloper.com&quot;)
    to(&quot;venkats@agiledeveloper.com&quot;)
    subject(&quot;Your code suks&quot;)
    body(&quot;details&quot;)
  }
}

fun sendMail(mailer: Mailer): Unit {
  mailer.send()
  println(&quot;Mail sent&quot;)
}</code></pre>
<h3 id="implicit-receivers">Implicit Receivers</h3>
<pre><code class="language-kotlin">var length = 100

val printIt: (Int) -&gt; Unit = { n: Int -&gt;
  println(&quot;n is $n, length is $length&quot;)
}

printIt(6) //n is 6, length is 100</code></pre>
<p>위 예시에서 <code>printIt</code>는 정수형을 인자로 받는 람다식이고 Unit을 리턴하는 void 메소드이다. 람다 내부에서는 <code>length</code> 프로퍼티를 출력한다. 여기서 <code>length</code>는 lexcial scoping 원칙에 따라 람다 외부에 있는 <code>var length = 100</code>를 가르킨다.</p>
<p>다른 예시를 보자</p>
<pre><code class="language-kotlin">var length = 100

val printIt: String.(Int) -&gt; Unit = { n: Int -&gt;
  println(&quot;n is $n, length is $length&quot;)
}

printIt(&quot;Hello&quot;, 6) // n is 6, length is 5
&quot;Hello&quot;.printIt(6) //멤버 함수인것처럼 실행 가능
// // n is 6, length is 5</code></pre>
<p>여기서 위와 바뀐 점은 <code>var printIt: String.(Int)</code>이다. 이는 컨텍스트 내부에서 람다를 실행시키는 것을 의미한다. 따라서 위처럼 컨텍스트 내부의 length 프로퍼티가 적용된다.</p>
<h4 id="multiple-scopes-with-receivers">Multiple scopes with receivers</h4>
<pre><code class="language-kotlin">fun top(func: String.() -&gt; Unit) = &quot;hello&quot;.func()

fun nested(func: Int.() -&gt; Unit) = (-2).func()

top {
  println(&quot;In outer lambda $this and $length&quot;)

  nested {
    println(&quot;in inner lambda $this and ${toDouble()}&quot;)
    println(&quot;from inner through receiver of outer: ${length}&quot;)
    println(&quot;from inner to outer receiver ${this@top}&quot;)
  }
}</code></pre>
<p>실행결과</p>
<pre><code class="language-shell">In outer lambda hello and 5
in inner lambda -2 and -2.0
from inner through receiver of outer: 5 
from inner to outer receiver hello</code></pre>
]]></description>
        </item>
        <item>
            <title><![CDATA[educative - kotlin - 11]]></title>
            <link>https://velog.io/@sungjun-jin/educative-kotlin-11</link>
            <guid>https://velog.io/@sungjun-jin/educative-kotlin-11</guid>
            <pubDate>Sun, 04 Apr 2021 10:22:40 GMT</pubDate>
            <description><![CDATA[<h3 id="external-vs-internal-iterators">External vs. Internal Iterators</h3>
<h4 id="external-iterator-외부-반복자">External Iterator (외부 반복자)</h4>
<p>외부 반복자는 배열에서 요소를 for나 while 등의 루프로 하나씩 꺼내서 직접 조작하는 방식이다</p>
<pre><code class="language-kotlin">val numbers = listOf(10, 12, 15, 17, 18, 19)

for (i in numbers) {
    if (i % 2 == 0) {
        print(&quot;$i, &quot;) //10, 12, 18, 
    }
}</code></pre>
<p>추가적으로 break나 continue문을 활용해 직접 루프를 쉽게 제어할 수 있다.</p>
<h4 id="internal-iterator-내부-반복자">Internal Iterator (내부 반복자)</h4>
<p>내부 반복자는 반복당 수행할 액션만을 정의하고 그 액션을 컬렉션이 받아 내부적으로 자동 처리하는 방식이다.</p>
<pre><code class="language-kotlin">numbers.filter { e -&gt; e % 2 == 0 }
    .forEach { e -&gt; print(&quot;$e, &quot;) } //10, 12, 18,</code></pre>
<p>여기서 <code>filter()</code> 함수는 리스트를 돌면서 인자로 들어온 람다 표현식의 조건에 대한 return 값이 <code>true</code>인 요소들만 필터링 한다. </p>
<p>필터링된 요소는<code>forEach()</code> 함수를 통해 컬렉션 타입의 개수만큼 람다식으로 들어온 구문을 반복 실행한다.</p>
<h4 id="which-is-better">Which is Better?</h4>
<p>예시를 확인해보자. 짝수로된 숫자의 제곱을 담는 리스트를 가져오려고 한다. 외부 반복자를 이용하면 다음과 같이 코드를 작성할 수 있다.</p>
<pre><code class="language-kotlin">val doubled = mutableListOf&lt;Int&gt;()

for (i in numbers) {
    if (i % 2 == 0) {
        doubled.add(i * 2)
    }
}                    

println(doubled) //[20, 24, 36]</code></pre>
<p>흔히 보는 익숙한 방식이다. 나쁘지 않다. 하지만 빈 리스트를 코드 시작부분에서 생성해야 하는 부분이 살짝 거슬린다. 내부 반복자를 활용해보자.</p>
<pre><code class="language-kotlin">val doubledEven = numbers.filter { e -&gt; e % 2 == 0 }
    .map { e -&gt; e * 2 }

println(doubledEven) //[20, 24, 36]</code></pre>
<p>낯설지만 확실히 간결하다. 어떤 iteration 방식을 사용할지는 본인 취향이다. 외부 반복자를 사용하고 내부 반복자도 사용해보고 둘 중 하나가 더 좋으면 리팩토링하면 된다. </p>
<h4 id="internal-iterators">Internal Iterators</h4>
<p>대표적인 internal iterator를 정리해보자.</p>
<ul>
<li>filter() : 람다의 조건식이 True가 되는 요소들을 리스트로 만들어준다. </li>
<li>map(): 람다를 통해 리스트 내부 요소들의 형태를 바꾼다. </li>
<li>reduce(): 전체 리스트를 누적 연산을 통해 하나의 데이터로 모아준다. </li>
</ul>
<p>간단하게 <code>Person</code> 클래스로 이루어진 <code>People</code> 컬렉션을 가지고 예시를 들어보자</p>
<pre><code class="language-kotlin">data class Person(val firstName: String, val age: Int)

val people = listOf(
  Person(&quot;Sara&quot;, 12),
  Person(&quot;Jill&quot;, 51),
  Person(&quot;Paula&quot;, 23),
  Person(&quot;Paul&quot;, 25),
  Person(&quot;Mani&quot;, 12),
  Person(&quot;Jack&quot;, 70),
  Person(&quot;Sue&quot;, 10))</code></pre>
<p>앞서 정리했던 3개의 internal iterator를 사용해 20세 이상의 대문자 이름을 구하는 코드를 작성해보자</p>
<pre><code class="language-kotlin">val result = people.filter { person -&gt; person.age &gt; 20 }
.map { person -&gt; person.firstName }
.map { name -&gt; name.toUpperCase() }
.reduce { names, name -&gt; &quot;$names, $name&quot; }

println(result) //JILL, PAULA, PAUL, JACK</code></pre>
<p>위 코드의 연산 과정은 다음 그림과 같다
<img src="https://images.velog.io/images/sungjun-jin/post/78468e34-4d46-4d5c-9260-2c1f74ce070d/image.png" alt=""></p>
<p><code>reduce()</code> 대신 <code>joinToString(&quot;,&quot;)</code> 으로 대체 가능하다. 더 직관적이고 error prone하다. </p>
<h4 id="getting-the-first-and-the-last">Getting the first and the last</h4>
<p>장고의 ORM과 비슷하게 <code>first()</code> 와 <code>last()</code>를 활용하면 리스트의 첫번째, 마지막 요소를 가져올 수 있다.</p>
<pre><code class="language-kotlin">val nameOfFirstAdult = people.filter { person -&gt; person.age &gt; 17 }
    .map { person -&gt; person.firstName }
    .first()

println(nameOfFirstAdult) //Jill</code></pre>
<h4 id="flatten-and-flatmap">flatten and flatMap</h4>
<p><code>flatten()</code>은 해당 리스트를 하나의 list로 펼치는 기능을 하고 <code>flatMap()</code>은 주어진 람다로 Map을 만들고 이를 다시 flat한 리스트로 만들어주는 함수이다.</p>
<pre><code class="language-kotlin">val families = listOf(
    listOf(Person(&quot;Jack&quot;, 40), Person(&quot;Jill&quot;, 40)),
    listOf(Person(&quot;Eve&quot;, 18), Person(&quot;Adam&quot;, 18)))

println(families.size) //2
println(families.flatten().size) //4

val namesAndReversed3 = people.map { person -&gt; person.firstName }
    .map(String::toLowerCase)  
    .flatMap { name -&gt; listOf(name, name.reversed())}

println(namesAndReversed3.size) //14</code></pre>
<p>size 프로퍼티는 리스트의 길이를 반환한다. <code>flatten()</code>, <code>flatMap()</code> 함수를 사용하기 전후의 값 차이를 확인해보자.</p>
<h4 id="sorting">Sorting</h4>
<p><code>sortedBy(), sortedByDescending()</code>를 사용하면 원본 리스트를 바꾸지 않고 주어진 람다를 기준으로 정렬된 리스트를 반환한다.</p>
<pre><code class="language-kotlin">val namesSortedByAge = people.filter { person -&gt; person.age &gt; 17 }
    .sortedBy { person -&gt; person.age }
    .map { person -&gt; person.firstName }

    // .sortedByDescending { person -&gt; person.age }
  //[Jack, Jill, Paul, Paula]

println(namesSortedByAge) //[[Paula, Paul, Jill, Jack]</code></pre>
<h4 id="grouping-objects">Grouping objects</h4>
<p><code>groupBy()</code> 함수를 사용해 group by 연산을 할 수 있다. </p>
<pre><code class="language-kotlin">val groupBy1stLetter = people.groupBy { person -&gt; person.firstName.first() }

println(groupBy1stLetter)
//{S=[Person(firstName=Sara, age=12), Person(firstName=Sue, age=10)], J=[...

val namesBy1stLetter = 
    people.groupBy({ person -&gt; person.firstName.first() }) {
        person -&gt; person.firstName
    }

println(namesBy1stLetter)
//{S=[Sara, Sue], J=[Jill, Jack], P=[Paula, Paul], M=[Mani]}</code></pre>
<h3 id="sequences-for-lazy-evaluation">Sequences for Lazy Evaluation</h3>
<p>Kotlin standard library는 컬렉션과 함께 또 다른 컨테이너 타입인 sequence (<code>Sequence&lt;T&gt;</code>) 를 가지고 있다. Sequence는 컬렉션을 감싸고 있는 일종의 Wrapper이다. Collection과 비교해서 lazy하다는 특성을 가지고 있다. </p>
<p>컬렉션에서 <code>map()</code>이나 <code>filter()</code>를 사용하는 경우 퍼포먼스 측면해서 생각해야 할 점이 있다. 위 함수를 거칠 때 마다 매번 list가 연산되어 return 되므로 컬렉션의 길이가 길면 프로세스에 부하가 걸리게 된다. 하지만 Sequence의 경우 최종 메소드가 실행될때 연산 전체가 수행되므로 부하가 상대적으로 덜 하다. </p>
<p>따라서 컬렉션에 길이에 따라 internal iterator 혹은 sequence를 사용할지 정해야 한다. *<em>당연히 컬렉션의 길이가 크면 lazy한 sequence를 사용하는게 좋다. *</em></p>
<pre><code class="language-kotlin">fun isAdult(person: Person): Boolean { 
    println(&quot;isAdult called for ${person.firstName}&quot;) 
    return person.age &gt; 17
}
fun fetchFirstName(person: Person): String { 
    println(&quot;fetchFirstName called for ${person.firstName}&quot;) 
    return person.firstName
}

val nameOfFirstAdult = people 
    .filter(::isAdult) 
    .map(::fetchFirstName) 
    .first()

println(nameOfFirstAdult)</code></pre>
<p>위 코드에서도 마찬가지로 <code>filter(), map()</code> 을 사용하는 두 함수는 iteration을 돌면서 내부적으로 새로운 리스트를 계속 생성한다. 결국 얻고자 하는건 리스트의 첫번째 요소이므로 실행결과는 확인해보면 리소스 낭비가 심하다는 것을 알 수 있다.</p>
<p>실행결과</p>
<pre><code>isAdult called for Sara
isAdult called for Jill
isAdult called for Paula
isAdult called for Paul
isAdult called for Mani
isAdult called for Jack
isAdult called for Sue
fetchFirstName called for Jill
fetchFirstName called for Paula
fetchFirstName called for Paul
fetchFirstName called for Jack
Jill</code></pre><p>이런 부하는 컬렉션의 사이즈가 클수록 더 커진다. sequence를 사용해 연산의 부담을 줄여줄 수 있다.</p>
<pre><code class="language-kotlin">val nameOfFirstAdult = people.asSequence() 
    .filter(::isAdult) 
    .map(::fetchFirstName) 
    .first()

println(nameOfFirstAdult)</code></pre>
<p>실행결과</p>
<pre><code>isAdult called for Sara
isAdult called for Jill
fetchFirstName called for Jill
Jill</code></pre><h4 id="infinite-sequences">Infinite Sequences</h4>
<p><code>generateSequence(seed: T&gt;, nextFunction: () -&gt; T?)</code>를 사용해 무한 Sequence를 생성할 수 있다. 첫번째 인자인 seed에 받아진 값을 첫 요소로 생성하고, 두 번째 인자인 nextFunction을 통해 함수를 통과한 요소를 무한히 생성할 수 있다.</p>
<pre><code class="language-kotlin">tailrec fun nextPrime(n: Long): Long =
  if (isPrime(n + 1)) n + 1 else nextPrime(n + 1)

val primes = generateSequence(5, ::nextPrime)</code></pre>
<p>tailrec은 tail recursive라는 의미로 추가적인 연산이 없이 자신 스스로 재귀적으로 호출하다가 어떤 값을 리턴하는 함수를 의미한다. <code>StackOverflowError</code>를 방지해준다. </p>
<p>위 예제를 보면 벌써 무한대의 컬렉션이 이미 생성된거 같지만 사실 직접적인 값을 호출하기 전까지는 아무 컬렉션도 생성되지 않는다. 이제 직접 호출해보자</p>
<pre><code class="language-kotlin">System.out.println(primes.take(6).toList()) //[5, 7, 11, 13, 17, 19]</code></pre>
<p><code>take()</code>  메소드를 사용하면 인자로 주어진 길이의 리스트만 생성해준다. 이 때 실제로 <code>generateSequence</code>의 연산이 실행되는 것이다. </p>
<p>다음과 같이도 사용할 수 있다.</p>
<pre><code class="language-kotlin">val primes = sequence { 
    var i: Long = 0
    while (true) { 
        i++
        if (isPrime(i)) { 
            yield(i)
        } 
    }
}
//drop()을 사용해 처음 두번째까지의 요소를 삭제
println(primes.drop(2).take(6).toList()) //[5, 7, 11, 13, 17, 19]</code></pre>
]]></description>
        </item>
        <item>
            <title><![CDATA[educative - kotlin - 10]]></title>
            <link>https://velog.io/@sungjun-jin/educative-kotlin-10</link>
            <guid>https://velog.io/@sungjun-jin/educative-kotlin-10</guid>
            <pubDate>Sun, 04 Apr 2021 08:40:26 GMT</pubDate>
            <description><![CDATA[<h2 id="functional-programming-with-lambdas">Functional Programming with Lambdas</h2>
<h3 id="imperative-vs-declarative">Imperative vs Declarative</h3>
<p>만약 <code>name</code>이라는 이름이 담긴 리스트에서 <code>Nemo</code>라는 이름을 찾고 싶다면?</p>
<ul>
<li><strong>imperative style(명령형)</strong> : <code>name</code> 리스트를 돌면서 하나씩 비교<pre><code class="language-kotlin">for (name in names) {
  if (name.equals(&quot;Nemo&quot;)) {
      return true
  }
}
</code></pre>
</li>
</ul>
<p>return false</p>
<pre><code>
- **declarative style(선언형)** : ```contains``` 메소드 호출
```kotlin
val names = listOf(&quot;Nemo&quot;, &quot;Tom&quot;, &quot;James&quot;)
val result = names.contains(&quot;Nemo&quot;)
println(result) // true</code></pre><p><img src="https://images.velog.io/images/sungjun-jin/post/a16bc2f7-c6b1-441c-b14d-03cddb38175d/image.png" alt=""></p>
<p>Imperative Programming 은 How 의 개념으로 어떻게 결과를 얻을 것인지 하나하나 생각하면서 프로그래밍하는 것이라고 생각할 수 있다. 즉, 어떤 연산을 수행할지 미리 정해주는 방식이다.</p>
<p>반면 Declarative Programming 은 What 의 개념으로 어떤 결과를 얻고 싶은지를 생각하면서 프로그래밍하는 것으로 생각할 수 있다. 즉, 값들을 명시하는 방식으로 프로그래밍을 하게 된다.</p>
<pre><code class="language-kotlin">// Imperative
var doubleOfEven = mutableListOf&lt;Int&gt;()

for (i in 1..10) {
    if (i % 2 == 0) {
        doubleOfEven.add(i * 2)
    }
}

println(doubleOfEven) //[4, 8, 12, 16, 20]

// Declarative
val doubleOfEven = (1..10)
    .filter { e -&gt; e % 2 == 0}
    .map { e -&gt; e * 2 }

println(doubleOfEven) //[4, 8, 12, 16, 20]</code></pre>
<h3 id="lambda-expressions">Lambda Expressions</h3>
<p>코틀린에서 람다 표현식 syntax는 다음과 같다. Return 타입과 함수 이름이 없다.</p>
<pre><code class="language-kotlin">{ parameter list -&gt; body }</code></pre>
<ul>
<li>중괄호로 감싼다 {}</li>
<li>인자와 본문은 -&gt;로 구분한다</li>
<li>인자는 ()로 감싸지 않는다</li>
<li>인자는 타입추론이 가능하므로 타입 명시를 생략할 수 있다</li>
<li>변수에 람다식을 담는 경우에는 인자의 타입을 생략할 수 없다</li>
</ul>
<h4 id="passing-lambdas">passing lambdas</h4>
<pre><code class="language-kotlin">fun isPrime(n: Int) = n &gt; 1 &amp;&amp; (2 until n).none({ i: Int -&gt; n % i == 0 })
fun isPrime(n: Int) = n &gt; 1 &amp;&amp; (2 until n).none { i -&gt; n % i == 0 } //괄호 생략 가능

fun isPrime(n: Int) = n &gt; 1 &amp;&amp; (2 until n).none { n % it == 0 } //인자가 1개면 it(implicit parameter)로 대체 가능</code></pre>
<p>여기서 <code>none()</code>은 매칭되는 element 가 없으면 <code>true</code>를 리턴한다</p>
<h4 id="receiving-lambdas">Receiving lambdas</h4>
<pre><code class="language-kotlin">fun walk1To(action: (Int) -&gt; Unit, n: Int) = 
  (1..n).forEach { action(it) }

walk1To({ i -&gt; print(i) }, 5) //12345</code></pre>
<p>첫번째 인자를 iteration을 돌 범위를 지정해주고, 두번째 인자는 첫번째 인자로 들어온 값으로 람다 식을 호출한다.</p>
<p><strong>Tip!</strong>
코드의 가독성을 위해 람다 표현식으로 넘어가는 인자는 맨 마지막에 두는것이 좋다.</p>
<pre><code class="language-kotlin">fun walk1To(n: Int, action: (Int) -&gt; Unit) = (1..n).forEach { action(it) }

walk1To(5, { i -&gt; print(i) })
walk1To(5) { i -&gt; print(i) }
walk1To(5) { print(it) }</code></pre>
<h3 id="using-functional-references">Using functional references</h3>
<p>인자로 별다른 연산을 수행하지 않는 람다식 같은 경우에는 다음과 같은 식으로 간략화 할 수 있다.</p>
<pre><code class="language-kotlin">({x -&gt; someMethod(x)})

(::someMethod) // 간단하게 사용 가능</code></pre>
<p>앞서 만들었던 <code>walk1To()</code> 함수의 호출을 더 간단하게 해보자</p>
<pre><code class="language-kotlin">fun walk1To(n: Int, action: (Int) -&gt; Unit) = (1..n).forEach { action(it) }

walk1To(5, { i -&gt; print(i) }) // 이거를 
walk1To(5, ::print) // 이렇게 간단하게 사용 가능</code></pre>
<h3 id="functions-returning-functions">Functions returning functions</h3>
<p>만약 각자의 이름을 가진 컬렉션에서 길이가 4 혹은 5인 이름을 찾아야 한다고 가정해보자. <code>find()</code> 함수는 람다를 만족하는 한개의 요소를 찾을 수 있다.</p>
<pre><code class="language-kotlin">val names = listOf(&quot;Pam&quot;, &quot;Pat&quot;, &quot;Paul&quot;, &quot;Paula&quot;)

println(names.find {name -&gt; name.length == 5 }) //Paula

println(names.find { name -&gt; name.length == 4 }) //Paul</code></pre>
<p>위 코드는 찾고자 하는 길이에 따라 똑같은 코드를 반복해서 써야하는 번거로움이 있다. 람다를 return 하는 함수로 다시 리팩토링 해보자</p>
<pre><code class="language-kotlin">fun predicateOfLength(length: Int): (String) -&gt; Boolean {
  return { input: String -&gt; input.length == length }
}

println(names.find(predicateOfLength(5))) //Paula

println(names.find(predicateOfLength(4))) //Paul</code></pre>
<h3 id="using-lambads">Using lambads</h3>
<pre><code class="language-kotlin">val names = listOf(&quot;Pam&quot;, &quot;Pat&quot;, &quot;Paul&quot;, &quot;Paula&quot;)

val checkLength5 = { name: String -&gt; name.length == 5 } 
println(names.find(checkLength5)) //Paula</code></pre>
<p><code>checkLength5</code>는 String 매개변수를 가진 람다를 가진다. </p>
<pre><code class="language-kotlin">val checkLength5: (String) -&gt; Boolean = { name -&gt; name.length == 5 }</code></pre>
<p>위와 같은 식으로 변수의 타입을 지정하고 람다 표현식의 매개변수의 타입을 추론하도록 할 수 있다.</p>
<h3 id="using-anonymous-functions">Using anonymous functions</h3>
<p>변수명을 사용하지 않는 방법으로 anonymous function(익명함수)를 람다 대신 사용할 수 있다. </p>
<pre><code class="language-kotlin">val checkLength5 = fun(name: String): Boolean { return name.length == 5 }

names.find(fun(name: String): Boolean { return name.length == 5 })</code></pre>
<p>하지만 return 키워드를 사용해야하는 등 람다 표현식을 사용하는 것보다 코드가 길어지므로 권장하지는 않는 방법이다.</p>
<h3 id="closures-and-lexical-scoping">Closures and Lexical Scoping</h3>
<p><strong>Closure</strong> : 람다 표현식에서 외부 범위에서 선언된 변수에 접근을 할 수 있는 개념이다. 
<strong>Lexical Scoping</strong> : 함수를 어디서 선언하였는지에 따라 상위 스코프가 결정되는 개념.</p>
<pre><code class="language-kotlin">val factor = 2

val doubleIt = { e: Int -&gt; e * factor }</code></pre>
<p>위 람다 표현식 바디안에서 factor 변수는 표현식 밖의 상위 scope인 closure 바로 위에 위치한 <code>val factor = 2</code>로 간주한다. </p>
<pre><code class="language-kotlin">fun predicateOfLength(length: Int): (String) -&gt; Boolean { 
  return { input: String -&gt; input.length == length }
}</code></pre>
<p>위와 같이 람다 바디 내부에는 <code>length</code>라는 변수가 없기 때문에 가장 가까운곳에 위치한 인자로 내려오는 <code>length</code>를 참조한다. </p>
<h3 id="mutual-variables">Mutual variables</h3>
<p>클로저가 참조하는 변수는 가급적이면 val을 사용하자. 다음과 같은 이상한 상황이 일어날 수 있다. </p>
<pre><code class="language-kotlin">var factor = 2

val doubled = listOf(1, 2).map { it * factor }
val doubledAlso = sequenceOf(1, 2).map { it * factor }

factor = 0

doubled.forEach { println(it) }
doubledAlso.forEach { println(it) }</code></pre>
<p>실행결과</p>
<pre><code class="language-shell">2
4
0
0</code></pre>
<h3 id="non-local-and-labeled-return">Non-Local and Labeled return</h3>
<p>코틀린 람다에서 <code>return</code> 키워드를 사용하지 않는 이유에 대한 예제를 보자</p>
<pre><code class="language-kotlin">fun invokeWith(n: Int, action: (Int) -&gt; Unit) {
  println(&quot;enter invokeWith $n&quot;)
  action(n)
  println(&quot;exit invokeWith $n&quot;)
}

fun caller() { 
  (1..3).forEach { i -&gt; 
    invokeWith(i) {
      println(&quot;enter for $it&quot;)

      if (it == 2) { return } //ERROR, return is not allowed here

      println(&quot;exit for $it&quot;)
    }
  }

  println(&quot;end of caller&quot;)
} 

caller()
println(&quot;after return from caller&quot;)</code></pre>
<p>여기서 <code>if (it == 2) {return}</code> 라인이 문제되는 이유는 다음 상황을 컴파일러가 판단하기가 어렵기 때문이다.</p>
<ol>
<li>람다를 종료하고 다음 라인으로 continue를 해야 하는지</li>
<li>바로 위의 for loop를 종료하는건지</li>
<li>caller() 함수를 종료해야하는 건지</li>
</ol>
<p>따라서 1번처럼 람다식을 종료하고자 하는 의도를 명시해야 할 경우 <code>labled return</code>을 사용해주면 된다. </p>
<pre><code class="language-kotlin">fun caller() {
    (1..3).forEach { i -&gt;
        invokeWith(i) here@ { 
            println(&quot;enter for $it&quot;)

            if (it == 2) {
                return@here 
            }

            println(&quot;exit for $it&quot;)
        }
    }

    println(&quot;end of caller&quot;)
} 

caller()
println(&quot;after return from caller&quot;)

&quot;&quot;&quot; 실행결과
enter invokeWith 1
enter for 1
exit for 1
exit invokeWith 1
enter invokeWith 2
enter for 2
exit invokeWith 2
enter invokeWith 3
enter for 3
exit for 3
exit invokeWith 3
end of caller
after return from caller
&quot;&quot;&quot;</code></pre>
<p>여기서 <code>return@here</code> 는 <code>continue</code>와 같은 역할을 한다. 즉 람다를 종료하는 역할을 한다. </p>
<pre><code class="language-kotlin">fun caller() {
  (1..3).forEach { i -&gt;
    invokeWith(i) { 
      println(&quot;enter for $it&quot;)

      if (it == 2) {
        return@invokeWith 
      }

      println(&quot;exit for $it&quot;)
    }
  }

  println(&quot;end of caller&quot;)
}</code></pre>
<p>이런식으로 함수이름을 명시해줘도 된다</p>
<h3 id="no-inline-optimization-by-default">No inline optimization by default</h3>
<h4 id="inline-optimization">Inline optimization</h4>
<p>람다를 받는 함수 앞에 <code>inline</code>이라는 키워드를 사용해 성능을 개선시킬 수 있다. 이렇게 되면 함수 호출부에 해당 함수가 바이트코드로 대체된다. 함수를 호출하는 부분에서 오버헤드를 없앨 수 있지만, 길이가 큰 함수를 inline으로 대체하는것은 시스템 부하를 일으킬 수 있다.</p>
<pre><code class="language-kotlin">inline fun invokeTwo( 
  n: Int,
  action1: (Int) -&gt; Unit, 
  action2: (Int) -&gt; Unit 
  ): (Int) -&gt; Unit {</code></pre>
<p>내부 call stack</p>
<pre><code class="language-shell">enter invokeTwo 1

called with 1, Stack depth: 28
Partial listing of the stack: 
Inlineoptimization.report(inlineoptimization.kts:31) 
Inlineoptimization.callInvokeTwo(inlineoptimization.kts:20) 
Inlineoptimization.&lt;init&gt;(inlineoptimization.kts:23)

called with 1, Stack depth: 28
Partial listing of the stack: 
Inlineoptimization.report(inlineoptimization.kts:31) 
Inlineoptimization.callInvokeTwo(inlineoptimization.kts:20) 
Inlineoptimization.&lt;init&gt;(inlineoptimization.kts:23)
exit invokeTwo 1</code></pre>
<p>만약 인라인을 사용한 최적화를 의도한것이 아니라면 <code>noineline</code> 키워드를 사용해준다.</p>
<pre><code class="language-kotlin">inline fun invokeTwo( 
  n: Int,
  action1: (Int) -&gt; Unit, 
  noinline action2: (Int) -&gt; Unit 
  ): (Int) -&gt; Unit {</code></pre>
<h4 id="crossinline-parameter">crossinline parameter</h4>
<p><code>noline</code>으로 명시해주지 않는 이상 람다 표현식으로 이루어진 매개변수는 inline 처리된다. 해당 람다가 호출되는 부분을 보두 람다 표현식으로 처리하기 위해서는 <code>crossinline</code> 키워드를 이용하면 된다.</p>
<pre><code class="language-kotlin">inline fun invokeTwo( 
    n: Int,
    action1: (Int) -&gt; Unit, 
    action2: (Int) -&gt; Unit //ERROR 
    ): (Int) -&gt; Unit {

    println(&quot;enter invokeTwo $n&quot;) 

    action1(n)

    println(&quot;exit invokeTwo $n&quot;)
    return { input: Int -&gt; action2(input) } 
}</code></pre>
<h4 id="good-practices-for-inline-and-returns">Good practices for inline and returns</h4>
<ul>
<li><code>Unlabeld return</code>은 람다가 아닌 함수에서의 반환형이다</li>
<li>none-inlined 람다에서는 <code>Unlabeld return</code>이 허용되지 않는다.</li>
<li>labed return을 사용하면 항상 함수에 custom name을 명시해주자.</li>
<li>inline 최적화를 하기 전에는 성능 검사를 한다</li>
<li>성능 검사를 할 수 있는 부분에서 inline을 활용하자</li>
</ul>
]]></description>
        </item>
        <item>
            <title><![CDATA[educative - kotlin - 9]]></title>
            <link>https://velog.io/@sungjun-jin/educative-kotlin-9</link>
            <guid>https://velog.io/@sungjun-jin/educative-kotlin-9</guid>
            <pubDate>Sun, 28 Mar 2021 11:27:53 GMT</pubDate>
            <description><![CDATA[<h2 id="extension-through-delegation">Extension through Delegation</h2>
<h3 id="delegation-클래스-위임-이란">Delegation (클래스 위임) 이란?</h3>
<blockquote>
<p>코틀린에서는 모든 클래스가 자바와 달리 final이다. 클래스 상속이 필요하면 상속될 클래스에 <code>open</code> 접근자를 명시해줘야 한다. 그래야 <code>open</code>이 붙은 클래스를 수정할 때 하위 클래스가 있다는 사실을 상기하여 수정할 수 있다. 그래서 상속 기능이 필요할 때 데코레이터 페턴(Decorator Pattern)이라는 방식을 사용하는데, 상속하고 싶은 클래스와 동일한 인터페이스를 구현하는 새로운 클래스를 만들고 상속하고 싶은 클래스는 내부 프로퍼티로 가지는 방식이다.</p>
</blockquote>
<p>상속은 흔하고, 객체지향 언어에서 자주 사용해 파생된 기능이 많다는게 가장 큰 장점이다. 위임은 상속보다 좀 더 유연하지만 아직 객체지향 언어에서 지원하는 특별한 기능이 없다. 코틀린은 상속과 위임 둘 다 지원한다. </p>
<h4 id="choose-wisely">Choose wisely</h4>
<ul>
<li>클래스의 인스턴스를 다른 클래스의 인스턴스 대신 사용하려면 상속을 사용해라</li>
<li>클래스의 인스턴스가 다른 클래스의 인스턴스를 사용하게 하려면 위임을 사용해라</li>
</ul>
<p>무슨 차이인가?</p>
<p>계층구조를 상세화하고 부모, 자식 클래스간의 관계를 표현하고 싶다면 상속을 사용
<code>Animal -&gt; Dog</code>
객체의 다른 인터페이스를 단순히 사용하고 싶다면 위임을 사용
<code>Manager -&gt; Assistant</code></p>
<p><img src="https://images.velog.io/images/sungjun-jin/post/97e6445e-3a83-4ddc-bb0a-62911a68d06a/image.png" alt=""></p>
<p>상속과의 차이점은 위임을 사용하면 인터페이스가 갖는 메소드 이외의 메소드들은 상속받지 않기 때문에 필요한 메소들만 상속 받을 수 있다는 점이다. 하지만 필요한 메소드를 사용하기 위해서는 많은 양의 duplicated code를 작성해야 하는데 코틀린에서는 위엠에서 자바보다 좀 더 편리하고 직관적인 기능을 지원한다.</p>
<h3 id="designing-with-delegates">Designing with Delegates</h3>
<p>좀 더 구체적인 예시를 들어보자. Worker 인터페이스를 정의한다. 두 가지 메소드가 있다.</p>
<ul>
<li>일하기</li>
<li>휴가 써버리기</li>
</ul>
<pre><code class="language-kotlin">interface Worker {
    fun work()
    fun takeVacation()
}</code></pre>
<p>위 Worker 인터페이스를 구현한 2개의 클래스(<code>JavaProgrammer, CSharpProgrammer</code>)를 만들어보자</p>
<pre><code class="language-kotlin">class JavaProgrammer: Worker {
    override fun work() = println(&quot;...write Java...&quot;)
    override fun takeVacation() = println(&quot;...code at the beach...&quot;)
}

class CSharpProgrammer: Worker {
    override work() = println(&quot;...write C#...&quot;)
    override takeVacation() = println(&quot;...branch at the ranch...&quot;)
}</code></pre>
<p>회사에는 <code>Worker</code> 인터페이스를 구현한 자바, C# 프로그래머가 존재한다. 그리고 회사에는 프로젝트의 Manager 또한 존재한다. </p>
<pre><code class="language-kotlin">class Manager</code></pre>
<p>회사는 매니저를 통해 프로젝트를 전달하고 진행시키고 싶어한다. 따라서 매니저는 앞서 구현된 두 개발자들에게 구현된 work() 메소드를 통해 개발자들에게 코드를 뽑아 제품을 만들어야 한다. 하지만 어떻게?</p>
<p>일단 가장 먼저 흔히 사용하는 상속을 통해 구현해보자. </p>
<pre><code class="language-kotlin">open class JavaProgrammer: Worker {
    // 내용 생략
}

class Manager: JavaProgrammer() // 내용 생략

val pm = Manager()
pm.work() // ...write Java...</code></pre>
<ul>
<li><code>JavaProgrammer</code> 클래스에 <code>open</code> 접근자를 사용해 상속이 가능한 상태로 만들어준다</li>
<li>그리고 <code>Manager</code> 클래스가 <code>JavaProgrammer</code>를 상속받는다</li>
<li>상속받은 <code>work()</code> 메소드를 사용해 Java 코드를 뽑아낸다....(?)</li>
</ul>
<p>뭔가 이상하다. 결국 매니저가 자바 코드를 치고 있다. 그건 둘째치고 이렇게 되면 C# 개발자는 놀고있는 셈이 된다. 슬프게도 위와 같은 식으로 코드를 구현하면 결국 매니저는 아래와 같이 자바 개발자가 되어버린다.</p>
<pre><code class="language-kotlin">val coder: JavaProgrammer = pm // 정상적으로 컴파일</code></pre>
<p>코틀린의 위임(delegation)을 사용해보자. <code>by</code> 키워드를 사용하면 간단하게 클래스 위임을 구현할 수 있다. <code>by</code> 키워드를 사용하면 해당 인터페이스에게 대한 구현을 다른 객체에 위임중이라는 사실을 명시할 수 있다.</p>
<pre><code class="language-kotlin">class Manager() : Worker by JavaProgrammer()</code></pre>
<p>상속과 다른점은 <code>Manager</code> 클래스가 <code>work()</code> 메소드를 실행시키기 위해 엉뚱하게 <code>JavaProgrammer</code>를 상속받지 않는다는 점이 있다. </p>
<pre><code class="language-kotlin">val coder: JavaProgrammer = pm // Type mismatch</code></pre>
<p>하지만 이렇게 되면 Manager 클래스는 JavaProgrammer 클래스만 위임받게 되어 Worker 인터페이스를 구현한 다른 객체에는 접근하지 못한다. </p>
<p>매개변수를 통한 위임을 활용하면 조금 더 유연한 구조가 된다</p>
<pre><code class="language-kotlin">class Manager(val staff: Worker) : Worker by staff {
    fun meeting() = println(&quot;organizing meeting with ${staff}&quot;)
}

val doe = Manager(CSharpProgrammer())
val roe = Manager(JavaProgrammer())

doe.work() //...write C#...
doe.meeting()//organizing meeting with CSharpProgrammer

roe.work() //...write Java...
roe.meeting()//organizing meeting with JavaProgrammer</code></pre>
<h3 id="how-to-deal-with-collisions">How to deal with collisions</h3>
<p>여태까지의 구조를 다시 한번 정리해보자</p>
<pre><code class="language-kotlin">interface Worker {
    fun work()
    fun takeVacation()
}

class JavaProgrammer: Worker {
    override fun work() = println(&quot;...write Java...&quot;)
    override fun takeVacation() = println(&quot;...code at the beach...&quot;)
}

class CSharpProgrammer: Worker {
    override work() = println(&quot;...write C#...&quot;)
    override takeVacation() = println(&quot;...branch at the ranch...&quot;)
}

class Manager(val staff: Worker) : Worker by staff {
    fun meeting() = println(&quot;organizing meeting with ${staff}&quot;)
}</code></pre>
<p>코틀린은 위임하는 클래스에 대해서 충돌을 방지하기 위해 위임받을 메소드를 <code>override</code> 키워드를 사용해 선택한다.</p>
<pre><code class="language-kotlin">class Manager(val staff: Worker) : Worker by staff {
  override fun takeVacation() = println(&quot;of course&quot;)
}

val doe = Manager(CSharpProgrammer())
doe.work()         //...write C#...
doe.takeVacation() //of course</code></pre>
<h3 id="collisions-in-the-two-interfaces">Collisions in the two interfaces</h3>
<p>인터페이스간 충돌이 일어날 경우 위임을 받는 클래스가 해당 메소드들을 재정의 해줘야 한다. </p>
<pre><code class="language-kotlin">interface Worker {
  fun fileTimeSheet() = println(&quot;Why? Really?&quot;)
}

interface Assistant {
  fun fileTimeSheet() = println(&quot;No escape from that&quot;)
}

class Manager(val staff: Worker, val assistant: Assistant) :
  Worker by staff, Assistant by assistant {      

  override fun takeVacation() = println(&quot;of course&quot;)

  // 메소드 충돌 처리
  override fun fileTimeSheet() {
    print(&quot;manually forwarding this...&quot;)
    assistant.fileTimeSheet()
  }
}</code></pre>
<p>여기서 <code>Manager</code>  클래스는 2개의 클래스(<code>Worker, Assistant</code>)를 위임받는다 이 중에 중복된 이름의 메소드 <code>fileTieSheet()</code>이 있다. 만약 <code>Manager</code> 클래스에서 위처럼<code>filtTimeSheet()</code> 메소드에 대한 처리를 하지 않는다면 두 인터페이스간의 충돌이 일어날 것이다. </p>
<p>실행결과</p>
<pre><code class="language-kotlin">val doe = Manager(CSharpProgrammer(), DepartmentAssistant())
doe.fileTimeSheet() //manually forwarding this...No escape from that</code></pre>
<h3 id="deligating-variables-and-properties">Deligating Variables and Properties</h3>
<p>클래스의 프로퍼티와 getter, setter 또한 위임할 수 있다. </p>
<h4 id="variables">variables</h4>
<pre><code class="language-kotlin">package com.agiledeveloper.delegates

import kotlin.reflect.KProperty

class PoliteString(var content: String) {
  operator fun getValue(thisRef: Any?, property: KProperty&lt;*&gt;) = 
    content.replace(&quot;stupid&quot;, &quot;s*****&quot;)

  operator fun setValue(thisRef: Any, property: KProperty&lt;*&gt;, value: String) {
    content = value
  }
}

var comment: String by PoliteString(&quot;Some nice message&quot;)
println(comment) // Some nice message

comment = &quot;This is stupid&quot;
println(comment) // This is s*****

println(&quot;comment is of length: ${comment.length}&quot;) // comment is of length: 14</code></pre>
<h4 id="properties">properties</h4>
<pre><code class="language-kotlin">import kotlin.reflect.KProperty
import kotlin.collections.MutableMap

class PoliteString(val dataSource: MutableMap&lt;String, Any&gt;) {
  operator fun getValue(thisRef: Any?, property: KProperty&lt;*&gt;) =
    (dataSource[property.name] as? String)?.replace(&quot;stupid&quot;, &quot;s*****&quot;) ?: &quot;&quot;

  operator fun setValue(thisRef: Any, property: KProperty&lt;*&gt;, value: String) {
    dataSource[property.name] = value
  }
}

class PostComment(dataSource: MutableMap&lt;String, Any&gt;) {
  val title: String by dataSource
  var likes: Int by dataSource
  val comment: String by PoliteString(dataSource)

  override fun toString() = &quot;Title: $title Likes: $likes Comment: $comment&quot;
}

// sample data 저장
val data = listOf(  
    mutableMapOf(
      &quot;title&quot; to &quot;Using Delegation&quot;, 
      &quot;likes&quot; to 2,
      &quot;comment&quot; to &quot;Keep it simple, stupid&quot;),
    mutableMapOf(
      &quot;title&quot; to &quot;Using Inheritance&quot;, 
      &quot;likes&quot; to 1,
      &quot;comment&quot; to &quot;Prefer Delegation where possible&quot;))

val forPost1 = PostComment(data[0])
val forPost2 = PostComment(data[1])

forPost1.likes++

//Title: Using Delegation Likes: 3 Comment: Keep it simple, s*****
println(forPost1)
//Title: Using Inheritance Likes: 1 Comment: Prefer Delegation where possible
println(forPost2)</code></pre>
<h3 id="built-in-standard-delegates">Built-in Standard Delegates</h3>
<p>코틀린에서 제공하는 built-in 위임은 다음과 같다</p>
<h4 id="observable">Observable</h4>
<p>말 그대로 프로퍼티를 observable 하게 만들어준다. 이것을 이용하면 프로퍼티의 데이터가 변할 때마다 callback을 받을 수 있다.</p>
<pre><code class="language-kotlin">import kotlin.properties.Delegates.observable

var count by observable(0) { property, oldValue, newValue -&gt; 
  println(&quot;Property: $property old: $oldValue: new: $newValue&quot;)
}

println(&quot;The value of count is: $count&quot;)
count = count + 1 // Need to be changed later
println(&quot;The value of count is: $count&quot;)
count = count - 1 // Need to be changed later
println(&quot;The value of count is: $count&quot;)</code></pre>
<p>실행결과</p>
<pre><code class="language-shell">The value of count is: 0
Property: var Observe.count: kotlin.Int old: 0: new: 1 
The value of count is: 1
Property: var Observe.count: kotlin.Int old: 1: new: 0 
The value of count is: 0</code></pre>
<h4 id="vetoable">vetoable</h4>
<p>vetoable은 observable과 거의 유사하지만 리턴 값이 있다는 차이점이 있다.</p>
<pre><code class="language-kotlin">import kotlin.properties.Delegates.vetoable

var count by vetoable(0) { _, oldValue, newValue -&gt; newValue &gt; oldValue }

println(&quot;The value of count is: $count&quot;)
count = count + 1 // Changed later
println(&quot;The value of count is: $count&quot;)
count = count - 1 // Changed later
println(&quot;The value of count is: $count&quot;)</code></pre>
<p>실행결과</p>
<pre><code class="language-shell">The value of count is: 0
The value of count is: 1
The value of count is: 1</code></pre>
]]></description>
        </item>
        <item>
            <title><![CDATA[educative - kotlin - 8]]></title>
            <link>https://velog.io/@sungjun-jin/educative-kotlin-8</link>
            <guid>https://velog.io/@sungjun-jin/educative-kotlin-8</guid>
            <pubDate>Sun, 21 Mar 2021 14:47:44 GMT</pubDate>
            <description><![CDATA[<h2 id="class-hierarchies-and-inheritance">Class Hierarchies and Inheritance</h2>
<h3 id="creating-interfaces">Creating interfaces</h3>
<p>몇가지 추상 메소드들로 구성된 인터페이스를 만들어보자! </p>
<p>코틀린 인터페이스는 <code>interface</code> 키워드를 사용해 정의해준다. </p>
<pre><code class="language-kotlin">interface Remote {
  fun up()
  fun down()

  fun doubleUp() {
    up()
    up()
  }
}</code></pre>
<p>위에서 작성한 인터페이스를 활용해보자! 기본 생성자 옆에 콜론(:)을 붙여 사용할 인터페이스를 명시해준다.</p>
<pre><code class="language-kotlin">class TV {
  var volume = 0
}

class TVRemote(val tv: TV) : Remote {
  override fun up() { tv.volume++ }
  override fun down() { tv.volume-- }
}</code></pre>
<p>TVRemote 클래스는 Remote 인터페이스에서 정의한 추상메소드를 의무적으로 오버라이딩 해야한다. 하지만 여기서 위에 <code>doubleUp()</code> 메소드는 예외이다 왜냐하면 코틀린의 인터페이스는 추상 메소드뿐만 아니라 default 메소드도 구현 가능하기 때문이다. 여기서 <code>up(), down()</code> 메소드는 추상 메소드로 간주되어 오버라이딩의 의무가 있지만 ``doubleUp()``` 메소드는 default 메소드로 간주되어 오버라이딩의 의무가 없다. </p>
<p>위 Remote 인터페이스를 Java 8에서 나온 <code>default</code> 키워드를 사용해 구현해보자. </p>
<pre><code class="language-java">public interface Remote {
    void up(); // abstract
    void down(); // abstract

    default void doubleUp() {
        up();
        up();
    }
}</code></pre>
<p>다시 정리해서 사용해보자</p>
<pre><code class="language-kotlin">class TV {
  var volume = 0
}

class TVRemote(val tv: TV) : Remote {
  override fun up() { tv.volume++ }
  override fun down() { tv.volume-- }
}

val tv = TV()
val remote: Remote = TVRemote(tv) //Remote 인터페이스를 구현한 TVRemote 인스턴스 생성

println(&quot;Volume: ${tv.volume}&quot;) //Volume: 0
remote.up()
println(&quot;After increasing: ${tv.volume}&quot;) //After increasing: 1
remote.doubleUp()
println(&quot;After doubleUp: ${tv.volume}&quot;) //After doubleUp: 3</code></pre>
<p>Java에서 interface는 static 메소드를 가질 수 있지만 코틀린은 아니다. 코틀린에서 인터페이스에 static 메소드를 구현하려면 companion object를 사용해준다. Remote 인터페이스를 상속받아 <code>combine()</code> 메소드를 통해 2개의 <code>Remote</code> 클래스의 연산을 수행해주는 역할을 한다. </p>
<pre><code class="language-kotlin">companion object {
    fun combine(first: Remote, second: Remote): Remote = object: Remote {
        override fun up() {
          first.up()
          second.up()
        }            

        override fun down() {
          first.down()
          second.down()
        }
      }
  }

// 사용
val tv = TV()
val remote: Remote = TVRemote(tv)

val anotherTV = TV()
val anotherRemote: Remote = TVRemote(anotherTV)

val combinedRemote = Remote.combine(remote, anotherRemote)

combinedRemote.up()
println(tv.volume) //1
println(anotherTV.volume) //1</code></pre>
<h3 id="creating-abstract-classes">Creating abstract classes</h3>
<p><code>abstract</code> 키워드를 클래스 앞에 붙여 추상 클래스를 구현할 수 있다. </p>
<pre><code class="language-kotlin">abstract class Musician(val name: String, val activeFrom: Int) {
  abstract fun instrumentType(): String
}

class Cellist(name: String, activeFrom: Int) : Musician(name, activeFrom) {
  override fun instrumentType() = &quot;String&quot;
}

val ma = Cellist(&quot;Yo-Yo Ma&quot;, 1961)

println(ma.name) // Yo-Yo Ma
println(ma.instrumentType()) // String</code></pre>
<p>인터페이스와 추상 클래스의 차이점?</p>
<ul>
<li>인터페이스에서 정의된 프로퍼티들은 field가 없다. 따라서 추상 메소드를 통해 프로퍼티에 접근을 해야한다. 하지만 추상 클래스는 가능하다</li>
<li>인터페이스는 다중 상속이 가능하지만 추상 클래스는 불가능하다.</li>
</ul>
<p>그렇다면 어느 상황에서 적절하게 사용할까? -&gt; 다중 상속 여부에서 갈림</p>
<ul>
<li>여러개의 클래스에서 사용해야 한다면 추상 클래스 -&gt; 1개만 만들어서 여러 클래스에 뿌리면 되니까</li>
<li>여러 클래스를 상속받아서 구현해야한다면 -&gt; 당연히 다중상속이 가능한 인터페이스</li>
</ul>
<h3 id="nested-and-inner-classes">Nested and Inner Classes</h3>
<p>좀 더 효율적인 구조를 위해 inner class를 사용해보자 </p>
<pre><code class="language-kotlin">class TV {
  private var volume = 0

  val remote: Remote
    get() = TVRemote()

  override fun toString(): String = &quot;Volume: ${volume}&quot;
  // TVRemote를 TV의 inner class로 옮김
  inner class TVRemote : Remote {
    override fun up() { volume++ } //바로 outer class property 접근 가능
    override fun down() { volume-- }

    override fun toString() = &quot;Remote: ${this@TV.toString()}&quot;
  }                    
}

val tv = TV()
val remote = tv.remote

println(&quot;$tv&quot;) //Volume: 0
remote.up()
println(&quot;After increasing: $tv&quot;) //After increasing: Volume: 1
remote.doubleUp()
println(&quot;After doubleUp: $tv&quot;) //After doubleUp: Volume: 3</code></pre>
<p>TVRemote inner class를 사용하면 up() down() 메소드에서 자유롭게 바로 TV의 private 프로퍼티에 접근 가능하다.  <code>this@</code> 표현식을 사용해 outer class의 멤버에 접근 가능하다.</p>
<h3 id="anonymous-inner-class">Anonymous inner class</h3>
<p>anonymous inner 클래스를 통해 똑같이 구현 가능하다. <code>inner</code>키워드가 없다는 점, 클래스명이 없다는 점빼곤 nested class를 사용하는것과 동일하다. </p>
<pre><code class="language-kotlin">class TV {
    private var volume = 0
    val remote: Remote get() = object: Remote { 
        override fun up() { volume++ } 
        override fun down() { volume-- }
        override fun toString() = &quot;Remote: ${this@TV.toString()}&quot; 
    }
    override fun toString(): String = &quot;Volume: ${volume}&quot; 
}</code></pre>
<h3 id="inheritance">Inheritance</h3>
<p>코틀린의 클래스와 메소드는 기본적으로 <code>final</code>이라서 상속이 불가능하다. 따라서 상속이 가능하려면 <code>open</code> 키워드를 사용해야 한다. 또한 부모 클래스의 val 프로퍼티는 자식 클래스에서 val이나 var로 오버라이딩이 가능하지만 부모 클래스의 var 프로퍼티는 자식 클래스에서 val로 오버라이딩이 불가능하다. 일반적으로 val은 getter, var는 getter와 setter가 있다. 따라서 부모클래스에서 var이 자식클래스에서 val로 오버라이드 된다면 부모클래스에서 정의된 setter를 빼주는것이기 때문에 불가능하다. </p>
<h4 id="creating-a-base-class">Creating a base class</h4>
<pre><code class="language-kotlin">open class Vehicle(val year: Int, open var color: String) {
  open val km = 0   
  // 상속 가능
  final override fun toString() = &quot;year: $year, Color: $color, KM: $km&quot;
  // 상속 불가
  fun repaint(newColor: String) {
    color = newColor
  }
}</code></pre>
<ul>
<li>base class의 첫번째 프로퍼티는 오버라이딩이 불가능한 val이고 두번째 프로퍼티는 오버라이딩이 가능한 var 프로퍼티이다. </li>
<li>상위 클래스인 any에서 정의된 <code>toString()</code> 메소드를 오버라이딩하고 있다. </li>
<li>상속을 금지한 <code>repaint()</code> 메소드가 있다. </li>
</ul>
<h4 id="creating-a-derived-class">Creating a derived class</h4>
<pre><code class="language-class">
```kotlin
open class Car(year: Int, color: String) : Vehicle(year, color) {
  override var km: Int = 0
    set(value) {
      if (value &lt; 1) {
        throw RuntimeException(&quot;can&#39;t set negative value&quot;)
      }

      field = value
    }

  fun drive(distance: Int) {
    km += distance
  }  
}</code></pre>
<p>자식 클래스인 Car 생성자의 매개변수는 부모 클래스인 Vehicle로 넘어간다. 여기서 자식 클래스는 부모 클래스의 <code>km</code> 프로퍼티를 오버라이딩 한다. <code>val -&gt; var</code>이고 커스텀 setter를 구현했다.</p>
<pre><code class="language-kotlin">val car = Car(2019, &quot;Orange&quot;)
println(car.year)  // 2019
println(car.color) // Orange

car.drive(10)
println(car) // year: 2019, Color: Orange, KM: 10 (부모 클래스 toString())

try {
  car.drive(-30)
} catch(ex: RuntimeException) {
  println(ex.message) // can&#39;t set negative value (커스텀 setter)
}</code></pre>
<h4 id="extending-the-class">Extending the class</h4>
<p>이번엔 Car를 상속받는 FamilyCar 클래스를 만들어 보자</p>
<pre><code class="language-kotlin">class FamilyCar(year: Int, color: String) : Car(year, color) {
  override var color: String
    get() = super.color
    set(value) {
      if (value.isEmpty()) {
        throw RuntimeException(&quot;Color required&quot;)
      }

      super.color = value
    }
}</code></pre>
<p>FamilyCar 클래스는 상위 클래스인 Vehicle 클래스의 color 프로퍼티를 getter와 setter로 사용하고 있다.</p>
<pre><code class="language-kotlin">val familyCar = FamilyCar(2019, &quot;Green&quot;)

println(familyCar.color) //Green

try {
  familyCar.repaint(&quot;&quot;) //Custom Setter 발동
} catch(ex: RuntimeException) {
  println(ex.message) // Color required
}</code></pre>
<p>오버라이딩을 할때 접근 제한자 주의해야할 점</p>
<ul>
<li>public -&gt; private or protected (0)</li>
<li>protected -&gt; public (x)</li>
</ul>
<h3 id="sealed-class">Sealed class</h3>
<p>Sealed class는 같은 파일 내에서 상위 클래스를 상속받는 자식 클래스의 종류를 제한하고 있는 특성을 가진 클래스이다. </p>
<pre><code class="language-kotlin">sealed class Card(val suit: String)

class Ace(suit: String) : Card(suit)

class King(suit: String) : Card(suit) {
  override fun toString() = &quot;King of $suit&quot;
}

class Queen(suit: String) : Card(suit) {
  override fun toString() = &quot;Queen of $suit&quot;
}

class Jack(suit: String) : Card(suit) {
  override fun toString() = &quot;Jack of $suit&quot;
}

class Pip(suit: String, val number: Int) : Card(suit) {
  init {
    if (number &lt; 2 || number &gt; 10) {
      throw RuntimeException(&quot;Pip has to be between 2 and 10&quot;)
    }
  }
}

fun process(card: Card) = when (card) {
  is Ace -&gt; &quot;${card.javaClass.name} of ${card.suit}&quot;
  is King, is Queen, is Jack -&gt; &quot;$card&quot;
  is Pip -&gt; &quot;${card.number} of ${card.suit}&quot;
}

// when() 구문에서 else는 넣지 않는다 -&gt; 새로운 sealed 클래스가 추가되었을때 오류가 생길 수 있다

fun main() {
    println(process(Ace(&quot;Diamond&quot;)))    // Ace of Diamond
    println(process(Queen(&quot;Clubs&quot;)))    // Queen of Clubs
    println(process(Pip(&quot;Spades&quot;, 2)))   // 2 of Spades
    println(process(Pip(&quot;Hearts&quot;, 6)))  // 6 of Hearts
}</code></pre>
<ul>
<li>sealed class는 자동으로 open 변경자가 붙는다 -&gt; 자동으로 상속 가능</li>
<li>private 생성자를 가지고 있기 때문에 객체를 직접 생성할 수 없다</li>
<li>같은 파일내에서 상속받아 하위 클래스를 구현할 수 있다. -&gt; 다른 파일에서 sealed class 상속 시도 -&gt; compile 에러</li>
</ul>
<h3 id="enum-classes">enum classes</h3>
<p>enum class를 사용하면 코드가 단순해지며 가독성이 더 좋아진다.</p>
<pre><code class="language-kotlin">enum class Suit { CLUBS, DIAMONDS, HEARTS, SPADES }

sealed class Card(val suit: Suit)

class Ace(suit: Suit) : Card(suit)

class King(suit: Suit) : Card(suit) {
  override fun toString() = &quot;King of $suit&quot;
}

// UseCardWithEnum.kt
println(process(Ace(Suit.DIAMONDS)))    // Ace of DIAMONDS
println(process(Queen(Suit.CLUBS)))    // Queen of CLUBS
println(process(Pip(Suit.SPADES, 2)))   // 2 of SPADES
println(process(Pip(Suit.HEARTS, 6)))  // 6 of HEARTS</code></pre>
<p><code>Suit.DIAMONDS</code>는 Suit 클래스 인스턴스의 <code>static</code> 프로퍼티이다.</p>
<h3 id="customizing-enums">Customizing enums</h3>
<p>enum 클래스는 커스터마이징과 iteration이 가능하다.</p>
<pre><code class="language-kotlin">// iteration
for (suit in Suit.values()) {
  println(&quot;${suit.name} -- ${suit.ordinal}&quot;) 
}

&quot;&quot;&quot;
CLUBS -- 0
DIAMONDS -- 1
HEARTS -- 2
SPADES -- 3
&quot;&quot;&quot;</code></pre>
<pre><code class="language-kotlin">// Customizing
enum class Suit(val symbol: Char) { 
  CLUBS(&#39;\u2663&#39;),
  DIAMONDS(&#39;\u2666&#39;),
  HEARTS(&#39;\u2665&#39;) {
    override fun display() = &quot;${super.display()} $symbol&quot;
  },
  SPADES(&#39;\u2660&#39;);

  open fun display() = &quot;$symbol $name&quot;
}

for (suit in Suit.values()) {
  println(suit.display())
}

♣ CLUBS
♦ DIAMONDS 
♥ HEARTS ♥ 
♠ SPADES</code></pre>
]]></description>
        </item>
        <item>
            <title><![CDATA[educative - kotlin - 7]]></title>
            <link>https://velog.io/@sungjun-jin/educative-kotlin-7</link>
            <guid>https://velog.io/@sungjun-jin/educative-kotlin-7</guid>
            <pubDate>Sun, 21 Mar 2021 09:25:26 GMT</pubDate>
            <description><![CDATA[<h2 id="objects-and-singletons">Objects and Singletons</h2>
<p>코틀린에서 <code>object</code> 키워드와 <code>{}</code> 를 사용하면 바로 anonymous object(익명 객체)를 생성할 수 있다.</p>
<pre><code class="language-kotlin">val circle = object {
    val x = 10
    val y = 10
    val radius = 30
}</code></pre>
<p>여기서 더 기능을 확장하려면 익명 객체보다 클래스를 설계하는게 더 좋다.</p>
<p>Why
익명 객체는 클래스와 비교해서 다음과 같은 한계점들이 있다</p>
<ul>
<li>익명 객체의 내부 타입은 함수나 메소드의 리턴 타입이 될 수 없다</li>
<li>익명 객체의 내부 타입은 매개변수로 이용할 수 없다</li>
<li>클래스 내부에 익명객체가 존재한다면 <code>Any</code> 타입으로 간주되며 객체의 프로퍼티나 메소드에 직접적인 접근이 불가능하다</li>
</ul>
<p>따라서 위와같은 익명 객체 표현식은 <code>x, y radius</code> 처럼 관련있는 지역 변수들을 묶어줄때 유용하다.</p>
<p>익명 객체 표현식으로 인터페이스를 상속받을 수 있다. </p>
<p><code>object: &lt;상속받고자 하는 인터페이스&gt; {}</code></p>
<pre><code class="language-kotlin">fun createRunnable(): Runnable {
  val runnable = object: Runnable {
    override fun run() { println(&quot;You called...&quot;) }
  }

  return runnable
}

val aRunnable = createRunnable() // 변수에 담하서 호출 가능
aRunnable.run() //You called...</code></pre>
<p>추상 메소드가 하나인 인터페이스(함수형 인터페이스 SAM)를 상속받으면 메소드 이름과 return 키워드를 생략해도 된다.</p>
<pre><code class="language-kotlin">fun createRunnable(): Runnable = Runnable { println(&quot;You called...&quot;) }

createRunnable().run() // You called...</code></pre>
<p>만약 1개 이상의 인터페이스를 상속받는다고 하면 : 옆에 인스턴스 타입을 명시해야한다.</p>
<pre><code class="language-kotlin">fun createRunnable(): Runnable = object: Runnable, AutoCloseable { 
    override fun run() { println(&quot;You called...&quot;) }
    override fun close() { println(&quot;closing...&quot;) } 
}

val runnable = createRunnable()
runnable.run() // You called...
runnable.close() // error: unresolved reference: close</code></pre>
<p>리턴 타입을 어느 익명객체로 지정해주느냐의 따라서 <code>createRunnable()</code> 함수 밖에서 내부 인터페이스에 대한 접근이 가능하다. 위와 같은 경우에는 <code>Runnable</code> 객체를 리턴 타입으로 지정해두었기 때문에 함수 밖에서 <code>Runnable</code> 객체만 접근 가능하다.</p>
<h3 id="singletons-with-object-declaration">Singletons with object declaration</h3>
<p><code>object {}</code> 표현식을 사용해서 싱글톤을 만들 수 있다. 코틀린에서 싱글톤은 대표적으로 <code>Unit</code> 클래스가 있다. </p>
<pre><code class="language-kotlin">object Util {
  fun numberOfProcessors() = Runtime.getRuntime().availableProcessors()
}

println(Util.numberOfProcessors()) // 12</code></pre>
<p>또한 싱글톤 내부에서 메소드 뿐만이 아닌 val, var 키워드로 프로퍼티 생성도 가능하다. 인터페이스나 클래스를 상속받는거도 가능하다. 함수의 인자 전달 또한 가능하다.</p>
<pre><code class="language-kotlin">object Sun : Runnable {
  val radiusInKM = 696000
  var coreTemperatureInC = 15000000

  override fun run() { println(&quot;spin...&quot;) }
}

fun moveIt(runnable: Runnable) {
  runnable.run()
}               

println(Sun.radiusInKM) //696000

moveIt(Sun)  //spin...</code></pre>
<p>하지만 위의 <code>var coreTemperatureInC</code> 처럼 싱글톤 객체에서 mutable한 변수를 사용하는건 딱히 좋은 방법이 아니다 (특히 멀티 쓰레드 어플리케이션에서는)</p>
<h3 id="top-level-functions-vs-singletons">Top-level functions vs. singletons</h3>
<p>코틀린에서 Top-level function은 패키지 내에서 직접적으로 선언된 함수를 의미한다. 코틀린은 java의 static을 지원하지 않는 대신 top-level function을 사용해 같은 효과를 낼 수 있다.</p>
<pre><code class="language-kotlin">package com.agiledeveloper.util

fun unitsSupported() = listOf(&quot;Metric&quot;, &quot;Imperial&quot;) // Top-level function

fun precision(): Int = throw RuntimeException(&quot;Not implemented yet&quot;) //Top-level function

object Temperature {
  fun c2f(c: Double) = c * 9.0/5 + 32
  fun f2c(f: Double) = (f - 32) * 5.0/ 9
}

object Distance {
  fun milesToKm(miles: Double) = miles * 1.609344
  fun kmToMiles(km: Double) = km / 1.609344
}</code></pre>
<p>먼저 <code>package</code> 키워드를 사용해 패키지를 정의해준다. 그 다음에 <code>unitsSupported(), precision()</code> 이라는 이름의 top-level function과 밑에 <code>Temperature, Distance</code>라는 싱글톤을 정의해준다.</p>
<p>이제 위에 패키지를 아래와 같은 방식으로 import 해서 사용할 수 있다.</p>
<pre><code class="language-kotlin">import com.agiledeveloper.util.*
import come.agiledeveloper.util.Temperature.c2f

fun main() {
  println(unitsSupported()) // [Metric, Imperial]
  println(Temperature.f2c(75.253)) // 24.029444444444444
  println(c2f(24.305)) // 75.749
}</code></pre>
<p>만약 위와 같이 공통 모듈을 만드는데 모듈안에 있는 함수가 공통적으로 넓은 범위에서 사용된다고 하면 top-level function으로 바로 정의해주면 좋다. 반면 2개이상의 서로 연관이 있고 비슷한 역할을 하는 함수는 하나의 싱글톤 내에서 묶어놓고 해당 싱글톤 객체를 import해서 사용하면 직관적이고 편하다. </p>
<h2 id="creating-classes">Creating Classes</h2>
<p><code>class</code> 키워드를 사용해 클래스를 생성해준다.</p>
<pre><code class="language-kotlin">class Car(val yearOfMake: Int)</code></pre>
<p>여기까지만 해주면 내부적으로 생성자와 함게 <code>yearOfMake</code>라는 int형 프로퍼티와 getter() 까지 정의해주는 코드가 된다. </p>
<p>인스턴스를 생성해주는 방법은 다음과 같다. 마찬가지로 내부 필드 <code>yearOfMake</code> <code>val</code>로 선언되어 있으므로 값의 직접적인 변경이 불가능하다. </p>
<pre><code class="language-kotlin">val car = Car(2019) 
println(car.yearOfMake) //2019

car.yearOfMake = 2019 //ERROR: val cannot be reassigned</code></pre>
<p>똑같이 var로 선언된 필드는 mutable 하다</p>
<pre><code class="language-kotlin">class Car(val yearOfMake: Int, var color: String)

val car = Car(2019, &quot;Red&quot;)
car.color = &quot;Green&quot;
println(car.color) //GREEN</code></pre>
<p>코틀린에서 클래스 내부의 변수 선언은 자동적으로 property로 선언된다. 위에 있는 <code>car.yearOfMake</code>는 실제로 <code>car.getYearOfMake()</code>이다. Car 클래스를 자바 바이트코드로 컴파일해보면 다음과 같은 구조가 나타난다. 이런식으로 내부 변수들에 대한 접근은 내부에서 간접적으로 constructor, getter와 setter를 통해 이루어진다.</p>
<pre><code class="language-kotlin">Compiled from &quot;Car.kt&quot; 
public final class Car {
  private final int yearOfMake;
  private java.lang.String color;
  public final int getYearOfMake(); Getter
  public final java.lang.String getColor();  Getter
  public final void setColor(java.lang.String);  Setter
  public Car(int, java.lang.String); Constructor
}</code></pre>
<h3 id="controlling-changes-to-properties">Controlling changes to properties</h3>
<p>Getter와 Setter를 특정한 연산을 수행할 수 있게 커스터마이징 할 수 있다. <code>set(), get()</code> 을 통해 사용해준다. color라는 프로퍼티에 empty string(&quot;&quot;) 값이 세팅되는것을 막아주는 커스텀 setter를 만들어보자. 관례적으로 setter의 매개변수의 이름은 <code>value</code>로 사용된다고 한다.</p>
<pre><code class="language-kotlin">class Car(val yearOfMake: Int, theColor: String) {
  var fuelLevel = 100

  var color = theColor
    set(value) {
      if (value.isBlank()) {
        throw RuntimeException(&quot;no empty, please&quot;)
      }

      field = value
    }
}</code></pre>
<p>코틀린에서 클래스 내부 필드는 모두 프로퍼티로 선언되기 때문에 필드를 직접적으로 정의할 순 없다. 따라서 커스텀 setter 내부에서 마지막에 field 키워드를 사용해 color 프로퍼티에 접근 해 값을 세팅해준다 이를 뒷받침필드(Backing Field)라고 한다. </p>
<p>이제 커스템 setter를 테스트해보자. </p>
<pre><code class="language-kotlin">val car = Car(2019, &quot;Red&quot;)
car.color = &quot;Green&quot;

println(car.color) //Green

try {
    car.color = &quot;&quot;
} catch(ex: Exception) {
    println(ex.message) //no empty, please
}</code></pre>
<h3 id="access-modifiers">Access modifiers</h3>
<p>코틀린에서 클래스 내부의 프로퍼티와 메소드는 기본적으로 <code>public</code>이다. 코틀린에서 지원하는 접근 제한자(access modifiers)는 다음과 같다</p>
<ul>
<li>public : 어디에서나 접근 가능 (default)</li>
<li>private : 해당 파일, 혹은 클래스 내에서만 접근 가능</li>
<li>protected : private과 같지만 같은 파일이 아니더라도 자식 클래스에서 접근 가능</li>
<li>internal : 같은 모듈 내에서 접근 가능</li>
</ul>
<p>사용은 다음과 같이 해주면 된다. set() 메소드의 value 매개변수는 생략가능하다</p>
<pre><code class="language-kotlin">var fuelLevel = 100 
  private set</code></pre>
<h3 id="primary-secondary-constructors">Primary, Secondary Constructors</h3>
<p>코틀린의 클래스 생성자는 기본 생성자(Primary Constructor)와 보조 생성자(Secondary Constructor)로 구성되어 있다.  </p>
<pre><code class="language-kotlin">class Person(val first: String, val last: String) {
  var fulltime = true
  var location: String = &quot;-&quot;

  // Secondary Constructor 1
  constructor(first: String, last: String, fte: Boolean): this(first, last) {
    fulltime = fte
  }

    // Secondary Constructor 2
  constructor(
    first: String, last: String, loc: String): this(first, last, false) {
    location = loc
  }

  override fun toString() = &quot;$first $last $fulltime $location&quot;
}</code></pre>
<p>기본 생성자는 클래스 이름 오른편 괄호안에 정의해줄 수 있다. 여기서는 문자열의 <code>first</code>, <code>last</code>이다. 여기서 val, var와 같은 어노테이션이나 접근 제한자(public, private)가지고 있지 않다면 위와 같이 constructor 키워드 생략이 가능하다. </p>
<p>보조 생성자는 <code>constructor</code> 키워드를 사용해 정의한다 (생략불가). 첫번째 보조 생성자는 반드시 <code>this()</code>를 사용해 기본 생성자를 호출 받아야 한다. </p>
<h3 id="initialization-code">Initialization code</h3>
<p>파이썬의 <code>init()</code>은 기본 생성자(primary constructor) 호출 직후 바로 실행되는 코드블럭이다. 기 본 생성자 매개변수를 초기화 시키는 역할을 한다. 이 메소드 내에서는 위에서 정의한 클래스 내부 프로퍼티에 대한 접근이 가능하다. </p>
<pre><code class="language-kotlin">class Car(val yearOfMake: Int, theColor: String) {
  var fuelLevel = 100
    private set

  var color = theColor
    set(value) {
      if (value.isBlank()) {
        throw RuntimeException(&quot;no empty, please&quot;)
      }

      field = value
    }
// init 블록 사용
  init {
    if (yearOfMake &lt; 2020) { fuelLevel = 90 }
  } 
}</code></pre>
<p>이렇게 사용해도 되고 아예 맨 위에서 바로 <code>fuelLevel</code> 프로퍼티 정의를 해줘도 좋다. init 블록을 한개 이상 사용하지 말자. 생성자에서의 복잡도를 최소화 하는것이 프로그램 안정성 측면에서도 좋다.</p>
<pre><code class="language-kotlin">class Car(val yearOfMake: Int, theColor: String) {
var fuelLevel = if (yearOfMake &lt; 20) 90 else 100
    private set</code></pre>
<h3 id="defining-instance-methods">Defining instance methods</h3>
<p>클래스 내부에서 메소드를 정의할때는 똑같이 접근 제한자와 함께 <code>fun</code> 키워드를 사용한다. 접근 제한자의 default는 public이다.. </p>
<pre><code class="language-kotlin">class Person(val first: String, val last: String) {

  internal fun fullName() = &quot;$last, $first&quot;

  private fun yearsOfService(): Int = 
    throw RuntimeException(&quot;Not implemented yet&quot;)
}

val jane = Person(&quot;Jane&quot;, &quot;Doe&quot;)
println(jane.fullName()) //Doe, Jane
//jane.yearsOfService() //ERROR: cannot access...private in &#39;Person&#39;</code></pre>
<p>여기서 정의된 <code>fullName()</code> 메소드는 내부 모듈에서 호출이 가능하지만 private method로 정의된 <code>yearsOfService()</code>는 클래스 외부에서 호출이 불가능하기 때문에 위와 같은 에러가 발생한다.</p>
<h3 id="inline-classes">Inline classes</h3>
<p>비즈니스 로직을 작성할때 어떤 타입의 wrapper를 작성할 때가 있다. 예를들어 SSN과 같은 주민등록번호는 String으로 표현할 수 있지만 SSN이라는 클래스를 만들어 관리하는게 좀 더 직관적이다. 하지만 이는 퍼포먼스적인 측면에서 부담스러울 수 있다. 이때 inline class를 사용하면 런타임에서 primitive 타입을 사용하는것과 같은 효과를 줄 수 있다. 실제로 바이트코드로 컴파일될때 inline class는 primitive 타입으로 간주된다. </p>
<pre><code class="language-kotlin">inline class SSN(val id: String)

fun receiveSSN(ssn: SSN) {
  println(&quot;Received $ssn&quot;)
}</code></pre>
<h3 id="class-level-members">Class-level members</h3>
<p>만약 클래스의 인스턴스없이 어떤 클래스 내부에 접근하고 싶다면 클래스 내부에서 객체를 선언할때 <code>companion</code> 키워드를 붙힌 object를 선언하면 된다. Java의 static 멤버 변수나 함수를 선언하는것과 같은 효과를 준다. 하지만 실제로 static한것은 아니다. companion object는 런타임 환경에서 실제 객체의 인스턴스로 실행된다고 한다</p>
<p>하지만 companion 객체 내부에 mutable한 프로퍼티를 사용한다면 thread-safety 이슈가 생길 수 있다.</p>
<pre><code class="language-kotlin">class MachineOperator(val name: String) {
  fun checkin() = checkedIn++
  fun checkout() = checkedIn--

  companion object {
    var checkedIn = 0

    fun minimumBreak() = &quot;15 minutes every 2 hours&quot;
  }
}

// 인스턴스 없이 바로 호출 가능
MachineOperator(&quot;Mater&quot;).checkin()
println(MachineOperator.minimumBreak()) //15 minutes every 2 hours
println(MachineOperator.checkedIn) //1</code></pre>
<p>위처럼 companion object 옆에 이름이 따로 없다면 <code>Companion</code> 키워드를 사용해 바로 접근이 가능하다.</p>
<pre><code class="language-kotlin">// companion.kts
val ref = MachineOperator.Companion

ref.minimumBreak() // 15 minutes every 2 hours</code></pre>
<p>하지만 아래처럼 companion object 옆에 따로 이름이 있다면 다음과 같이 사용해준다. </p>
<pre><code class="language-kotlin">//companion object {
companion object MachineOperatorFactory { 
  var checkedIn = 0

val ref = MachineOperator.MachineOperatorFactory //직관성을 위해 Factory suffix를 사용해주는게 좋다 </code></pre>
<h3 id="companion-objects-factories">Companion objects factories</h3>
<p>companion object를 객체 생성을 대신 수행해주는 팩토리로 활용할 수 있다. </p>
<pre><code class="language-kotlin">class MachineOperator private constructor(val name: String) { 
    //...
    companion object { 
        //...
        fun create(name: String): MachineOperator { 
            val instance = MachineOperator(name) 
            instance.checkin()
            return instance
        } 
    }
}</code></pre>
<p>MachineOperator 클래스의 생성자를 private로 정의해 클래스 내부에서만 인스턴스가 생성되게 해준다. 이후 create() 메소드 내에서 checkin() 메소드를 실행시킨 이후에 생성된 인스턴스를 return 해주면 factory의 역할을 수행할 수 있다. </p>
<p>따라서 인스턴스를 생성하려면 위에서 정의한 create() 통해서만 생성되게 할 수 있다.</p>
<pre><code class="language-kotlin">val operator = MachineOperator.create(&quot;Mater&quot;)
println(MachineOperator.checkedIn) //1</code></pre>
<h3 id="generic-classes">Generic classes</h3>
<p>PriorityPair라는 제네릭 클래스를 만들어보자. feature는 다음과 같다</p>
<ul>
<li>코틀린 Pair와 비슷한 한 쌍의 object를 가지고 있다</li>
<li><code>compareTo()</code> 메소드를 통해 첫번째 객체는 두번째 객체보다 크도록 정렬한다.</li>
</ul>
<pre><code class="language-kotlin">class PriorityPair&lt;T: Comparable&lt;T&gt;&gt;(member1: T, member2: T) {
  val first: T
  val second: T

  init {
    if (member1 &gt;= member2) {
      first = member1
      second = member2
    } else {
      first = member2
      second = member1
    }
  }                                               

  override fun toString() = &quot;${first}, ${second}&quot;
}</code></pre>
<p><img src="https://images.velog.io/images/sungjun-jin/post/283158db-f683-4f59-8df4-5001ef1162e0/image.png" alt=""></p>
<h3 id="data-classes">Data classes</h3>
<p>데이터 클래스는 데이터 보관을 목적으로 만든 클래스이다. 코틀린 데이터 클래스는 자동적으로 <code>equals(), hashCode(), toString(), copy()</code> 메소드를 생성해 boilerplate code를 만들지 않아도 된다. 데이터 클래스는 클래스 앞에 <code>data</code>를 붙여준다. </p>
<p>특징은 다음과 같다</p>
<ul>
<li>데이터 클래스의 생성자(primary constructor)는 1개 이상의 프로퍼티를 선언되어야 합니다.</li>
<li>데이터 클래스의 생성자 프로퍼티는 val 또는 var으로 선언해야 합니다.</li>
<li>데이터 클래스에 abstract, open, sealed, inner 를 붙일 수 없습니다.</li>
<li>클래스에서 toString(), hashCode(), equals(), copy()를 override하면, 그 함수는 직접 구현된 코드를 사용합니다.</li>
<li>데이터 클래스는 상속받을 수 없습니다.</li>
</ul>
<pre><code class="language-kotlin">data class Task(val id: Int, val name: String, 
  val completed: Boolean, val assigned: Boolean)

val task1 = Task(1, &quot;Create Project&quot;, false, true)

println(task1)
//Task(id=1, name=Create Project, completed=false, assigned=true)
println(&quot;Name: ${task1.name}&quot;) //Name: Create Project</code></pre>
<p>println()을 통해 알아서 생성된 toString()을 호출보면 기본 생성자에 있는 프로퍼티들을 (id, name, completed, assigned) 리스팅해주는 식으로 구현된걸 확인할 수 있다.  </p>
<p>데이터 클래스에서 생성해주는 <code>copy()</code> 메소드는 객체의 복사본을 만들어 리턴한다. 인자로 생성자에 정의된 프로퍼티를 넘길 수 있다. 그 외에 나머지 값은 동일한 객체가 생성된다.</p>
<pre><code class="language-kotlin">val task1 = Task(1, &quot;Create Project&quot;, false, true)

val task1Completed = task1.copy(completed = true, assigned = false)
println(task1Completed) //Task(id=1, name=Create Project, completed=true, assigned=false)</code></pre>
<p>위 처럼 인자로 넘겨진 completed, assigned 이외에는 모두 동일한 값이 복사된걸 확인할 수 있다.</p>
<p>또한 아래와 같은 식으로 프로퍼티 순서대로  destructuring도 가능하다. </p>
<pre><code class="language-kotlin">val (id, _, _, isAssigned) = task1
println(&quot;Id: $id Assigned: $isAssigned&quot;) //Id: 1 Assigned: true</code></pre>
<h4 id="when-to-use-data-classes">When to use data classes?</h4>
<ul>
<li>데이터를 모델링할때 </li>
<li><code>equals(), hashcode(), toString(), copy()</code> 메소드를 오버라이딩 하거나 필요할때</li>
<li>기본 생성자에 하나 이상의 프로퍼티를 필요로 할때, 혹은 프로퍼티만 있어도 될 때</li>
<li>객체로부터 쉽게 데이터를 가져오고 싶을 때 (destructuring 활용)</li>
</ul>
]]></description>
        </item>
        <item>
            <title><![CDATA[educative - kotlin - 6]]></title>
            <link>https://velog.io/@sungjun-jin/educative-kotlin-6</link>
            <guid>https://velog.io/@sungjun-jin/educative-kotlin-6</guid>
            <pubDate>Sun, 14 Mar 2021 07:50:04 GMT</pubDate>
            <description><![CDATA[<h2 id="type-safety-to-save-the-day">Type Safety to Save the Day</h2>
<h4 id="any-and-nothing-classes">Any and Nothing Classes</h4>
<p>코틀린에서 Any 클래스는 모든 클래스가 상속받는 자바의 Object 클래스라고 할 수 있다. 자바의 equals(), toString() 메소드가 Object 클래스의 소속인것 처럼 코틀린에도 <code>equals(), hashCode(), toString()</code> 다양한 메소드가 Any 클래스에 속해 있다. 실직적으로 바이트코드로 컴파일 됐을 때 자바의 Object와 일치합니다.</p>
<h3 id="nothing-is-deeper-than-void">Nothing is deeper than void</h3>
<p>자바의 void는 코틀린의 Unit과 비슷하다. 함수의 리턴타입을 Unit으로 지정하게 된다면 실질적으로 리턴 값이 없어야하며, 명시작으로 return을 작성하지 않아도 된다. 하지만 말그대로 정말 함수가 <code>아무것도</code> 리턴하지 않는다면 Nothing 클래스를 사용해줄 수 있다. </p>
<p>Nothing은 어떠한 값도 포함하지 않는 타입이며 인스턴스를 생성할 수 없다. 코틀린 문서에는 다음과 같이 명시되어 있다.</p>
<blockquote>
<p>Nothing has no instances. You can use Nothing to represent &quot;a value that never exists&quot;</p>
</blockquote>
<p>따라서 Nothing은 예외를 던지는 함수에서 리턴 타입으로 사용되기도 한다. </p>
<pre><code class="language-kotlin">fun throwException(): Nothing {
    throw IllegalStateException()
}</code></pre>
<p>아래 함수를 살펴보자 return 타입이 Double이고, 파라미터로 들어오는 n 값이 양수일때는 Double 타입이 리턴되고 음수일때는 예외가 발생하여 Nothing이 리턴된다. 음수일 경우 리턴타입이 달라 컴파일 에러가 날거 같지만 Nothing 클래스는 모든 타입의 서브 클래스이다. 따라서 아래와 같은 코드는 컴파일 에러없이 잘 돌아간다.</p>
<pre><code class="language-kotlin">fun computeSqrt(n: Double): Double { 
    if(n &gt;= 0) {
        return Math.sqrt(n) 
    } else {
        throw RuntimeException(&quot;No negative please&quot;) 
    }
}</code></pre>
<h3 id="using-nullable-types">Using nullable types</h3>
<p>코틀린에서 nullable 한 타입을 표현하려면 <code>?</code> suffix를 사용해준다. </p>
<pre><code class="language-kotlin">fun nickName(name: String?): String? {
  if (name == &quot;William&quot;) {
    return &quot;Bill&quot;
  }

  return name.reversed()
} </code></pre>
<h4 id="safe-call-operator">Safe-call operator</h4>
<p>하지만 위와 같은 코드는 문제가 있는데 만약 name 파라미터가 null로 들어가게 될 경우 reversed() 함수를 실행하면 에러가 발생한다. 따라서 파라미터에 대한 null checking이 필요한데 코틀린에서는 null checking을 ? 연산자를 사용해 간단히 작성해줄 수 있다. 다른 말로 <code>safe-call operator</code>라고 한다.일반적으로 작성할 수 있는 null checking 코드와 비교해보자</p>
<p>일반적인 null checking</p>
<pre><code class="language-kotlin">if (name != null) {
    return name.reversed()
}

return null</code></pre>
<p>? 연산자를 사용한 null checking</p>
<pre><code class="language-kotlin">return name?.reversed()</code></pre>
<h4 id="elvis-operator">Elvis operator</h4>
<p>만약 위 코드에서 리턴 타입이 null일경우 특정한 값을 리턴하고 싶다면 elvis operator <code>?:</code>를 사용해주면 된다. 여기서는 리턴 타입이 null일때 &quot;Joker&quot;라는 문자열이 리턴된다</p>
<pre><code class="language-kotlin">return name?.reversed() ?: &quot;Joker&quot;</code></pre>
<h3 id="type-checking--smart-casting">Type Checking &amp; Smart Casting</h3>
<p>코틀린에서 is를 사용하면 타입체킹을 할 수 있다.
<code>[타입 체크할 변수] is [확인하고자 하는 타입]</code></p>
<p>Animal 클래스의 equals 메소드를 오버라이딩 해보자</p>
<pre><code class="language-kotlin">class Animal {
   override operator fun equals(other: Any?) = other is Animal
}

val greet: Any = &quot;hello&quot; 
val odie: Any = Animal() 
val toto: Any = Animal()
println(odie == greet) //false 
println(odie == toto) //true</code></pre>
<p>여기까지는 자바의 instanceof 와 비슷하지만 신기한점은 코틀린에서 is 의 결과가 true이면 해당 타입으로 자동 캐스팅을 해준다. </p>
<p>만약 Animal 클래스에 <code>age</code>라는 프로퍼티가 있다고 가정해보자, 그리고 앞서 오버라이딩했던 equals 함수를 인스턴스 타입에 대한 비교뿐만이 아니라 만약 Animal 인스턴스라면 age 또한 비교하는 쪽으로 살짝 바꿔보자. 자바였으면 다음과 같이 코드가 작성되었을 것이다.</p>
<pre><code class="language-java">@Override public boolean equals(Object other) { 
  if(other instanceof Animal) {
    return age == ((Animal) other).age; 
  }
  return false; 
}</code></pre>
<p>과정 </p>
<ul>
<li>other의 인스턴스를 비교한다</li>
<li>Animal 타입의 인스턴스가 맞으면 Animal 타입으로 캐스팅을 하고 age 프로퍼티를 비교해본다</li>
</ul>
<p>하지만 코틀린의 경우 인스턴스 비교 후 true면 자동 타입 캐스팅이 되기 때문에 바로 age 프로퍼티에 접근이 가능하다. 간단하게 코드를 작성할 수 있다 이는 is 뿐만 해당되는게 아니라 &amp;&amp; || 연산자를 사용할때도 적용이 가능하다. </p>
<pre><code class="language-kotlin">// smartcast.kts
class Animal(val age: Int) {
  override operator fun equals(other: Any?):Boolean {
    return if (other is Animal) age == other.age else false
  }
}

override operator fun equals(other: Any?) =
  other is Animal &amp;&amp; age == other.age</code></pre>
<p>만약 확실하지 않은 타입 캐스팅, 즉 unsafe casting이 필요할때는 <code>as</code> 연산자로 처리해준다. 다음 예시를 보자. 메세지를 처리해주는 함수가 있다. 경우에 따라서 String, StringBuilder 타입을 리턴해준다.</p>
<pre><code class="language-kotlin">fun fetchMessage(id: Int): Any =
  if (id == 1) &quot;Record found&quot; else StringBuilder(&quot;data not found&quot;)</code></pre>
<p>이 함수를 아래 반복문에서 호출했을때, String 타입이 리턴되면 정상처리가 되겠지만 StringBuilder 타입이 리턴되었을때는 다음과 같은 에러가 발생할 것이다.</p>
<pre><code class="language-kotlin">for (id in 1..2) {
  println(&quot;Message length: ${(fetchMessage(id) as String).length}&quot;)
}</code></pre>
<p>StringBuilder가 리턴되었을때 에러</p>
<pre><code class="language-shell">Message length: 12
java.lang.ClassCastException: java.base/java.lang.StringBuilder cannot be cast to java.base/java.lang.String</code></pre>
<p>이럴때 as를 사용해주면 된다.</p>
<pre><code class="language-kotlin">val message: String = fetchMessage(1) as String
val message: String? = fetchMessage(1) as? String</code></pre>
<p>null check를 하는 as? 가 as 보다는 안전하다. as?를 사용하면 캐스팅이 안될 경우 null을 리턴하지만 as 연산자를 사용할때는 에러가 발생하기 때문이다 따라서 다음과 같은 권고사항이 있다.</p>
<ul>
<li>smart casting을 최대한 많이 사용해라</li>
<li>smart casting이 옵션이 아닐 경우에만 safe casting을 사용해라</li>
<li>어플리케이션이 터지는걸 보고싶으면 unsafe casting을 사용해라...</li>
</ul>
<h4 id="generics-variance-and-constraints-of-parametic-types">Generics: Variance and Constraints of Parametic Types</h4>
<h4 id="제네릭generics이란">제네릭(Generics)이란</h4>
<blockquote>
<p>클래스, 메소드에서 사용할 데이터 타입을 인스턴스를 생성할 때나 메소드를 호출할때 확정하는 기법이다. 객체의 타입을 컴파일 타임에 체크할 수 있어서 타입 안정성을 높이고 형변환의 번거로움이 줄어든다는 장점이 있다.</p>
</blockquote>
<p>형식은 다음과 같다
<code>List&lt;T&gt;</code>
여기서 T는 타입을 지정할 수 있는 type parameter를 의미한다.</p>
<h4 id="type-invariance">Type invariance</h4>
<p>만약 다음과 같은 클래스 구조가 있다고 가정해보자. 가장 상위 클래스인 Fruit을 두 하위 클래스 Banana와 Orange가 상속받고 있다.</p>
<pre><code>open class Fruit
class Banana : Fruit()
class Orange: Fruit()</code></pre><pre><code class="language-kotlin">fun receiveFruits(fruits: Array&lt;Fruit&gt;) {
  println(&quot;Number of fruits: ${fruits.size}&quot;)
}

val bananas: Array&lt;Banana&gt; = arrayOf() 
receiveFruits(bananas) //type mismatch 에러

val bananas: List&lt;Banana&gt; = listOf() 
receiveFruits(bananas) //OK</code></pre>
<p>여기서 receiveFruits에 파라미터로 들어갈 수 있는 타입은 오로지 Fruit 타입이다. Banana와 Orange가 Fruit의 하위클래스라고 해도 들어갈 수 없다. 이런식으로 제네릭을 사용하면 sub type의 관계가 유지되지 않는것을 type invariance라고 한다.</p>
<p>하지만 여기서 함수의 매개변수를 <code>List&lt;Fruit&gt;</code>으로 바꿔버리면 정상동작한다. 왜냐면 Array와 List의 구조가 살짝 다르기 때문이다. <code>Array&lt;T&gt;와 List&lt;out E&gt;</code></p>
<p>여기서 <code>Array&lt;out Fruit&gt;</code>을 사용해주면 Fruit의 파생 클래스까지 허용이 가능하다.</p>
<pre><code class="language-kotlin">open class Fruit
class Banana : Fruit()
class Orange: Fruit()

fun copyFromTo(from: Array&lt;out Fruit&gt;, to: Array&lt;out Fruit&gt;) {
  for (i in 0 until from.size) {
    to[i] = from[i]
  }
}

val fruitsBasket = Array&lt;Fruit&gt;(3) { _ -&gt; Fruit() }
val bananaBasket = Array&lt;Banana&gt;(3) { _ -&gt; Banana() }

copyFromTo(bananaBasket, fruitsBasket) //OK</code></pre>
<p>만약 어떤 타입이 올지 모르면 List&lt;*&gt;를 사용해주면 된다. 하지만 read only라는 점!</p>
<pre><code class="language-kotlin">fun printValues(values: Array&lt;*&gt;) {
  for (value in values) {
    println(value)
  }

  //values[0] = values[1] //이런 식으로 값을 바꿔주려 하면 ERROR
}

printValues(arrayOf(1, 2)) //1\n2</code></pre>
<h4 id="parametic-type-constraints-using-where">Parametic type constraints using where</h4>
<p>예를들어 다음 함수에서 인자로 들어가는 input에 close() 메소드가 구현되지 않았다면 다음과 같은 에러가 날 것이다.</p>
<pre><code class="language-kotlin">fun &lt;T&gt; useAndClose(input: T) {
  input.close() //ERROR: unresolved reference: close
}</code></pre>
<p>여기서 close() 인터페이스를 가지고 있는 인자만 넘어오도록 할 수 있다</p>
<pre><code class="language-kotlin">fun &lt;T: AutoCloseable&gt; useAndClose(input: T) {
  input.close() //OK
}</code></pre>
<p>2개 이상의 조건을 걸어주고 싶으면 where를 사용하면 된다.</p>
<pre><code class="language-kotlin">fun &lt;T&gt; useAndClose(input: T) 
  where T: AutoCloseable,
        T: Appendable {
  input.append(&quot;there&quot;)
  input.close()
}</code></pre>
<h4 id="applying-reification">Applying reification</h4>
<p>아래와 같은 상황에서 첫번째 인스턴스의 타입을 반환하는 findFirst() 메소드를 정의한다고 해보자.</p>
<pre><code class="language-kotlin">val books: List&lt;Book&gt; = listOf(
  Fiction(&quot;Moby Dick&quot;), NonFiction(&quot;Learn to Code&quot;), Fiction(&quot;LOTR&quot;))

 fun &lt;T&gt; findFirst(books: List&lt;Book&gt;, ofClass: Class&lt;T&gt;): T {
    val selected = books.filter { book -&gt; ofClass.isInstance(book) }
    if(selected.size == 0) {
        throw RuntimeException(&quot;Not found&quot;)
    }
    return ofClass.cast(selected[0]) 
}

println(findFirst(books, NonFiction::class.java).name) //Learn to Code</code></pre>
<pre><code class="language-kotlin">inline fun &lt;reified T&gt; findFirst(books: List&lt;Book&gt;): T {
  val selected = books.filter { book -&gt; book is T }

  if(selected.size == 0) {
    throw RuntimeException(&quot;Not found&quot;)
  }

  return selected[0] as T
}</code></pre>
]]></description>
        </item>
        <item>
            <title><![CDATA[educative - kotlin - 5]]></title>
            <link>https://velog.io/@sungjun-jin/educative-kotlin-5</link>
            <guid>https://velog.io/@sungjun-jin/educative-kotlin-5</guid>
            <pubDate>Sat, 13 Mar 2021 08:52:59 GMT</pubDate>
            <description><![CDATA[<h2 id="using-collections">Using Collections</h2>
<h4 id="collection이란">Collection이란</h4>
<blockquote>
<p>Collection 객체는 여러 원소들을 담을 수 있는 자료구조를 뜻한다. 
ex) DTO, Array, List, Set, Map</p>
</blockquote>
<h3 id="types-of-collections-in-kotlin">Types of collections in Kotlin</h3>
<p>코틀린 Collection은 기본적으로 Mutable (read-write), immutable(read-only) 컬렉션을 별개로 지원한다. 아래와 같은 상속 구조를 가지고 있다. 코틀린에서는 동시성, 병렬 문제에서 더 안전한 immutable collection을 권장한다.
<img src="https://images.velog.io/images/sungjun-jin/post/03591106-5cb0-4178-b3ed-d34181cc182f/image.png" alt=""></p>
<p>코틀린에서 제공하는 collection은 다음과 같다</p>
<ul>
<li>Pair : 동일하거나 다른 데이터 유형의 두 값으로 이루어진 튜플</li>
<li>Triple : 동일하거나 다른 데이터 유형의 세 값으로 이루어진 튜플</li>
<li>Array : 인덱스로 접근가능한 고정된 길이의 객체나 값으로 이루어진 컬렉션</li>
<li>List : 정렬된 순서의 객체로 이루어진 컬렉션</li>
<li>Set : 정렬되지 않는 순서의 객체로 이루어진 컬렉션</li>
<li>Map : key와 value로 이루어진 딕셔너리</li>
</ul>
<p>코틀린에서는 자바의 컬렉션과 함께 사용할 수 있는 다양한 함수들을 지원한다. 예를들어 withIndex() 메소드는 List의 인덱스와 값을 리턴해주는 IndexedValue라는 데이터 클래스를 반환한다. 이 데이터 클래스를 destructing한 값인 index, value를 출력해보면 다음과 같은 결과가 나타난다</p>
<pre><code class="language-kotlin">val names = listOf(&quot;Tom&quot;, &quot;Jerry&quot;)

for ((index, value) in names.withIndex()) {
    println(&quot;$index $value&quot;)
}
&quot;&quot;&quot;
0 Tom
1 Jerry
&quot;&quot;&quot;</code></pre>
<h4 id="using-pair-and-triple">Using Pair and Triple</h4>
<p>코틀린에서 2개, 혹은 3개의 객체를 저장할 수 있는 튜플을 각각 Pair, Triple 타입이라고 한다. </p>
<pre><code class="language-kotlin">println(Pair(&quot;Tom&quot;, &quot;Jerry&quot;)) //(Tom, Jerry)
println(mapOf(&quot;Tom&quot; to &quot;Cat&quot;, &quot;Jerry&quot; to &quot;Mouse&quot;)) //{Tom=Cat, Jerry=Mouse} 

// map은 key와 value로 이루어진 딕셔너리이므로 key를 사용해 value에 접근할 수 있음
val map = mapOf(&quot;Tom&quot; to &quot;Cat&quot;, &quot;Jerry&quot; to &quot;Mouse&quot;)
println(map[&quot;Tom&quot;]) // Cat
println(map[&quot;Jerry&quot;]) // Mouse</code></pre>
<p>to() 메소드는 Pair 인스턴스를 생성하는 코틀린의 확장함수(extension function) 이다.</p>
<pre><code class="language-kotlin">&gt;&gt;&gt; &quot;Tom&quot; to &quot;Cat&quot;
res85: kotlin.Pair&lt;kotlin.String, kotlin.String&gt; = (Tom, Cat)
&gt;&gt;&gt; &quot;Jerry&quot; to &quot;Mouse&quot;
res88: kotlin.Pair&lt;kotlin.String, kotlin.String&gt; = (Jerry, Mouse)</code></pre>
<p>예를들어 공항 코드(String)와 온도(Double)을 통해 특정 공항의 온도를 알고 싶다면 Pair를 사용해 효과적으로 구현할 수 있다.</p>
<pre><code class="language-kotlin">val airportCodes = listOf(&quot;LAX&quot;, &quot;SFO&quot;, &quot;PDX&quot;, &quot;SEA&quot;)

val temperatures = airportCodes.map{code -&gt; code to getTemperatureAirport(c0de)}

for (temp in temperatures) {
  println(&quot;Airport: ${temp.first}: Temperator ${temp.second}&quot;)
}</code></pre>
<p>airportCode 컬렉션을 돌면서 해당 공항의 온도를 map() 함수와 to() 함수를 통해 공항코드와 온도로 이루어진 Pair 인스턴스를 만들어준다.</p>
<p>Triple도 Pair의 특성과 동일하다. 예를들어 Circle에 대한 정보를 담는 데이터 클래스를 생성하고 싶다면 다음과 같이 (x, y, radius) 값을 가지고 있는 Triple 객체를 사용해줄 수 있다</p>
<pre><code class="language-kotlin">val circle = Triple&lt;Int, Int, Double&gt;(1,1,2.5)
println(circle) //(1, 1, 2.5)</code></pre>
<p>Pair, Triple 모두 immutable한 튜플이다. 만약 2,3 이상의 immutable 객체를 저장하고 싶다면 데이터 클래스를 생성해야 한다.</p>
<h4 id="creating-an-array-in-kotlin">Creating an array in Kotlin</h4>
<p>코틀린에서 array는 mutable한 값의 컬렉션이다. arrayOf() 함수를 사용해 인스턴스를 생성할 수 있고 <code>[]</code> 연산자를 통해 값에 대한 접근이 가능하다.</p>
<pre><code class="language-kotlin">val friends = arrayOf(&quot;Tintin&quot;, &quot;Snowy&quot;, &quot;Haddock&quot;)

println(friends::class) //class kotlin.Array
println(friends.javaClass) //class [Ljava.lang.String]
println(&quot;${friends[0]}, and ${friends[1]}&quot;) //Tintin and Snowy</code></pre>
<p>friends 변수는 새롭게 만들어진 <code>Kotlin.Array = Array&lt;T&gt;</code> 타입의 객체를 참조한다. 이는 String으로 이루어진 Java 배열이다.</p>
<p>정수형 배열 또한 똑같이 선언해주면 된다. </p>
<pre><code class="language-kotlin">val numbers = arrayOf(1, 2, 3)

println(numbers::class) //class kotlin.Array
println(numbers.javaClass) //class [Ljava.lang.Integer;</code></pre>
<p>그러나 좋은 방법은 아니다. 정수나 문자열이나 arrayOf() 함수를 통해 넘겨주면 똑같이 <code>Array&lt;T&gt;</code> 인스턴스가 생성되지만, 내부 요소들은 Integer 클래스를 참조하는 박싱된 Integer 타입이다. 단순한 정수로 이루어진 배열을 선얼할때 굳이 이렇게 선언해줄 필요가 없으므로 아예 arrayOf() 함수 대신 intArrayOf()를 사용해 배열의 요소를 원시(primitive) 타입으로 해주면 더 효율적이다</p>
<pre><code class="language-kotlin">val numbers = intArrayOf(1, 2, 3) // 효율 Good

println(numbers::class) //class kotlin.Array
println(numbers.javaClass) //class [Ljava.lang.Integer;</code></pre>
<p>Array 클래스는 내부적으로 지원해주는 편한 프로퍼트와 함수들이 있다.</p>
<pre><code class="language-kotlin">println(numbers.size) //3 
println(numbers.average()) //2.0

println(Array(5) { i -&gt; (i + 1) * (i + 1) }.sum()) //55</code></pre>
<h4 id="creating-list--set-in-kotlin">Creating list &amp; Set in Kotlin</h4>
<p><code>listOf()</code> 함수를 사용하면 immutable한 리스트, mutableListOf()를 사용하면 mutable한 리스트가 생성된다. 똑같이 <code>[]</code> 연산자를 사용해 배열의 요소에 대하 접근이 가능하다. <code>contains()</code> 메소드 혹은 <code>in</code> 연산자를 통해 값에 존재 여부를 확인할 수 있다.</p>
<pre><code class="language-kotlin">val fruits: List&lt;String&gt; = listOf(&quot;Apple&quot;, &quot;Banana&quot;, &quot;Grape&quot;)
println(fruits) //[Apple, Banana, Grape]

// lists.kts
println(&quot;first&#39;s ${fruits[0]}, that&#39;s ${fruits.get(0)}&quot;) 
//first&#39;s Apple, that&#39;s Apple

println(fruits.contains(&quot;Apple&quot;)) //true 
println(&quot;Apple&quot; in fruits) //true</code></pre>
<p>listOf() 함수를 사용하면 immutable한 리스트를 생성하기 때문에 다음과 같이 리스트 안에 <code>add()</code>를 통해 값을 추가하려고 하면 컴파일 오류가 발생한다. 따라서 배열의 요소를 추가, 혹은 삭제하고 싶다면 <code>+ , -</code>연산자를 사용하면 된다. 이 연산자를 사용하면 원래 리스트를 복사한 다음, 추가 &amp; 삭제된 값을 반영해주는 새로운 리스트가 생성된다..</p>
<pre><code class="language-kotlin">val fruits: List&lt;String&gt; = listOf(&quot;Apple&quot;, &quot;Banana&quot;, &quot;Grape&quot;)
fruits.add(&quot;Orange&quot;) //ERROR: unresolved reference: add

val fruits: List&lt;String&gt; = listOf(&quot;Apple&quot;, &quot;Banana&quot;, &quot;Grape&quot;)
val fruits2 = fruits + &quot;Orange&quot;

println(fruits) //[Apple, Banana, Grape] 
println(fruits2) //[Apple, Banana, Grape, Orange]

val fruits: List&lt;String&gt; = listOf(&quot;Apple&quot;, &quot;Banana&quot;, &quot;Grape&quot;)
val noBanana = fruits - &quot;Banana&quot; 

println(noBanana) //[Apple, Grape]</code></pre>
<p>물론 이것도 처음부터 mutable한 리스트를 생성하면 <code>add()</code> 메소드 사용이 가능하다. 하지만 코틀린은 당연히 immutable 리스트를 권장함.</p>
<pre><code class="language-kotlin">val fruits: MutableList&lt;String&gt; = mutableListOf(&quot;Apple&quot;, &quot;Banana&quot;, &quot;Grape&quot;)
fruits.add(&quot;Orange&quot;)
println(fruits) // [Apple, Banana, Grape, Orange]</code></pre>
<p>Set은 정렬되지 않는 순서의 객체로 이루어진 컬렉션이다. <code>setOf(), mutableSetOf(), hashSetOF()</code> 함수를 사용해 생성이 가능하다.</p>
<pre><code class="language-kotlin">val fruits: Set&lt;String&gt; = setOf(&quot;Apple&quot;, &quot;Banana&quot;, &quot;Apple&quot;)</code></pre>
<h4 id="using-map">Using Map</h4>
<p>Map은 Key, Value 페어로 이루어진 컬렉션이다. <code>mutableMapOf(), hashMapOF(), linkedMapOf(), sortedMapOF()</code>등의 함수를 사용하여 생성이 가능하다.</p>
<pre><code class="language-kotlin">val sites = mapOf(&quot;pragprog&quot; to &quot;https://www.pragprog.com&quot;,
  &quot;agiledeveloper&quot; to &quot;https://agiledeveloper.com&quot;)

println(sites.size) //2
println(sites.containsKey(&quot;agiledeveloper&quot;)) //true
println(sites.containsValue(&quot;http://www.example.com&quot;)) //false
println(sites.contains(&quot;agiledeveloper&quot;)) //true
println(&quot;agiledeveloper&quot; in sites) //true</code></pre>
<p><code>[key]</code> 연산자를 통해 값에 대한 접근이 가능하다. 만약 key에 대한 존재여부가 확실하지 않는다면 getOrDefault() 함수를 사용해 key 값이 존재하지 않을때에 대한 return 값을 지정해줄 수 있다.</p>
<pre><code class="language-kotlin">val pragProgSite2: String? = sites[&quot;pragprog&quot;]
val agiledeveloper =
  sites.getOrDefault(&quot;agiledeveloper&quot;, &quot;http://www.example.com&quot;)</code></pre>
<p>리스트와 똑같이 +,- 연산자를 사용해 추가 &amp; 삭제된 값이 반영된 새로운 map을 생성해줄 수 있다.</p>
<pre><code class="language-kotlin">val sitesWithExample = sites + (&quot;example&quot; to &quot;http://www.example.com&quot;)
val withoutAgileDeveloper = sites - &quot;agiledeveloper&quot;</code></pre>
<p>destructuring을 사용해 반복문을 돌면서 key, value값을 가져올 수 있다.</p>
<pre><code class="language-kotlin">for ((key, value) in sites) {
    println(&quot;$key --- $value&quot;)
}</code></pre>
]]></description>
        </item>
        <item>
            <title><![CDATA[educative - kotlin - 4]]></title>
            <link>https://velog.io/@sungjun-jin/educative-kotlin-4</link>
            <guid>https://velog.io/@sungjun-jin/educative-kotlin-4</guid>
            <pubDate>Sun, 07 Mar 2021 13:29:42 GMT</pubDate>
            <description><![CDATA[<h3 id="range-and-iteration">Range and Iteration</h3>
<p>코틀린에서 1부터 5사이의 숫자와 같은 범위를 표현할때는 Range 클래스를 사용해준다. 기본적으로 마지막 범위를 포함시키며 증가 또는 감소값은 기본 1이다.</p>
<p><code>&lt;첫번째 값&gt; .. &lt;마지막 값&gt;</code></p>
<p>숫자 범위 뿐만 아니라 문자 범위, 문자열 범위도 표현할 수 있다.</p>
<pre><code class="language-kotlin">val oneToFive: IntRange = 1..5 //1 ~ 5까지의 정수
val aToE: CharRange = &#39;a&#39;..&#39;e&#39; // &#39;a&#39; ~ &#39;e&#39; 까지의 소문자 알파벳
val seekHelp: ClosedRange&lt;String&gt; = &quot;hell&quot;..&quot;help&quot; // //&quot;help&quot; ~ &quot;hell&quot; 까지의 문자열

println(seekHelp.contains(&quot;helm&quot;) //true
println(seekHelp.contains&quot;helq&quot;) //false</code></pre>
<p>범위를 정해주었다면 for (x in ..)문을 사용해 반복문을 만들수 있다. for문 안에있는 <code>x</code>또한 암묵적으로 val이다.</p>
<pre><code class="language-kotlin">for (i in 1..5) {print(&quot;$i, &quot;)} // 1, 2, 3, 4, 5
for (ch in &#39;a&#39;..&#39;e&#39;) { print(ch) } //abcde</code></pre>
<p>downTo() 메소드를 사용해 reverse iteration 또한 가능하다.</p>
<pre><code class="language-kotlin">for (i in 5.downTo(1)) {print&quot;$i, &quot;)} // 5, 4, 3, 2, 1
for (i in 5 downTo 1) { print(&quot;$i, &quot;) } //5, 4, 3, 2, 1,</code></pre>
<p>또한 맨 끝의 요소를 포함시키지 않는 범위로 동작시키고 시다면 until()을 이용한다.</p>
<pre><code class="language-kotlin">for (i in 1 until 5) {print(&quot;$i ,&quot;)} // 1, 2, 3, 4</code></pre>
<p>1이 아닌 일정 간격으로 동작시키고 싶다면 step()을 이용한다</p>
<pre><code class="language-kotlin">for (i in 1 until 10 step 3) { print(&quot;$i, &quot;) } //1, 4, 7,
or (i in 10 downTo 0 step 3) { print(&quot;$i, &quot;) } //10, 7, 4, 1,</code></pre>
<p>filter()를 사용해 조건을 걸어줄 수 있다.</p>
<pre><code class="language-kotlin">for (i in (1..9).filter { it % 3 == 0 || it % 5 == 0 }) { 
    print(&quot;$i, &quot;) //3, 5, 6, 9,
}</code></pre>
<h4 id="iterating-over-arrays-and-lists">Iterating over Arrays and Lists</h4>
<p>코틀린에서 배열을 생성하기 위해서는 코틀린 패키지에 있는 <code>kotlin.arrayOf()</code>를 사용해준다. 참고로 코틀린 패키지안에 내장된 함수들은 kotlin. prefix를 사용하지 않고 호출이 가능하다.</p>
<pre><code class="language-kotlin">val array = arrayOf(1, 2, 3)

for (e in array) { print(&quot;$e, &quot;)} //1, 2, 3</code></pre>
<p>좀더 구체적으로 정수형 배열 선언을 명시하고 싶다면 <code>intArrayOf()</code> 함수를 호출해주면 된다.</p>
<p>혹은 리스트 인스턴스 <code>List&lt;T&gt;</code>로 만들어줄 수 있다.</p>
<p>배열의 인덱스를 활용하고 싶다면 <code>indices</code> 프로퍼트를 사용해준다.</p>
<pre><code class="language-kotlin">val names = listOf(&quot;Tom&quot;, &quot;Jerry&quot;, &quot;Spike&quot;)

for (index in names.indices) {
  println(&quot;Position of ${names.get(index)} is $index&quot;)
}
&quot;&quot;&quot;
Position of Tom is 0
Position of Jerry is 1
Position of Spike is 2
&quot;&quot;&quot;</code></pre>
<p>value와 index를 둘 다 활용하고 싶다면 <code>withIndex()</code> 함수를 사용해준다. 파이썬의 <code>enumerate()</code> 와 비슷하다.</p>
<pre><code class="language-kotlin">for ((index, name) in names.withIndex()) { 
   println(&quot;Position of $name is $index&quot;)
}
&quot;&quot;&quot;
Position of Tom is 0
Position of Jerry is 1
Position of Spike is 2
&quot;&quot;&quot;</code></pre>
<h4 id="when-its-time-to-use-when">When It&#39;s Time to Use when</h4>
<p>코틀린에서 when문은 다른 언어의 switch문과 동일하다. 자바의 case 대신 -&gt; 을 사용해준다. 맨 밑에 else문으로 조건에 맞지 않을때 행동을 정의할 수 있다.</p>
<pre><code class="language-kotlin">fun whatToDo(dayOfWeek: Any) =  when (dayOfWeek) {
  &quot;Saturday&quot;, &quot;Sunday&quot; -&gt; &quot;Relax&quot;
  in listOf(&quot;Monday&quot;, &quot;Tuesday&quot;, &quot;Wednesday&quot;, &quot;Thursday&quot;) -&gt; &quot;Work hard&quot;
  in 2..4 -&gt; &quot;Work hard&quot;
  &quot;Friday&quot; -&gt; &quot;Party&quot;
  is String -&gt; &quot;What?&quot;
  else -&gt; &quot;No clue&quot;
}

println(whatToDo(&quot;Sunday&quot;)) //Relax
println(whatToDo(&quot;Wednesday&quot;)) //Work hard
println(whatToDo(3)) //Work hard
println(whatToDo(&quot;Friday&quot;)) //Party
println(whatToDo(&quot;Munday&quot;)) //What?
println(whatToDo(8)) //No clue</code></pre>
]]></description>
        </item>
        <item>
            <title><![CDATA[educative - kotlin - 3]]></title>
            <link>https://velog.io/@sungjun-jin/educative-kotlin-3</link>
            <guid>https://velog.io/@sungjun-jin/educative-kotlin-3</guid>
            <pubDate>Sat, 06 Mar 2021 07:10:43 GMT</pubDate>
            <description><![CDATA[<h3 id="working-with-functions">Working with Functions</h3>
<h4 id="creating-functions">Creating Functions</h4>
<p>코틀린에서 함수는 <code>fun</code> 키워드로 정의한다. 뒤이어 함수의 이름과 매개변수(parameter) 리스트를 정의해준다.</p>
<pre><code class="language-kotlin">fun greet() = &quot;hello&quot;</code></pre>
<p>위와 같은 간단한 단일표현 함수 (single-expression function) 이라면 함수의 body 부분의 중괄호({})를 = 연산자로 대체 가능하다. 또한 return 문도 사용하지 않는다. 이런 단일표현 함수들은 코틀린에서 자동으로 컴파일 레벨에서 타입추론(type inference)을 통해 반환타입을 지정해주기 때문에 return 타입을 직접 명시해주지 않는다. 따라서 아래와 같은 코드는 컴파일 에러가 발생한다.</p>
<pre><code class="language-kotlin">fun greet() = &quot;hello&quot;

var message: Int = greet() // type mismatch error</code></pre>
<p>타입 추론을 통해 코틀린은 greet() 함수의 리턴타입을 String으로 정의했다. 따라서 Int형 message 변수에 greet()의 리턴값을 담으려고 한다면 type mismatch 컴파일 에러가 발생할 것이다.</p>
<p>코틀린에서 Unit 타입은 자바의 void와 같은 역할을 한다. 따라서 반환값이 없는 함수들에 한해서 리턴 타입을 Unit으로 명시해줄수 있다 (생략가능).</p>
<pre><code class="language-kotlin">fun sayHello(): Unit = println(&quot;Well, hello&quot;)

val message: Unit = sayHello()

println(&quot;The result of sayHello is $message&quot;)
&quot;&quot;&quot;
[실행결과]
Well, hello
The result of sayHello is kotlin.Unit
&quot;&quot;&quot;</code></pre>
<p>리턴 타입은 위처럼 매개변수를 정의해준 다음에 콜론(:)과 함께 정의해준다. 코틀린 Unit이 자바의 void와 다른점이 있다면, 자바에서 void는 존재하지 않음을 뜻하지만 코틀린에서 Unit 클래스로 정의되어 있으며, 아무것도 반환하지 않는 <strong>타입</strong> 을 뜻한다. Unit 타입은 toString(), equals(), hashCode와 같은 메소드를 가지고 있다. sayHello() 함수에서 println()은 내부적으로 Unit 객체의 toString() 메소드를 호출하기 때문에 위와 같은 실행결과가 나타난다.</p>
<p>매개변수를 정의할때는 파이썬과 똑같이 <code>이름: 타입</code> 으로 정의해준다. 2개이상일 경우 콤마(,)로 구분해준다. </p>
<pre><code class="language-kotlin">fun greet(name: String) = println(&quot;Hello $name&quot;)
println(greet(&quot;Eve&quot;)) // Hello Eve</code></pre>
<p>매개변수를 정의할때는 var, val 키워드도 사용하지 않는다. 이유는 코틀린에서 암묵적으로 매개변수는 val로 처리를 해준다. 그래서 실험을 해봤다</p>
<pre><code class="language-kotlin">fun greet(name: String): String = {name = &quot;sungjun&quot;}
// error: val cannot be reassigned</code></pre>
<p>매개변수로 넣어주는 name의 값을 수정하려고 시도해보니 위와 같은 에러가 나타났다. 따라서 코틀린에서 매개변수는 val로 지정된다!</p>
<p>함수가 단일표현 함수(single-expression function)이 아닌경우는 자바와 동일하게 중괄호를 통해서 함수의 body를 정의해준다. 리턴타입 또한 명시해줘야 한다.</p>
<pre><code class="language-kotlin">fun max(numbers: IntArray): Int {
    var large = Int.MIN_VALUE

    for (number in numbers) {
        large = if (number &gt; large) number else large
    }

    return large
}

println(max(intArrayOf(1,5,2,12,7,3))) // 12</code></pre>
<h4 id="default-and-named-arguments">Default and Named Arguments</h4>
<p><strong>디폴트 인자(default argument)</strong> 는 입력 변수의 기본 값을 정해줄 수 있다. 함수를 사용할 때 해당 입력 변수에 값을 입력하지 않아도 정의된 기본 값이 사용된다. 즉, 기본 값이 있는 매개변수들은 함수를 사용할때 입력하지 않으면 기본 값이 사용되고, 입력하면 입력한 값이 사용된다.</p>
<pre><code class="language-kotlin">fun greet(name: String, msg: String = &quot;Hello&quot;): String = &quot;$msg, $name&quot;

println(greet(&quot;Eve&quot;)) // Hello, Eve
println(greet(&quot;Eve&quot;, &quot;Howdy&quot;)) // Howdy, Eve</code></pre>
<p>디폴트인자와 일반인자를 함께 사용할때 주의할점이 있다. 일반 매개변수는 순서대로 들어오는 값이 필요하기 때문에 변수의 값을 입력하지 않아도 되는 디폴트 인자는 일반변수 뒤에 사용해준다.</p>
<p><strong>지명 인자(named argument)</strong> 를 사용하면 함수를 호출할때 매개변수의 이름과 값을 동시에 전달해줄 수 있다. </p>
<pre><code class="language-kotlin">fun createPerson(name: String, age: Int = 1, height: Int, weight: Int) {
    println(&quot;$name $age $height $weight&quot;)
}</code></pre>
<p>위 함수를 두 가지 방법으로 호출해보자</p>
<pre><code class="language-kotlin">createPerson(&quot;Jake, 12, 152, 43)</code></pre>
<p>위 같은 방식으로 호출하면 12, 152, 43과 같은 Int형 매개변수가 어떤 값을 의미하는지 문맥상 추론이 어렵다. 이를 named argument를 통해 아래와 같이 가독성을 개선시킬 수 있다. 또한 named argument를 사용하면 매개변수가 정의된 순서와 무관하게 값을 전달시킬 수 있다.</p>
<pre><code class="language-kotlin">createPerson(name=&quot;Jake&quot;, age=12, height=152, weight=43)
// parameter 순서 바꿔보기 
createPerson(name=&quot;Jake&quot;, height=152, age=12, weight=43)</code></pre>
<h3 id="vararg-and-spread">vararg and Spread</h3>
<p>함수를 정의할 때 매개변수에 vararg(variable arguments, 가변인자) 키워드를 사용하면 호출 시 인자의 개수를 가변적으로 전달할 수 있다.</p>
<pre><code class="language-kotlin">fun max(vararg numbers: Int): Int {
    var large = Int.MIN_VALUE

    for (number in numbers) {
        large = if number &gt; large number else large
    }

    return large
}

println(max(1,5,2)) // 5
println(max(1,5,2,12,7,3)) // 12</code></pre>
<p>매개변수로 넘어가는 Int형 타입의 numbers 앞에 vararg 키워드를 사용하면 실제로 매개변수는 Int타입의 가변 길이의 배열로 넘어간다. </p>
<p>만약 varargs로 할당된 numbers 매개변수에 고정된 길이의 Int형 배열을 넣으면 어떻게 될까?</p>
<pre><code class="language-kotlin">val values = intArrayOf(1, 21, 3)

println(max(values)) // type mistmach: inferred type is IntArray but Int was expected</code></pre>
<p>type mismatch 에러가 발생하는 이유는 numbers 매개변수는 IntArray 타입이 아닌, Int 타입만 허용하기 때문이다. vararg는 가변 인자를 단일 인자(single parameter) 형식으로 전달받기를 강제한다 따라서 배열을 전달하게 되면 에러가 발생하는 것이다. 아래와 같이 극단적인 코드를 작성해 해결할 수는 있다...</p>
<pre><code class="language-kotlin">println(max(values[0], values[1], values[2])) //SMELLY, don&#39;t</code></pre>
<p>vararg로 명시된 가변인자에 배열을 전달하고자 할때는 * 연산자를 prefix로 사용하면 된다.</p>
<pre><code class="language-kotlin">println(max(*values)) //21
println(max(*listOf(1, 4, 18, 12).toIntArray()))</code></pre>
<p>2개 이상의 매개변수를 정의한 함수에서 가변인자는 가장 뒤에 정의하는것이 좋다.</p>
<pre><code class="language-kotlin">fun greetMany(vararg names: String, msg: String) {
    println(&quot;$msg ${names.joinToString(&quot;, &quot;)}&quot;)
}

greetMany(&quot;Hello&quot;, &quot;Tom&quot;, &quot;Jerry&quot;, &quot;Spike&quot;)</code></pre>
<p>만약 위와 같은 식으로 정의하면 컴파일러가 어디서부터 어디까지가 names 인자에 해당하는지 헷갈려 명확한 구분을 위해 뒤에 있는 msg 매개변수에 named parameter를 사용하도록 강제할 것이다.</p>
<h4 id="destructuring">Destructuring</h4>
<p>Python의 unpacking과 비슷한 개념인 것 같다.</p>
<pre><code class="language-kotlin">fun getFullName() = Triple(&quot;John&quot;, &quot;Quincy&quot;, &quot;Adams&quot;)

val result = getFullName()
val firstName = result.first
val middleName = result.second
val lastName = result.third

println(&quot;$firstName $middleName $lastName&quot;) // John Quincy Adams</code></pre>
<p>이런식의 긴 코드를</p>
<pre><code class="language-kotlin">val (firstName, middleName, lastName) = getFullName()

println(&quot;$firstName $middleName $lastName&quot;) // John Quincy Adams</code></pre>
<p>이렇게 간결하게 작성할 수 있다. </p>
<p>참고로 Triple은 코틀린에서 3개의 객체를 저장할 수 있는 라이브러리이다. first, second, thirtd 프로퍼티를 사용해 해당 순서의 값을 가져올 수 있다. 비슷한 개념으로 Pair가 있다.</p>
<p>파이썬과 똑같이 _ 연산자를 사용해 값을 skip 할 수 있다 (단, destructuring되는 순서는 지켜야 한다)</p>
<pre><code class="language-kotlin">val (first, _, last) = getFullName() 

println(&quot;$first $last&quot;) //John Adams

val (_, _, last) = getFullName() 

println(last) //Adams

val (_, middle) = getFullName() 

println(middle) //Quincy</code></pre>
]]></description>
        </item>
        <item>
            <title><![CDATA[educative - Kotlin - 2]]></title>
            <link>https://velog.io/@sungjun-jin/educative-Kotlin-2</link>
            <guid>https://velog.io/@sungjun-jin/educative-Kotlin-2</guid>
            <pubDate>Mon, 01 Mar 2021 09:45:19 GMT</pubDate>
            <description><![CDATA[<h2 id="kotlin-essentials-for-the-java-eyes">Kotlin Essentials for the Java Eyes</h2>
<h3 id="1-less-typing-optionals">1) Less Typing (Optionals)</h3>
<h4 id="semicolon">Semicolon</h4>
<p>코틀린 개발자들은 세미콜론은 굳이 권장하지 않는다고 한다. 세미콜론은 한줄에 여러 문장을 적는 경우를 빼고 사용하지 않아도 된다. </p>
<pre><code class="language-kotlin">val sum = 6 + 2</code></pre>
<h4 id="variable-type-specification">Variable type specification</h4>
<p>코틀린은 정적 타입 지정 언어다. 컴파일 시점에서 모든 객체나 메소드의 타입을 알 수 있다. 즉, 컴파일러가 타입을 확정하고 검증해준다. 코틀린은 문맥을 통해 변수의 타입을 추론하는 타입 추론(type inference) 기능을 지원한다.</p>
<pre><code class="language-kotlin">val greet = &quot;hello&quot;

println(greet)
println(greet::class)
println(greet.javaClass)</code></pre>
<p>결과</p>
<pre><code class="language-shell">hello
class kotlin.String
class java.lang.String</code></pre>
<p>::class call은 해당 객체가 참조하는 Kotlin class를 반환한다. .javaClass는 해당되는 Java class를 반환해준다. greet 변수에 대입되는 값이 문자열이므로 코틀린 컴파일러는 변수의 타입을 String형임을 추론하는것을 확인할 수 있다.</p>
<p>따라서 아래처럼 미리 greet이라는 변수에 문자열 할당한 다음 정수형 변수를 재할당(reassgine)하게 되면 코틀린은 컴파일 레벨에서 변수에 따른 type error를 미리 감지할 수 있다.</p>
<pre><code class="language-kotlin">val greet = &quot;hello&quot;
println(greet)

greet = 0 //error</code></pre>
<p>결과</p>
<pre><code class="language-shell">error: val cannot be reassigned
greet = 0
^
error: the integer literal does not conform to the expected type String
greet = 0</code></pre>
<p>결론은 변수명을 지을때 한번에 제대로 지어줘야 한다. 또한 변수명을 지을때 타입에 대한 명시는 하지 않는게 좋다. ex) taxRateDouble = 0.08</p>
<h4 id="classes-and-functions">Classes and Functions</h4>
<p>자바와 다르게 코틀린은 선언, 표현식 없이 클래스와 함수 작성이 가능하다. 코틀린 컴파일러는 코드가 컴파일되거나 스크립트로 실행될 시점에 JVM expectation에 맞는 기준의 wrapper 클래스나 메소드를 생성해 처리해주기 때문이다.</p>
<pre><code class="language-kotlin">fun nofluff() {
    println(&quot;nofluff called...&quot;)
    throw RuntimeException(&quot;oops&quot;)
}

println(&quot;not in a function, calling nofluff()&quot;)

try {
    nofluff()
} catch(ex: Exception) {
    val stackTrace = ex.getStackTrace()
    println(stackTrace[0])
    println(stackTrace[1])
}</code></pre>
<p>결과</p>
<pre><code class="language-shell">not in a function, calling nofluff() 
nofluff called... 
Standalone.nofluff(standalone.kts:4) 
Standalone.&lt;init&gt;(standalone.kts:10)</code></pre>
<p>실행 결과를 확인해보면 작성한 함수를 코틀린 컴파일러가 Standalone이라는 클래스로 wrapping 해버린 모습을 확인할 수 있다. 이런 간단한 구조의 코드는 클래스나 함수부 선언 없이 작성해도 되지만 코드의 구조가 복잡해질수록 클래스와 메소드를 적절히 조합해 구조상의 설계를 잘 잡아줘야 한다.</p>
<h4 id="try---catch">try - catch</h4>
<p>코틀린은 try-catch문을 강요하지 않는다. 코틀린에서 만약 try-catch로 처리되지 않는 코드가 오류를 낸다면, 해당 exception은 자동적으로 호출부로 넘어가게 되고 만약 exception handling 처리를 하지 못한다면 프로그램이 종료된다. </p>
<h3 id="prefer-val-over-var">Prefer val over var</h3>
<p>불변(immutable) 타입 변수를 선언할때는 val을 사용한다 (코틀린에서는 변수명 뒤에 콜론(:)를 붙혀 변수의 타입을 hinting 해줄 수 있다. val은 자바의 final에 해당한다고 볼 수 있다.</p>
<pre><code class="language-kotlin">val pi: Double = 3.141592

pi = 3.1555 // error</code></pre>
<p>가변(mutable) 타입 변수를 선언할때는 var를 사용해준다.</p>
<pre><code class="language-kotlin">var score = 10
score += 1
println(score) // 결과 : 11</code></pre>
<p>코틀린에서는 코드의 가변성이 높을수록 오류의 확률 또한 비례한다고 한다. 또한 같은 코드를 병렬로 실행하기도 어려워서 함수형 프로그래밍 기법에서는 거의 금기(taboo)라고 한다. 따라서 가능하면 val을 사용하도록 권장하고 있다. </p>
<p>하지만 val은 값에 의한 불변성만을 보장한다. 만약 변수가 객체에 대한 참조값만 저장하고 있다면 객체의 내부값은 변경이 가능하다. 다음 예시를 확인해보자</p>
<pre><code class="language-kotlin">val message = StringBuilder(&quot;hello &quot;)
message.append(&quot;there&quot;)

println(message) // 결과 : hello there</code></pre>
<h3 id="improved-equality-check">Improved Equality Check</h3>
<p>코틀린에서 동일성 확인(equality check)는 다음과 같이 사용해준다</p>
<p>1) 값에 대한 equality check</p>
<pre><code class="language-kotlin">val num1 = 18
val num2 = 18

println(num1 == num2) // true</code></pre>
<p>2) 참조(reference)에 대한 equality check. 같은 객체(같은 주소를 참조하는지)인지 비교할때</p>
<pre><code class="language-kotlin">val num1 = 18
val num2 = 18

println(num1 === num2) // 같은 kotlin.Int = 18을 참조하기 때문에 True</code></pre>
<p>코틀린에서 == 연산을 사용해 값을 비교할때, 코틀린은 null check를 먼저 수행하고 equals() 메소드를 수행한다. 따라서 어느 한쪽의 값이 null이면 자동적으로 NullPointerException을 핸들링 해준다. 만약 둘중 하나가 null이면, 결과는 false이다. </p>
<pre><code class="language-kotlin">println(&quot;hi&quot; == &quot;hi&quot;) // true
println(&quot;hi&quot; == &quot;Hi&quot;) // false
println(null == &quot;hi&quot;) // false
println(&quot;hi&quot; == null) // false
println(null == null) // true</code></pre>
<h3 id="string-templates">String templates</h3>
<p>코틀린에서 문자열 안에서 외부에 있는 변수를 가져올 때 $를 사용해준다. 외부 변수에 대한 연산이 필요할때는 ${}를 사용해준다</p>
<pre><code class="language-kotlin">val name = &quot;sungjun&quot;
var age = 27

println(&quot;Hello my name is $name&quot;) // Hello my name is sungjun
println(&quot;My Age is ${age + 1}&quot;) // My age is 28</code></pre>
<p>코틀린의 string template은 문자열이 생성된 시점에 초기화된다 따라서 다음과 같은 코드는 컴파일상의 오류는 없지만 문맥상 문제를 일으킬 수 있다. </p>
<pre><code class="language-kotlin">var factor = 2

fun doubleIt(n: Int) = n * factor
var message = &quot;The factor is $factor&quot; // The factor is 2로 초기화

factor = 0                  

println(doubleIt(2)) // 0
println(message) // The factor is 2</code></pre>
<h3 id="raw-strings">Raw Strings</h3>
<p>코틀린에서 raw string은 new line을 포함하는 임의 텍스트이다, string template도 가능하다. &quot;&quot;&quot;을 사용해준다</p>
<pre><code class="language-kotlin">val name = &quot;Eve&quot;

val memo = &quot;&quot;&quot;Dear $name, a quick reminder about the
party we have scheduled next Tuesday at
the &#39;Low Ceremony Cafe&#39; at Noon. | Please plan to...&quot;&quot;&quot;

println(memo)

// 결과
Dear Eve, a quick reminder about the
party we have scheduled next Tuesday at
the &#39;Low Ceremony Cafe&#39; at Noon. | Please plan to...</code></pre>
<p>만약 텍스트가 if문 안에 위치해 들여쓰기 문제가 생긴다면 다음과 같이 처리해주면 된다.</p>
<pre><code class="language-kotlin">fun createMemoFor(name: String): String { 
    if (name == &quot;Eve&quot;) {
        val memo = &quot;&quot;&quot;Dear $name, a quick reminder about the
            |party we have scheduled next Tuesday at
            |the &#39;Low Ceremony Cafe&#39; at Noon. | Please plan to...&quot;&quot;&quot;
        return memo.trimMargin() 
        }
        return &quot;&quot; 
    }
println(createMemoFor(&quot;Eve&quot;))

//결과
Dear Eve, a quick reminder about the
party we have scheduled next Tuesday at
the &#39;Low Ceremony Cafe&#39; at Noon. | Please plan to...</code></pre>
<p>문장의 시작에 |를 사용하면 된다. 이후 trimMargin() 메소드를 사용해주면 깔끔하게 들여쓰기 공백이 사라진다.</p>
<h3 id="more-expressions-fewer-statements">More Expressions, Fewer Statements</h3>
<p>우선 Expression(표현식)과 Statement(선언식)의 차이점 부터 알아보자.</p>
<p>Expression은 수식이라는 뜻으로 하나 이상의 값을 만들어내는 코드를 말한다. 예를들어 사칙연산이 있다 (1+1, 2+2, &quot;hello&quot; 등)</p>
<p>프로그래밍에서 Statement(선언식)이란 실행가능(executable)한 최소의 독립적인 코드 조각을 일컫는다. 흔히 한개 이상의 expression과 프로그래밍 키워드를 포함하는 경우가 많다. (if, switch, for, while, age = 7, name = &quot;sungjun&quot; 등)</p>
<p>다음 예시를 보자</p>
<pre><code class="language-kotlin">fun canVote(name: String, age: Int): String { 
    var status: String
    if (age &gt; 17) {
        status = &quot;yes, please vote&quot;
    } else {
        status = &quot;nope, please come back&quot;
        }
    return &quot;$name, $status&quot; 
}
println(canVote(&quot;Eve&quot;, 12))</code></pre>
<p>canVote() 메소드는 if 선언식을 사용한다. if문 안을 보면 status라는 변수에 대한 값만 변경할 뿐이지 무언가를 return 하지 않는다. 이 if 선언식을 사용한 함수 안에서 유의미한 값을 얻어낼려면 status라는 변수를 선언한 후 조건 처리를 통해 값을 할당해 마지막에 선언식 밖에서 return 해줘야 한다. </p>
<p>반면에 코틀린에서 if는 표현식으로 처리할 수 있다.</p>
<pre><code class="language-kotlin">val status = if (age &gt; 17) &quot;yes, please vote&quot; else &quot;nope, please come back&quot; 

return &quot;$name, $status&quot;</code></pre>
<p>아까와는 다르게 status 변수를 val로 선언할 수 있다. 왜냐하면 if문을 표현식으로 선언함으로써 실행이후 값에 대한 변화가 없기 때문이다. 또한 status 변수에 대한 타입 추론(type inference)도 사용해주고 있다. 코드가 훨씬 더 간결해졌다.</p>
<p>코틀린에서는 try-catch 또한 표현식으로 선언할 수 있다. </p>
<pre><code class="language-kotlin">fun tryExpr(blowup: Boolean): Int { 
    return try {
        if (blowup) {
            throw RuntimeException(&quot;fail&quot;)
        }
        2
    } catch(ex: Exception) {
        4
    } finally {
        //...
    } 
}
println(tryExpr(false)) //2 
println(tryExpr(true)) //4</code></pre>
]]></description>
        </item>
        <item>
            <title><![CDATA[educative - Kotlin - 1]]></title>
            <link>https://velog.io/@sungjun-jin/educative-Kotlin-1</link>
            <guid>https://velog.io/@sungjun-jin/educative-Kotlin-1</guid>
            <pubDate>Sun, 28 Feb 2021 08:38:09 GMT</pubDate>
            <description><![CDATA[<h2 id="kotlin">Kotlin</h2>
<p>코틀린은 서버사이드, 안드로이드 앱, 프론트엔드 개발에 전부 다 사용할 수 있는 몇 안되는 풀스택 개발언어 중 하나이다. Java bytecode의 컴파일링, Javascript로써의 트랜스파일링 또한 가능하다. </p>
<h3 id="정적-타입statically-typed-vs-동적-타입dynamically-typed-지정-언어란">정적 타입(Statically typed) vs 동적 타입(Dynamically typed) 지정 언어란?</h3>
<p>자바, 코틀린은 정적 타입 지정 언어이다. <strong>정적 타입 언어</strong>는 프로그램의 구성 요소의 타입을 컴파일 시점에 알 수 있다. 따라서 코드를 작성할 때 변수에 들어갈 값의 형태에 따라 직접 변수의 타입을 명시해줘야 한다. 예를들어 다음과 같은 java 코드이다.</p>
<pre><code class="language-java">String myName = &quot;sungjun&quot;
int myAge = 18</code></pre>
<p>이런 정적 타입 코드는 런타임 환경 진입전에 컴파일러가 타입을 검증해줌으로써 프로그램에 대한 안정성을 보장 받을 수 있다. </p>
<p>자바와 살짝 다른점이 있다면 코틀린은 코드의 문맥을 통해 자료형을 유추할 수 있다. 코틀린 컴파일러가 다음과 같이 문맥으로 변수 타입을 자동적으로 유추(type inference)하기 때문에 타입 선언을 생략해도 된다. (JAVA SE 10에서도 컴파일러가 문맥으로 타입을 유추하는 기능이 추가되었다.)  </p>
<pre><code class="language-java">var myAge = 18 // 변수 myAge가 Int 타입이라는 것을 문맥을 통해 유추한다.</code></pre>
<p><strong>동적 타입 언어</strong>란 컴파일 환경이 아닌 런타임 환경에서 자료형이 결정되는 언어를 뜻한다. 유연성이 높고 빠르게 코드를 작성할 수 있는 장점이 있지만 정적 타입 언어에 비해 type error가 발생할 위험이 높아 코드의 안정성이 떨어진다는 단점이 있다.</p>
<pre><code class="language-python"># 파이썬은 대표적인 동적 타입 언어이다. 
name = 10
my_age = &quot;sungjun&quot;</code></pre>
<h3 id="verifying-the-installation">Verifying the installation</h3>
<h4 id="코틀린-설치여부--버전확인">코틀린 설치여부 &amp; 버전확인</h4>
<pre><code class="language-shell">kotlinc-jvm
Welcome to Kotlin version 1.4.21 (JRE 15.0.2+7-27)</code></pre>
<h4 id="코틀린-쉘">코틀린 쉘</h4>
<p>코틀린 REPL (Read Evaluate Print Loop) 커맨드라인 쉘을 실행하여 간단한 코드를 실행해볼 수 있다. </p>
<pre><code class="language-shell">kotlinc-jvm

&gt;&gt;&gt; val myName = &quot;sungjun&quot;
&gt;&gt;&gt; myName
res2: kotlin.String = sungjun
&gt;&gt;&gt; println(myName)
sungjun</code></pre>
<p>간단한 코드확인이 가능하다. <code>:quit</code>를 입력하면 쉘이 종료된다.</p>
<h3 id="compile-to-bytecode-and-run">Compile to Bytecode and Run</h3>
<p>간단하게 Hello World가 출력되는 코틀린 소스파일 작성해보자.</p>
<pre><code class="language-kotlin">fun main() = println(&quot;Hello World&quot;)</code></pre>
<p>다음으로 코틀린 소스파일을 컴파일 해보자. 코틀린 코드를 컴파일 하는 방법은 다음과 같다</p>
<pre><code class="language-shell">kotlinc-jvm &lt;소스파일 or 디렉터리&gt; -d &lt;jar 이름&gt;</code></pre>
<pre><code class="language-shell">kotlinc-jvm hello.kt hello.jar</code></pre>
<h4 id="코틀린-코드-컴파일러">코틀린 코드 컴파일러</h4>
<p>Kotlin in Action에서 가져온 코틀린 코드 컴파일러에 대한 설명이다</p>
<blockquote>
</blockquote>
<p>코틀린 컴파일러는 자바컴파일러가 자바 소스코드를 컴파일할 때와 마찬가지로 코틀린 소스코드를 분석해서 .class 파일을 만들어낸다. 만들어진 .class 파일은 개발 중인 앱 유형에 맞는 표준 패키징 과정을 거쳐 실행될 수 있다.
. . .
코틀린 컴파일러로 컴파일한 코드는 코틀린 런타임 라이브러리에 의존한다. 런타임 라이브러리에는 코틀린 자체 표준 라이브러리 클래스와 코틀린에서 자바 API 의 기능을 확장한 내용들이 들어있다. 코틀린으로 컴파일한 앱을 배포할 때는 런타임 라이브러리도 함께 배포해야 한다.</p>
<p><img src="https://images.velog.io/images/sungjun-jin/post/098ae020-8839-4b4f-9ed5-65540bf83b1c/image.png" alt=""></p>
<p>JAVA와 컴파일 과정은 비슷하지만 다른 컴파일러를 사용하며, 실행 시 코틀린 런타임 라이브러리를 통해 애플리케이션이 실행된다. 좀 더 세부적으로 들어가자면, 과정은 다음과 같다.</p>
<p><img src="https://images.velog.io/images/sungjun-jin/post/e8133644-3744-4e6b-8505-82b472be38f3/image.png" alt=""></p>
<ol>
<li>Java코드를 참조하는 Kotlin 코드를 코틀린 컴파일러가 함께 컴파일해 .class 파일 생성.</li>
<li>Java 컴파일러가 앞서 컴파일링된 Kotlin .class 파일의 경로를 클래스파일에 추가하여 .class 파일을 생성.</li>
</ol>
<h4 id="jar">JAR?</h4>
<p>성공적으로 컴파일 되었다면 현재 디렉터리 내에서 hello.jar라는 JAR file이 생성된것을 확인할 수 있다. JAR(Java Ahchive) 파일이란 여러개의 자바 클래스 파일과, 그 클래스들이 이용하는 리소스와 메타데이터를 하나로 묶어서 자바 플랫폼에 배포하기 위한 소프트웨어 패키지 파일 포맷이다. JAR 파일을 살펴보면, 실제로 ZIP 파일 포맷으로 이루어진 압축 파일이다. </p>
<pre><code class="language-shell">vi hello.jar

  1 &quot; zip.vim version v30
  2 &quot; Browsing zipfile /Users/jinsungjun/devel/kotlin_test/hello.jar
  3 &quot; Select a file with cursor and press ENTER
  4
  5 META-INF/MANIFEST.MF
  6 HelloKt.class
  7 META-INF/main.kotlin_module</code></pre>
<p>jar 파일은 다음과 같은 명령어로 실행시킬 수 있다. kolin 옵션을 사용하면 -jar 옵션 없이 실행할 수 있다.</p>
<pre><code class="language-shell">java -jar hello.jar
Hello World

kotlin hello.jar
Hello World</code></pre>
]]></description>
        </item>
        <item>
            <title><![CDATA[데놀랜드 요약]]></title>
            <link>https://velog.io/@sungjun-jin/%EB%8D%B0%EB%86%80%EB%9E%9C%EB%93%9C-%EC%9A%94%EC%95%BD</link>
            <guid>https://velog.io/@sungjun-jin/%EB%8D%B0%EB%86%80%EB%9E%9C%EB%93%9C-%EC%9A%94%EC%95%BD</guid>
            <pubDate>Sat, 17 Oct 2020 07:38:35 GMT</pubDate>
            <description><![CDATA[<h2 id="당근마켓">당근마켓</h2>
<p>Data Democratization(데이터 민주화) : 높은 접근성</p>
<p>ELT (Extract-Load-Transform)</p>
<p>Process
데이터 추출 -&gt; 가공 -&gt; 웨어하우스 저장</p>
<p>문제점 : 저장하기전에 데이터 가공과정에서 문제가 발생함. 어떤 형태로 데이터를 가공할 것인지에 대한 기술적, 커뮤니케이션 공수 발생</p>
<p>해결 : 선 데이터 저장 -&gt; 꺼낼 때 가공</p>
<h4 id="lambda-architecture">Lambda Architecture</h4>
<p>배치 프로세스를 이용해서 더 작은 단위로 데이터를 쪼개  처리
실시간 데이터를 처리하는 파이프라인</p>
<p>히스토리컬 데이터부터 실시간 데이터까지 빠르게 처리 가능</p>
<h4 id="google-bigquery">Google Bigquery</h4>
<p>당마 메인 데이터 웨어하우스
장점 : 퀄리티 있는 웹 UI 제공, 저렴한 비용(실제로 스캔된 데이터에 대해서만 비용 발생)과 확장성
BigQuery Omni 
AWS S3 Azure blog storage 클라우드에 적용 가능</p>
<p>단계적 티어(Tier) 테이블
raw(원본) -&gt; analytic(가공) -&gt; objective(최종 사용자)</p>
<h2 id="open-source-contribution">Open Source Contribution</h2>
<p>분석적인 유저의 관점으로 프로젝트 분석, 다양한 use case를 남겨보기, tester로써의 안목 기르기
광범위한 프로젝트 활용</p>
<p>code commit 만이 기여가 아니다 문서화(Documentation), 번역, 오타수정 또한 contribution이 될 수 있다.</p>
<p>git 대한 높은 이해도 또한 필수적</p>
<p>github의 이슈들 중에서 내가 해결할 수 있는 issue를 판단해 찾는다. (issue labeling 참고)
ex) Good First issue</p>
<p>프로젝트 전체를 압도하는 실력이 아니라 issue를 해결한 실력이면 충분하다</p>
<h2 id="kubernetes">Kubernetes</h2>
<p>Amazon Athena를 활용한 문제</p>
<p>Apache Spark</p>
<h3 id="kubernetes-1">Kubernetes</h3>
<p>Container Orchestration 툴
Pod (컨테이너) 를 포함하여 추상화된 리소스를 선언, 실질적인 배치, 배포는 Kubernetes 자동화</p>
<p>Cluster Autoscaler
가용한 리소스가 부족할 경우 -&gt; 인스턴스를 자동으로 늘리고 줄여주는 작업을 자동화</p>
<p>Superset
Web 기반 오픈소스 BI 프레임워크</p>
<p>Spark Thrift Server + Superset</p>
<p>Spark Thrift Server 기반 데이터 서비스
유저 push message 자동화</p>
<p>UserDB, DW(S3)</p>
<h2 id="의료-데이터">의료 데이터</h2>
<p>one-hot 인코딩</p>
<p>특정 값만 1
[1,0,0,0,0,0]</p>
<p>임베딩을 직접 만드는 AI</p>
<h4 id="skip-grams-자연어-처리--입력된-단어를-기준으로-그-주변의-단어를-예측">Skip-Grams (자연어 처리) : 입력된 단어를 기준으로 그 주변의 단어를 예측</h4>
<p>Word 2 Vec 생성을 위한 모델</p>
<p>Linewalks is hiring great machine learning engineers</p>
<p>window size = 단어 주변의 거리</p>
<p>ex) input = machine, window size = 2
hiring, great learning, engineers</p>
<h4 id="matrix-factorization-행렬-인수분해">Matrix Factorization (행렬 인수분해)</h4>
<p>하나의 행렬을 특정한 구조를 가진 다른 행렬의 구조로 나타내는 수학적 방법</p>
<h2 id="elastic">Elastic</h2>
<p>검색 기능의 종류</p>
<ol>
<li><p>풀텍스트 (Full Text)
텍스트 검색에 사용, 입력된 검색을 포함하는 결과를 연관도가 높은 순서대로 스코어를 매김
ex) 다룰줄 아는 개발 언어, 도구, OS
Elastic Search Query
match</p>
</li>
<li><p>정확값 (Exact Value)
불리언 여부만 따짐, 정확하게 일치하는 결과만 가져옴
ex) 고용 조건 (22세 ~ 55세) 
22 &lt;= age &lt;= 55</p>
</li>
</ol>
<p>Elastic Search Query
term
range
geo_bounding_box
geo_distance</p>
<p>total score = tf + idf</p>
<p>precision : 검색 결과에 나타난 것 중 실제로 True인 것
Recall : 실제 true인 것에서 검색엔진이 true라고 판단한 것</p>
<h2 id="airflow">Airflow</h2>
<p>Bigquery : 쿼리 예약을 통한 자동화 모듈, 쿼리 실패시 메일 발송</p>
<p>Airflow : Airbnb에서 개발 설정한 시간에 특정 작업을 진행할 수 있도록 하는 라이브러리
스케쥴링, python, DAG구성 </p>
<p>DAG: Airflow의 하나의 workflow, Python 스크립트로 정의</p>
<p>GCP Cloud Composer와 함께 사용</p>
<p>수동 DAG도 지원</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[Redis]]></title>
            <link>https://velog.io/@sungjun-jin/Redis</link>
            <guid>https://velog.io/@sungjun-jin/Redis</guid>
            <pubDate>Sun, 30 Aug 2020 14:05:09 GMT</pubDate>
            <description><![CDATA[<p><img src="https://images.velog.io/images/sungjun-jin/post/11369da6-0a78-4204-bcc1-958ffd0324d7/image.png" alt=""></p>
<h2 id="redis란">Redis란?</h2>
<blockquote>
<p>REDIS(REmote DIctionary Server)의 약자로 인 메모리 기반의 key-value 구조 데이터 관리 시스템이다. 모든 데이터를 메모리에 저장하므로 빠른 Read &amp; Write 속도를 보장하는 NoSQL 데이터베이스이다.</p>
</blockquote>
<p>요즘 회사들은 자체 RBDMS의 부하를 줄이기 위해 redis 클러스터를 주로 사용해 운영한다.  </p>
<h3 id="redis-data-type">Redis Data Type</h3>
<p>공식 문서에 의하면 redis는 다음과 같은 데이터타입을 지원한다</p>
<ul>
<li>String</li>
<li>List</li>
<li>Sorted sets</li>
<li>Bitmaps &amp; HyperLogs</li>
<li>Hashes</li>
</ul>
<p><img src="https://images.velog.io/images/sungjun-jin/post/d8ee4599-a447-4afb-88fb-fff6a78b2412/image.png" alt=""></p>
<h3 id="persistence">Persistence</h3>
<p>redis에서는 데이터를 저장하는 방식이 2가지가 있다</p>
<ul>
<li><p><strong>스냅샷</strong> : 순간적으로 메모리에 있는 내용을 DISK 전체에 옮겨 담는 방식이다. 순간적으로 모든 redis의 동작을 정지시키고 그때의 스냅샷을 저장하는 <code>SAVE</code> 방식과 별도의 process를 띄운 후 정지동작 없이 스냅샷을 저장시키는 <code>BIGSAVE</code> 방식이 있다.</p>
</li>
<li><p><strong>AOF (Append On File)</strong> : redis의 모든 write/update 연산 자체를 모두 로그 파일에 기록해두는 방식이다. redis 서버가 재시작될때 기록된 모든 연산을 순차적으로 실행하여 데이터를 복구한다. 스냅샷 방식과는 달리 특정 시점이 아니라 항상 현재 시점까지의 로그를 기록할수 있다는 장점이 있다.</p>
</li>
</ul>
<p>일반적으로 위 두가지 방식을 혼용해서 사용하는 것이 바람직하다고 한다. </p>
<h3 id="간단한-예제">간단한 예제</h3>
<h4 id="1-redis-설치">1. redis 설치</h4>
<pre><code class="language-shell">$ brew install redis</code></pre>
<h4 id="2-redis-데몬으로-서버-구동">2. redis 데몬으로 서버 구동</h4>
<pre><code>$ redis-server --daemonize yes</code></pre><h4 id="3-redis-cli-서버-상태-확인">3. redis-cli 서버 상태 확인</h4>
<pre><code class="language-shell">$ redis-cli ping                                                       
PONG</code></pre>
<p>간단한 파이썬 코드를 사용해 redis queue를 구현해보자. redis queue에는 데이터를 write하는 <code>enqueue()</code> 메소드와 데이터를 read하는 <code>dequeue()</code> 메소드가 있다.</p>
<p><code>redis.py</code></p>
<pre><code class="language-python">class RedisQueue():
    def __init__(self):
        self.redis = redis.StrictRedis(
            host=&quot;localhost&quot;,
            port=6379
        )

        logger.info(&quot;redis_initialization&quot;)

    def enqueue(self, key, value):
        self.redis.rpush(key, value)

    def dequeue(self, key):
        return self.redis.lpop(key)
</code></pre>
<p>redis 커넥션을 잡을 때 포트 번호는 전용 포트인 6379를 잡아준다. </p>
<p>간단하게 key, value 페어로 redis에 데이터를 넣어주는 코드를 짜봤다.</p>
<p><code>view.py</code></p>
<pre><code class="language-python">import logging
import json

from rest_framework.decorators import api_view
from rest_framework.response import Response
from rest_framework.status import HTTP_200_OK
from .redis import RedisQueue

logger = logging.getLogger(__name__)


@api_view([&#39;POST&#39;])
def post_data(request):
    try:
        # args (key, value)
        data = json.loads(request.body)
        redis = RedisQueue()
        redis.enqueue(data[&#39;key&#39;], data[&#39;value&#39;])
        return Response(status=HTTP_200_OK)

    except Exception as e:
        return Response({&quot;message&quot;: str(e)}, status=400)
</code></pre>
<p><img src="https://images.velog.io/images/sungjun-jin/post/f71b27a0-72c3-4b33-8f3d-b497a562db99/image.png" alt=""></p>
<p>그리고 실제로 API를 호출해 데이터를 날려보면</p>
<p><img src="https://images.velog.io/images/sungjun-jin/post/6eec025d-eb31-4e77-acae-8c9d1730a909/image.png" alt=""></p>
<p>이런식으로 redis DB에 저장된다. </p>
]]></description>
        </item>
    </channel>
</rss>