diff --git "a/Chapter01_\354\275\224\355\213\200\353\246\260 \352\270\260\353\263\270 \354\235\265\355\236\210\352\270\260/3_\355\225\250\354\210\230\354\231\200 \355\225\250\354\210\230\355\230\225 \355\224\204\353\241\234\352\267\270\353\236\230\353\260\215/README.md" "b/Chapter01_\354\275\224\355\213\200\353\246\260 \352\270\260\353\263\270 \354\235\265\355\236\210\352\270\260/3_\355\225\250\354\210\230\354\231\200 \355\225\250\354\210\230\355\230\225 \355\224\204\353\241\234\352\267\270\353\236\230\353\260\215/README.md" index 53a5cc4..44b689d 100644 --- "a/Chapter01_\354\275\224\355\213\200\353\246\260 \352\270\260\353\263\270 \354\235\265\355\236\210\352\270\260/3_\355\225\250\354\210\230\354\231\200 \355\225\250\354\210\230\355\230\225 \355\224\204\353\241\234\352\267\270\353\236\230\353\260\215/README.md" +++ "b/Chapter01_\354\275\224\355\213\200\353\246\260 \352\270\260\353\263\270 \354\235\265\355\236\210\352\270\260/3_\355\225\250\354\210\230\354\231\200 \355\225\250\354\210\230\355\230\225 \355\224\204\353\241\234\352\267\270\353\236\230\353\260\215/README.md" @@ -323,17 +323,13 @@ fun funParam(a:Int, b: Int, c:(Int, Int) -> Int) : Int{ } ``` -

- -# 4. 고차 함수와 람다식 함수의 사례들 -Need More Time



-# 5. 코틀린의 다양한 함수들 +# 4. 코틀린의 다양한 함수들 코틀린에는 일반 함수, 고차 함수, 람다식 함수 이외에도 다양한 형태의 함수가 존재한다. 예로 익명 함수, 인라인 함수, 확장 함수, 중위 함수 등이 그 예이다. 이를 알아보자. -### 5-1. 익명함수 +### 4-1. 익명함수 익명함수(Anonymous Function)란 일반 함수이지만 이름이 없는 함수를 말한다. 물론 람다식 함수도 이름 없이 구성은 가능하나, 익명 함수는 일반 함수의 이름을 생략하고 사용하는 것을 뜻한다. 기능적으로 동일하고 사용법이 유사함에도 익명함수가 필요한 이유는, 람다식에서는 return이나 break,continue처럼 제어문을 사용하기 어렵기 때문이다. 따라서 함수 본문 조건식에 따라 함수를 중단하고 반환해야 하는 경우에는 익명 함수를 활용한다. @@ -351,13 +347,13 @@ val add = {x:Int, y:Int -> x+y} // 동일한 기능하는 람다식 ```
-### 5-2. 인라인 함수 +### 4-2. 인라인 함수
인라인 함수(Inline Function)란 함수가 호출되는 곳에 함수 본문의 내용을 모두 복사해 넣어 함수의 분기 없이 처리되기 때문에 코드의 성능을 높이는 함수이다. 기존 함수의 경우 호출되었을 때 다른 코드로 분기해야 하기 때문에 내부적으로 기존 내용을 저장했다가 다시 돌아올 때 복구하는 작업에 CPU와 메모리 비용을 꽤 들이나, 인라인 함수는 그렇지 않기에 비용을 줄일 수 있다. 허나, 인라인 함수는 코드가 복사되어 들어가기에 내용은 대게 짧게 작성합니다. -### 5-3. 확장 함수 -### 5-4. 중위 함수 -### 5-5. 꼬리 재귀 함수 +### 4-3. 확장 함수 +### 4-4. 중위 함수 +### 4-5. 꼬리 재귀 함수

diff --git "a/Chapter02_\352\260\235\354\262\264 \354\247\200\355\226\245 \355\224\204\353\241\234\352\267\270\353\236\230\353\260\215/05 \355\201\264\353\236\230\354\212\244\354\231\200 \352\260\235\354\262\264/05-1 \355\201\264\353\236\230\354\212\244\354\231\200 \352\260\235\354\262\264\354\235\230 \354\240\225\354\235\230/readme.md" "b/Chapter02_\352\260\235\354\262\264 \354\247\200\355\226\245 \355\224\204\353\241\234\352\267\270\353\236\230\353\260\215/05 \355\201\264\353\236\230\354\212\244\354\231\200 \352\260\235\354\262\264/05-1 \355\201\264\353\236\230\354\212\244\354\231\200 \352\260\235\354\262\264\354\235\230 \354\240\225\354\235\230/readme.md" index c2dc090..725c83d 100644 --- "a/Chapter02_\352\260\235\354\262\264 \354\247\200\355\226\245 \355\224\204\353\241\234\352\267\270\353\236\230\353\260\215/05 \355\201\264\353\236\230\354\212\244\354\231\200 \352\260\235\354\262\264/05-1 \355\201\264\353\236\230\354\212\244\354\231\200 \352\260\235\354\262\264\354\235\230 \354\240\225\354\235\230/readme.md" +++ "b/Chapter02_\352\260\235\354\262\264 \354\247\200\355\226\245 \355\224\204\353\241\234\352\267\270\353\236\230\353\260\215/05 \355\201\264\353\236\230\354\212\244\354\231\200 \352\260\235\354\262\264/05-1 \355\201\264\353\236\230\354\212\244\354\231\200 \352\260\235\354\262\264\354\235\230 \354\240\225\354\235\230/readme.md" @@ -3,25 +3,30 @@ - 객체 지향 프로그래밍 - 정의 : 프로그램의 구조를 개체 간 상호작용으로서 표현하는 프로그래밍 방식 - 코틀린은 **객체 지향 프로그래밍**과 **함수형 프로그래밍**기법을 지원 + * 함수형 프로그래밍 : 말 그대로 자료 처리를 수학적 함수의 계산으로 취급하고 상태 혹은 가변 데이터를 멀리하는 프로그래밍 기법 - 개념 - 추상화 : 특정 클래스를 만들 때 기본 형식을 규정하는 방법 - 인스턴스 : 클래스로부터 생성한 객체 + * 객체와 인스턴스의 차이 : 클래스를 '생성'만 하면 실제 메모리에 존재하여 실행되고 있는 것은 아니지만, 클래스로부터 객체를 생성(인스턴스화)하면 메모리 영역에서 실행 가능 - 상속 : 부모클래스의 내용을 자식 클래스가 그대로 물려 받음 (클래스 다이어그램에서 상속은 화살표로 표현) - 다형성 : 하나의 이름으로 다양한 처리를 제공 - 캡슐화 : 내용을 숨기고 필요한 부분만 사용 - 메시지 전송 : 객체 간에 주고 받는 메시지 - 연관 클래스 간의 관계 - - - 용어 정리 + + - 객체지향 프로그래밍의 용어 정리 | **코틀린** | **타 언어** | | ---- | ---- | | 클래스(Class) | 분류, 범주 | - | 프로퍼티(Property) | 속성, 변수, 필드, 데이터 | + | **프로퍼티(Property)** | 속성, 변수, 필드, 데이터 | | 메소드(Method) | 함수, 동작, 행동 | | 객체(Object) | 인스턴스 | - - 클래스 추상화 : 프로그램 동작에 있어서 필요한 만큼 속성과 동작을 정의하는 과정 + - 클래스 다이어그램 + 화면 캡처 2022-09-18 192944 + + - 클래스 추상화 : 프로그램 동작에 있어서 필요한 만큼 속성과 동작(메소드)을 정의하는 과정 - 클래스 선언 ```kotlin class Bird { } //빈 클래스 diff --git "a/Chapter02_\352\260\235\354\262\264 \354\247\200\355\226\245 \355\224\204\353\241\234\352\267\270\353\236\230\353\260\215/05 \355\201\264\353\236\230\354\212\244\354\231\200 \352\260\235\354\262\264/05-2 \354\203\235\354\204\261\354\236\220/readme.md" "b/Chapter02_\352\260\235\354\262\264 \354\247\200\355\226\245 \355\224\204\353\241\234\352\267\270\353\236\230\353\260\215/05 \355\201\264\353\236\230\354\212\244\354\231\200 \352\260\235\354\262\264/05-2 \354\203\235\354\204\261\354\236\220/readme.md" index 68a668c..2165a66 100644 --- "a/Chapter02_\352\260\235\354\262\264 \354\247\200\355\226\245 \355\224\204\353\241\234\352\267\270\353\236\230\353\260\215/05 \355\201\264\353\236\230\354\212\244\354\231\200 \352\260\235\354\262\264/05-2 \354\203\235\354\204\261\354\236\220/readme.md" +++ "b/Chapter02_\352\260\235\354\262\264 \354\247\200\355\226\245 \355\224\204\353\241\234\352\267\270\353\236\230\353\260\215/05 \355\201\264\353\236\230\354\212\244\354\231\200 \352\260\235\354\262\264/05-2 \354\203\235\354\204\261\354\236\220/readme.md" @@ -1,6 +1,7 @@ # 05-2 생성자 - 생성자는 주생성자와 부생성자로 나뉨 +- 주 생성자 -> 클래스를 **초기화** 하기 위함 - 부 생성자 -> 매개변수를 다르게 여러번 정의 가능 - kotlin에서 부생성자는 **constructor 키워드를 사용** ```kotlin @@ -41,7 +42,7 @@ } ``` -- 주 생성자 : 클래스 이름과 블록 시작 부분 사이에 선언 +- 주 생성자 : 클래스 이름과 블록 시작 부분 사이에 선언(클래스 이름 바로 뒤에 오는 () 부분) ```kotlin // 주생성자는 contructor 키워드 생략 가능(생략해도 사용법은 동일) class Bird constructor(_name: String, _wing: Int, _beak: String, _color: String) { @@ -65,8 +66,8 @@ - 초기화 블록 사용 : 코드가 간결해짐, 반드시 클래스 선언부에 넣어주어야 함 - 생성자 매개변수에 기본값을 사용하면 객체를 생성할 때 기본값이 있는 인자는 생략 가능 - ```kotlin - class Bird constructor(var name: String = "PAEHA", var wing: Int = 2, var beak: String, var color: String) { + ```kotlin + class Bird constructor(var name: String = "PAEHA", var wing: Int = 2, var beak: String, var color: String) { init { println("초기화 블록입니다") this.sing(3) @@ -81,4 +82,4 @@ println("coco.color: ${coco.color}"} // coco.color: yellow } - ``` + ``` diff --git "a/Chapter02_\352\260\235\354\262\264 \354\247\200\355\226\245 \355\224\204\353\241\234\352\267\270\353\236\230\353\260\215/05 \355\201\264\353\236\230\354\212\244\354\231\200 \352\260\235\354\262\264/05-3 \354\203\201\354\206\215\352\263\274 \353\213\244\355\230\225\354\204\261/readme.md" "b/Chapter02_\352\260\235\354\262\264 \354\247\200\355\226\245 \355\224\204\353\241\234\352\267\270\353\236\230\353\260\215/05 \355\201\264\353\236\230\354\212\244\354\231\200 \352\260\235\354\262\264/05-3 \354\203\201\354\206\215\352\263\274 \353\213\244\355\230\225\354\204\261/readme.md" index 7c7804a..739ed69 100644 --- "a/Chapter02_\352\260\235\354\262\264 \354\247\200\355\226\245 \355\224\204\353\241\234\352\267\270\353\236\230\353\260\215/05 \355\201\264\353\236\230\354\212\244\354\231\200 \352\260\235\354\262\264/05-3 \354\203\201\354\206\215\352\263\274 \353\213\244\355\230\225\354\204\261/readme.md" +++ "b/Chapter02_\352\260\235\354\262\264 \354\247\200\355\226\245 \355\224\204\353\241\234\352\267\270\353\236\230\353\260\215/05 \355\201\264\353\236\230\354\212\244\354\231\200 \352\260\235\354\262\264/05-3 \354\203\201\354\206\215\352\263\274 \353\213\244\355\230\225\354\204\261/readme.md" @@ -2,6 +2,7 @@ - 상속 : 상위 클래스의 속성과 기능을 자식 클래스가 물려받아 사용 - 상속을 통해 하위클래스는 **상위 클래스의 내용을 다시 만들지 않아도 됨** +- 상속은 UML 다이어그램에서 화살표로 표현 - 부모 클래스에서 *open* 키워드를 사용해야 하위클래스에서 상속받을 수 있음 ```kotlin val someVal: Int diff --git "a/Chapter02_\352\260\235\354\262\264 \354\247\200\355\226\245 \355\224\204\353\241\234\352\267\270\353\236\230\353\260\215/05 \355\201\264\353\236\230\354\212\244\354\231\200 \352\260\235\354\262\264/05-4 super\354\231\200 this\354\235\230 \354\260\270\354\241\260/readme.md" "b/Chapter02_\352\260\235\354\262\264 \354\247\200\355\226\245 \355\224\204\353\241\234\352\267\270\353\236\230\353\260\215/05 \355\201\264\353\236\230\354\212\244\354\231\200 \352\260\235\354\262\264/05-4 super\354\231\200 this\354\235\230 \354\260\270\354\241\260/readme.md" new file mode 100644 index 0000000..46f9d26 --- /dev/null +++ "b/Chapter02_\352\260\235\354\262\264 \354\247\200\355\226\245 \355\224\204\353\241\234\352\267\270\353\236\230\353\260\215/05 \355\201\264\353\236\230\354\212\244\354\231\200 \352\260\235\354\262\264/05-4 super\354\231\200 this\354\235\230 \354\260\270\354\241\260/readme.md" @@ -0,0 +1,114 @@ +# 05-4 super와 this의 참조 + +- super + - 상위 클래스를 가리키는 특별한 키워드로, 상위 클래스의 프로퍼티, 메소드, 생성자 등을 사용할 수 있다 + ```kotlin + open class Bird(var name: String, var wing: Int, var beak: String, var color: String) { + fun fly() = println("Fly wing: $wing") + open fun sing(vol: Int) = println("Sing vol: $vol") + } + + class Parrot(name: String, wing: Int=2, beak: String, color: String, var language: String = "natural") : Bird(name, wing, beak, color) { + override fun sing(vol: Int) { + super.sing(vol) //상위 클래스의 sing()을 수행 + } + } + ``` + + +- this + - 현재 객체를 참조하는 키워드 + ```kotlin + open class Person { + constructor(firstNAme: String) { + println("[Person] firstName: $firstNAme") + } + constructor(firstName: String, age: Int) { + println("[Person] firstName: $firstName, $age") + } + } + + class Developer: Person { + constructor(firstName: String): this(firstName, 10) { //인자가 2개인 아래 부생성자를 호출 + println("[Devloper] $firstNAme") + } + constructor(firstName: String, age: Int): super(firstName, age) { //인자가 2개인 부모 클래스의 생성자를 호출 + println("[Developer] $firstName, $age") + } + } + + fun main() { + val sean = Developer("Sean") + } + ``` + + +- inner class(바깥 클래스 호출하기) + - 클래스 안에 선언된 클래스 + - inner class에서 바깥 클래스의 상위 클래스를 호출하려면 super 키워드와 함께 @기호 옆에 바깥 클래스 이름을 사용해야함 + ```kotlin + open class Parents { + open val x: Int = 1 + open fun f() = println("Parents Class f()") + } + + class Child : Parents() { + override val x: Int = super.x + 1 + override fun f() = println("Child Class f()") + + inner class Inside { + fun f() = println("Inside Class f()") + fun test() { + f() + child().f() + super@Child.f() + println("[Inside] x: ${super@Child.x}") + } + } + } + fun main() { + val c1 = Child() + // Child 클래스 안에 있는 Inside클래스 이므로 + c1.Inside().test() // Inside Class f() + // Child Class f() + // Parents Class f() + // [Inside] x: 1 + } + ``` + + +- 인터페이스(인터페이스에서 참조하기) + - 코틀린은 자바와 달리 한 번에 2개 이상의 클래스를 상속 받는 다중 상속이 불가능 + - 하지만 인터페이스는 다수의 인터페이스를 지정해 구현할 수 있음 + - 상속 받는 클래스 혹은 인터페이스의 프로퍼티나 메소드 이름이 중복되면 앵클 브래킷( <> )을 활용하여 접근할 수 있음 + ```kotlin + open class A { + open fun f() = println("A class f()") + fun a() = println("A Class a()") + } + + interface B { + fun f() = println("B interface f()") + fun b() = println("B interface b()") + } + + class C : A(), B { + override fun f() = println("C Class f()") + + fun test() { + f() + b() + super.f() + super.f() + } + } + + fun main() { + val.c = C() + c.test() // C Class f() + // B Interface b() + // A Class f() + // B Interface f() + } + ``` + diff --git "a/Chapter02_\352\260\235\354\262\264 \354\247\200\355\226\245 \355\224\204\353\241\234\352\267\270\353\236\230\353\260\215/05 \355\201\264\353\236\230\354\212\244\354\231\200 \352\260\235\354\262\264/05-5 \354\240\225\353\263\264 \354\235\200\353\213\211 \354\272\241\354\212\220\355\231\224/readme.md" "b/Chapter02_\352\260\235\354\262\264 \354\247\200\355\226\245 \355\224\204\353\241\234\352\267\270\353\236\230\353\260\215/05 \355\201\264\353\236\230\354\212\244\354\231\200 \352\260\235\354\262\264/05-5 \354\240\225\353\263\264 \354\235\200\353\213\211 \354\272\241\354\212\220\355\231\224/readme.md" new file mode 100644 index 0000000..c7a9f58 --- /dev/null +++ "b/Chapter02_\352\260\235\354\262\264 \354\247\200\355\226\245 \355\224\204\353\241\234\352\267\270\353\236\230\353\260\215/05 \355\201\264\353\236\230\354\212\244\354\231\200 \352\260\235\354\262\264/05-5 \354\240\225\353\263\264 \354\235\200\353\213\211 \354\272\241\354\212\220\355\231\224/readme.md" @@ -0,0 +1,28 @@ +# 05-5 정보 은닉 캠슐화 + +- 캡슐화 : 클래스를 작성할 때 숨겨야 하는 속성이나 기능을 숨겨서 구현(예 : 자동차의 핸들과 조향장치) +- 가시성 지시자 : 접근 범위를 지시자를 통해 명시해줌 + | 지정자 | 범위 | + | --- | --- | + | private | 해당 클래스 내부 | + | protected | 해당 클래스 + 서브 클래스 내부 | + | internal | 같은 모듈 내부 | + | public | 어디서든지 가능, 기본 지정자로서 생략 시 자동으로 지정 됨 | + +- iternal + - 프로젝트 단위의 모듈을 가리치켜 모듈이 달라지면 접근 제한 + - java에서는 package의 이름이 같은 경우 접근을 허용했다면, kotlin에서는 패키지에 제한하지 않고 하나의 모듈 단위로 제한 가능 + - kotlin에서는 package지시자를 사용하지 않음(package가 없는 게 아님!) + - 자바에서 페키지 단위로 관리했던 가시성의 경우 보안 문제가 발생할 수 있는데 이를 보완하기 위해 모듈 개념을 도입 + - [코틀린 프로젝트 범위 설명 링크](https://acaroom.net/ko/blog/youngdeok/%EC%97%B0%EC%9E%AC-%EC%BD%94%ED%8B%80%EB%A6%B0-%ED%94%84%EB%A1%9C%EA%B7%B8%EB%9E%98%EB%B0%8D-%EC%BD%94%ED%8B%80%EB%A6%B0-%ED%8C%A8%ED%82%A4%EC%A7%80) + +- 가시성 지시자와 클래스의 관계 + - 가시성 지시자를 활용하여 상속된 하위 클래스에서 접근 범위를 지정하거나 다른 클래스와의 연관 관계를 지정할 수 있음 + - UML의 가시성 표기 기호 + - private : - + - public : + + - protected : # + - package : ~ (코틀린의 internal의 표기법이 아직 지정되지 않았음) + + 화면 캡처 2022-09-17 171452 + diff --git "a/Chapter02_\352\260\235\354\262\264 \354\247\200\355\226\245 \355\224\204\353\241\234\352\267\270\353\236\230\353\260\215/05 \355\201\264\353\236\230\354\212\244\354\231\200 \352\260\235\354\262\264/05-6 \355\201\264\353\236\230\354\212\244\354\231\200 \355\201\264\353\236\230\354\212\244\354\235\230 \352\264\200\352\263\204/readme.md" "b/Chapter02_\352\260\235\354\262\264 \354\247\200\355\226\245 \355\224\204\353\241\234\352\267\270\353\236\230\353\260\215/05 \355\201\264\353\236\230\354\212\244\354\231\200 \352\260\235\354\262\264/05-6 \355\201\264\353\236\230\354\212\244\354\231\200 \355\201\264\353\236\230\354\212\244\354\235\230 \352\264\200\352\263\204/readme.md" new file mode 100644 index 0000000..f7ffed4 --- /dev/null +++ "b/Chapter02_\352\260\235\354\262\264 \354\247\200\355\226\245 \355\224\204\353\241\234\352\267\270\353\236\230\353\260\215/05 \355\201\264\353\236\230\354\212\244\354\231\200 \352\260\235\354\262\264/05-6 \355\201\264\353\236\230\354\212\244\354\231\200 \355\201\264\353\236\230\354\212\244\354\235\230 \352\264\200\352\263\204/readme.md" @@ -0,0 +1,77 @@ +# 05-6 클래스와 클래스의 관계 + +- 클래스 혹은 객체 간의 관계는 연관, 의존, 집합, 구성이 있음. +- '두 클래스가 서로를 참조하는지'를 먼저 확인하고, 그 다음 '두 클래스가 생명주기에 영향을 주는지'를 나눔 + 화면 캡처 2022-09-17 172054 + 화면 캡처 2022-09-17 172906 + +- 연관 관계 + - **2개의 서로 분리된 클래스가 연결을 가지는 것** + - 단방향 혹은 양방향으로 연결될 수 있음 + - 두 요소는 서로 다른 생명주기를 갖고 있음 + + ```kotlin + class patient(val name: String){ + fun doctorList(d: Doctor){ + println("Patient : $name, Doctor : ${d.name}") + } + } + + class Doctor(val name: String) { + fun parientList(p: Patient) { + println("Doctor: $name, Patient: ${p.name}") + } + } + ``` + - Doctor와 Patient 클래스는 따로 생성되며 독립적인 생명주기를 가짐 + - **서로의 객체를 참조하고 있지만 생명주기에 영향을 주지 않으면 연관관계라고 함** + +- 의존 관계 + - 한 클래스가 다른 클래스에 의존되어 영향을 주는 경우 + ```kotlin + class patient(val name: String){ + fun doctorList(d: Doctor){ + println("Patient : $name, Doctor : ${d.name}") + } + } + + class Doctor(val name: String, val p: Patient) { //Doctor 클래스를 생성하려면 Patient 클래스가 필요함 -> 서로 영향을 줌 + fun parientList(p: Patient) { + println("Doctor: $name, Patient: ${p.name}") + } + } + ``` + +- 집합 관계 + - 연관 관계와 거의 동일하지만 특정 객체를 **소유** 한다는 개념이 추가됨 + ```kotlin + class Pond(_name: String, _members: MutableList){ + val name: String = _name + val members: MutableList = _members + constructor(_name: String): this(_name, mutableListOf()} + } + + class Duck(val name: String) { + // 두 객체가 서로의 생명주기에 영향을 주지는 않음 + val pond = Pond("myFavor") + val duck1 = Duck("Duck1") + val duck2 = Duck("Duck2") + + pond.members.add(duck1) + pond.members.add(duck2) + } + ``` + +- 구성 관계 + - 집합 관계와 거의 동일하지만 **특정 클래스가 어느 한 클래스의 부분이 되는 개념** + ```kotlin + class Car(val name: String, val power: String) { + private var engine = Engine(power) + + fun startEnfine() = engine.start() + } + + class Engine(power: String) { + fun start() = println("Engine start") + } + diff --git "a/Chapter02_\352\260\235\354\262\264 \354\247\200\355\226\245 \355\224\204\353\241\234\352\267\270\353\236\230\353\260\215/06 \355\224\204\353\241\234\355\215\274\355\213\260\354\231\200 \354\264\210\352\270\260\355\231\224/06 \355\224\204\353\241\234\355\215\274\355\213\260\354\231\200 \354\264\210\352\270\260\355\231\224.md" "b/Chapter02_\352\260\235\354\262\264 \354\247\200\355\226\245 \355\224\204\353\241\234\352\267\270\353\236\230\353\260\215/06 \355\224\204\353\241\234\355\215\274\355\213\260\354\231\200 \354\264\210\352\270\260\355\231\224/06 \355\224\204\353\241\234\355\215\274\355\213\260\354\231\200 \354\264\210\352\270\260\355\231\224.md" new file mode 100644 index 0000000..391111a --- /dev/null +++ "b/Chapter02_\352\260\235\354\262\264 \354\247\200\355\226\245 \355\224\204\353\241\234\352\267\270\353\236\230\353\260\215/06 \355\224\204\353\241\234\355\215\274\355\213\260\354\231\200 \354\264\210\352\270\260\355\231\224/06 \355\224\204\353\241\234\355\215\274\355\213\260\354\231\200 \354\264\210\352\270\260\355\231\224.md" @@ -0,0 +1,835 @@ +# 06. 프로퍼티와 초기화 (251-296) + +\*️⃣ 키보드 고장으로 오타가 많을 수 있음 유의 + +- 코틀린의 클래스의 프로퍼티는 변수와 접근 메서드가 포함된 개념 +- 코틀린에서 프로퍼티는 게터와 세터를 자동으로 만듬 + +## 06-1 프로퍼티의 접근 ⭐ + +### 자바에서 필드를 사용할 때의 문제점 + +- 자바에서는 접근자(접근메서드 getter,setter)를 만들어야 외부에서 직접 접근하지 않으면서 값을 읽거나 저장할 수 있음 → 데이터 무결성, 보안상 문제 때문에 사용 + - 자바의 필드는 단순한 변수 선언 부분만을 가지기 때문에 접근하기 위한 메서드 (getter,setter)를 일일이 만들어야함 +- 코틀린에서는 각 프로퍼티에 게터와 세터가 자동으로 만들어짐 + - 코틀린의 프로퍼티는 변수 선언부분과 기본적인 접근 메서드를 모두 가지고 있음 + +### 코틀린에서 게터와 세터가 작동하는 방식⭐ + +```jsx +// 주 생성자에 3개의 매개변수 정의 +class User(_id: Int, _name: String, _age: Int){ + //프로퍼티 + val id: Int = _id //블변(읽기 전용) + var name: String = _name //변경 가능 + var age: Int = _age //변경 가능 +} + +fun main(){ + val user = User(1,"Sean",30) + val name = user.name //getter에 의한 값 획득 -> c#과 비슷 + user.age = 41 //setter에 의한 값 지정 + + user.id = 2 //Error! 읽기 전용 프로퍼티에는 세터로 값 지정 x + + println("name: #name, ${user.age}") +} +``` + +- user.name은 프로퍼티에 직접 접근하는 것처럼 보이나 코틀린 내부적으로 접근 메서드가 내장되어 있음 +
+ - 실제로는 getName()과 같은 코틀린 내부의 게터 메서드를 통해 접근 + +### 기본 게터와 세터 직접 지정하기 + +- 프로퍼티 선언 구조 + ```jsx + var 프로퍼티 이름[: 프로퍼티 자료형] [= 프로퍼티 초기화] + [get() {게터 본문}] + [set(value) {세터 본문}] + val 프로퍼티 이름[: 프로퍼티 자료형] [= 프로퍼티 초기화] + [get() {게터 본문}] + ``` +- 기본 게터와 세터 지정 + + ```kotlin + class User(_id: Int, _name: String, _age: Int){ + //프로퍼티 + val id: Int = _id + get() = field + + var name: String = _name + get() = name + set(value){ + field = value + this.name = value + } + var age: Int = _age + } + + fun main(){ + val user1 = User(1, "Kildong", 30) + // user1.id = 2 //val 프로퍼티는 값 변경 불가 + user1.age = 35 //setter + pritnln("user1.age = ${user1.age}") //getter + } + ``` + + - 자동으로 생성되는 코드와 동일하기 때문에 intellij idea에서는 getter와 setter가 중복되므로 삭제할 것을 권장 + - value : 세터의 매개변수로 외부로부터 값을 가져옴 / 이름 변경 가능 + - field : 프로퍼티를 참조하는 변수 / 이름 변경 불가 + +- 보조 필드의 역할 + + - field : 프로퍼티를 참조하는 변수로 보조필드라고도 함 + - field는 this로 변환됨 + - get() = field : 각 프로퍼티의 값을 읽는 특별한 식별자 + - 만약 getter setter 안에서 field 대신 get() = age와 같이 사용하면 프로퍼티의 get() 다시호출 → 무한 재귀 호출 + + - getter, setter 내부에서 프로퍼티 이름을 직접사용하면 x + + ```kotlin + var name: String = _name + get() = field + set(value){ + name = value //java로 변환될 때 this.setName(vale) 형태가 됨 + } + ... + this.name = value //field는 this.name으로 변환됨 + ``` + +
+ +### 커스텀 게터와 세터의 사용 + +- 커스텀 게터와 세터 : 사용자가 직접 게터와 세터를 정의하면서 새로운 내용을 작성하는 것 + ```kotlin + class User(_id: Int, _name: String, _age: Int){ + val id: Int = _id + var name: String = _name + set(value){ + println("The name was changed") + field = value.toUpperCase() //받은 인자를 대문다로 변경해 프롶퍼티에 할당 + } + var age: Int = _age + } + fun main(){ + val user1 = User(1, "kiildong",35) + user1.name = "coco" //여기서 사용자 고유의 출력 코드가 실행 + println("user3.name = ${user1.name}") + } + ``` + - 외부에서 name에 접근해서 사용하지 못하게 하려면 가시성 지시자를 넣어 줄 수 있음 + ```kotlin + var namme: String = _name + private set //외부에서 user.name = "test" 같이 값 재할당 금지됨 + ``` +- 보조 프로퍼티의 사용 + + - 보조 프로퍼티 : 보조 필드를 사용하지 않고 추가로 내부의 프로퍼티리를 임시로 선언하여 사용 + + ```kotlin + class User(_id: Int, _name: String, _age: Int){ + val id: Int = _id + private var tempName: String? = null + var name: String = _name + get(){ + if(tempName == null) tempName = "NONAME" + return tempName ?: throw AssertionError("Asserted by others") + } + var age: Int = _age + } + + fun main(){ + val user1 = User(1, "kiildong",35) + user1.name = "" + println("user3.name = ${user1.name}") //NONAME 출력 + } + ``` + +- 프로퍼퍼티의 오버라이딩 + + - 프로퍼티는 기본적으로 오버라이딩할 수 없는 final 형태로 선언 + - 오버라이딩 가능하게 하려면 open 키워드를 사용해 선언해야함 + + ```kotlin + open class First{ + open val x: Int = 0 //오버라이딩 가능 + get(){ + println("First x") + return field + } + val y: Int = 0 //open 키워드가 없으면 final 프로퍼티 + } + class Second: First(){ + override val x:Int = 0 //상위 클래스와 구현부가 다르게 오버라이딩 + get(){ + println("Second x") + return field + 3 + } + //override val y: Int = 0 //오류! 오버라이딩 불가 + } + + fun main(){ + val second = Second() + println(second.x) //오버라이딩된 두번째 클래스 객체의 x + println(second.y) //상위 클래스로부터 상속받은 값 + } + ``` + + → Second x + 3 + 0 + +- 프로퍼티를 이용한 나이 속이기 예제 + +```kotlin +fun main(){ + val kim = FakeAge() + kim.age = 15 + println("Kim's real age = 15, pretended age = ${kim.age}"} //18 + + val hong = FakeAge() + hong.age = 35 + println("Hong's real age = 35, pretended age = ${hkong.age}") //32 +} +class FakeAge{ + var age:Int = 0 + set(vale){ //나이에 다라 판별하는 세터 + field = when { + value < 18 -> 18 + value in 18..30 -> value + else -> value - 3 + } + } +} +``` + +## 06-2 지연 초기화와 위임 + +- 지연 초기화 : 객체의 정보가 나중에 나타나는 경우 사용 + +### lateinit을 사용한 지연 초기화 + +- Engine과 Car 처럼 특정 객체의 의존성이 있는 경우에도 지연 초기화를 해야함 +- 해당 자료형의 프로퍼티를 즉시 사용하지 않을 경우에도 지연 초기화를 하면 메모리를 절약할 수 있음 +- 모듈별로 소스 코드를 텥스트하는 유닛 테스트를 할 때 임시적으로 겍체를 생성시켜야 하는경우가 많은 데 이경우에도 사용 + +- 프로퍼티 지연 초기화하기 + + - 클래스를 선언할때 프로퍼티 선언은 null을 허용하지 않음 + - lateinit 키워드를 사용하면 프로퍼티에 값이 바로 할당되지 않아도 컴파일러에서 허용 + - 실행할때까지 값이 비어있는 상태면 오류를 유발할 수 있음 + - 초기화 하지 않고 사용ㅣ 다음 오류 발생 +
+ - 프로퍼티 초기화 방법 : 생성자에서 초기화, init 블록 초기화, 부 생성자 초기화, 매개변수의 기본값 초기화 등 → 05장 생성자 참고 + + ```kotlin + //init 블록을 통한 초기화 + class Persono{ + var name: String + init{ + name = "NONAME" //프로퍼티 name이 'NONAME"으로 초기화 + } + } + + //새애성과 동시에 기본값 초기화 + class Person{ + var name:String ="NONAME" + } + ``` + +- lateinit + + - var로 선언된 프로퍼티만 가능 + - 프로퍼티에 대한 게터와 세터를 사용할 수 없음 +
+ + ```kotlin + class Person { + lateinit var name: String //지연 초기화를 위한 선언 + fun test() { + if (!::name.isInitialized) { //프로퍼티의 초기화 여부 판단 (:: 프로퍼티 참조) + println("not initialized") + } else { + println("initialized") + } + } + } + fun main() { + val kildong = Person() + kildong.test() + kildong.name = "Kildong" //이 시정에서 초기화됨(지연 초기화) + kildong.test() + println("name = ${kildong.name}") + } + + /** + not initialized + initialized + name = Killdong + */ + ``` + +- 객체 지연 초기화하기 + + - 생성자를 통해 겍체를 생성할 때도 lateinit을 사용해 필요한 시점에 객체를 지연 초기화 할 수 있음 + + ```kotlin + data class Person(var name:String, var age:Int) + lateinit var person1: Person //객체 생성의 지연 초기화 + + fun main(){ + person1 = Person("Kildong",30) //생성자 호출 시점에서 초기화됨 + print(person1.name+" is " + person1.age.toString()) + } + ``` + +### lazy를 사용한 지연 초기화 + +- 읽기 전용의 val롤 선언한 객체나 프로퍼티를 초기화할 때 사용 +- lazy 특징 + - 호출 시점에 by lazy{…} 정의에 의해 블록 부분의 초기화를 진행한다. + - lazy는 해당 프로퍼티가 최초로 호출되면 초기화 한다. + - 불변의 변수 선언인 val에서만 사용 가능하다(읽기 전용) + - val 값이므로 값을 다시 변경할 수 없다. +- 프로퍼티 지연 초기화하기 + ```kotlin + class LazyTest{[ + init{ + println("init block") //2 초기화블럭 + } + val subject by lazy{ //by : 프로퍼티를 위임할 때 사용한 키워드 + println("lazy initialized") //6 + "Kotliln Programming" //7 lazy 반환값 + } + fun flow(){ + println("not initialized") //4 + println("subject one: $subject") //5 최초 초기화 시점 + println("subject two: $subject") //8 이미 초기화된 값 사요 + } + } + fun main(){ + val test = LazyTest() //1 객체 생성 + test.flow() //3 + } + /* + init block + not initialized + lazy initialized + subject one: Kotlin Programming + subject two: Kotlin Programming + */ + ``` + - 5에서 초기화 한 값은 다시 값 설정 불가능 +- 객체 지연 초기화 + + ```kotlin + class Person(val name:String, val age: Int) + + fun main(){ + var isPersonInstantiated: Boolean = false //초기화 확인 용도 + + val person : Person by lazy{ //1 lazy를 사용한 person 객체의 지연 초기화 + isPersonInstantiated = true + Preson("Kim",23) //2 이 부분이 Lazy 객체로 반환됨 + } + val personDelelegate = lazy{Preson("Hone", 40)} //3 위임 변수를 사용한 초기화 + + println("person init: $isPersonInstantiated") + println("personDelegate init = ${personDelegate.isInitialized()}") + + println("person.name: ${person.name}") //4 이 시점에세 초기화 + println("personDelegate.value.name = ${personDelegate.value.name}") //5 이 시점에서 초기화 + + println("person init: $isPersonInstantiated") + println("personDelegate init = ${personDelegate.isInitialized()}") + } + + /* + person Init: false + personDelegate Init : false + person name = Kim + personDelegate.value.name = Hong + person init: true + ppersonDelegate init : true + */ + ``` + + - by lazy와 lazy 할당의 차이점 + - by lazy : 객체의 위임을 나타냄 + - lazy : 변수에 위임된 Lazy 객체 자체를 나타냄 → 이 변수의 value를 한 단계 더 거처객체의 맴버인 value.name과 같은 형태로 접근해야함 + _`println_("\n${personDelegate::class.simpleName}\n")` + → SynchronizedLazyImpl + +- lazy 모드 확인하기 + - lazy 선언부 살펴보기 +
+ - lazy는 람다식으로 만들어져 있음 + - 매개변수 없는 람다식을 받을 수 있으며 Lazy를 반환 / lazy()의 실행은 구현분 SynchhronizedLazyImpl()에 보내 처리 + - mode 매개변수를 지정할 경우 SYNCHRONIZED, PUBLICATION, NONE을 지정할 수 있음 + - 기본은 SYNCHRONIZED +
+ - SynchronizedLazyImpl() 구현부 +
+ - 다른 모드를 사용하고 싶다면 by lazy(모드 이름) {…} 형태로 사용 가능 + - 항상 단일 스레드에서 사용하고 있다는 것이 보장되면 LazyThreadSafetyMode.NONE을 사용해도 됨 / 동기화 기법을 사용하지 않으면 다른 모드는 사용하는 것을 권장하지는 않음(syncronized만 사용하라는 뜻 같음) + ~~→ 뭔 소리야~~ + ```kotlin + private val model by lazy(mode = LazyThreadSafetyMode.NONE){ + Injector.app().transactionsModel() //이 코드는 단일 스레드의 사용이 보장될 때 + } + ``` +- 단일 스레드 사용의 보장 : 단 하나의 코드 흐름에서 해당 데이터를 접근 → 다른 코드에 의해 변경되지 않을 것임을 보장한다는 뜻 → 11장 동시성 프로그래밍 + +### by를 이용한 위임 + +- 위임(Delegation) : 어떤 특정일을 대신하는 중간자 역할 + - kotlin : 특정 클래스를 확장하거나 이용할 수 있도록 by를 통한 위임가능 + - 하나의 클래스가 다른 클래스에 위임하도록 선언하여 위임된 클래스가 가지는 맴버를 참조 없이 호출할 수 있음 + - 프로퍼티 위임 : 프로퍼티의 게터와 세터를 특정 객체에게 위임하고 그 객체가 값을 읽거나 쓸 때 수행하도록 만드는 것 + - 프로퍼티 혹은 클래스 이름: 자료형 by 위임자 + - 왜 위임을 사용하나 + - 기본적으로 코틀린 표준 라이브러리는 open으로 정의되지 않은 클래스를 사용 → 모두 final 형태의 클래스이므로 상속이나 직접 클래스의 기능 확장이 어려움 + → 표준 라이브러리의 무분별한 상속에 따른 복잡한 문제 방지 + → 필요한 경우에만 위임을 통해 상속과 비슷하게 해당 클래스의 모든 기능을 사용하면서 동시에 기능을 추가 확장할 수 있음 +- 클래스의 위임 + ```kotlin + interface Animal{ + fun eat(){...} + ..... + } + class Cat : Animal{} + val cat = Cat() + class Robot : Animal by cat //Animal의 정의된 Cat의 모든 맴버를 Robot에 위임 + ``` + - Animal에서 정의하고 있는 Cat의 모든 맴버를 Robot 클래스로 위임할 수 있음 + - 즉, Robot은 Cat이 가지는 모든 Animal의 메소드를 가짐 : 클래스 위임 + - Cat은 Animal 자료형의 private 맴버로 Robot 클래스 안에 저장되며 Cat에서 구현된 모든 Animal 메서드는 정적 메서드로 생성됨 + - 디컴파일 한 것 +
+ - Robot 클래스를 사용할 때 Animal을 명시적으로 참조하지 않고도 eat()을 바로 호출하는 것이 가능 +- 클래스 위임 사용하기 + + ```kotlin + interface Car{ + fun go(): String + } + + class VanImpl(val power: String): Car { + override fun go() = "은 짐을 적재하며 $power 을 가집니다." + } + + class SportImpl(val power: String): Car{ + override fun go() = "은 경주용에 사용되며 $power 을 가집니다." + } + class CarModel(val model: String, impl:Car): Car by impl{ + fun carInfo(){ + println("$model ${go()}") //참조 없이 각 인터페이스 구현 클래스의 go()에 접근 + } + } + + fun main(){ + val myDamas = CarModel("Damas 2010", VanImpl("100마력")) + val my350z = CarModel("350Z 2008",SportImpl("350마력")) + + myDamas.carInfo() //carinfo에 대한 다형성을 나타냄 + my350z.carInfo() + } + ``` + +
+ + - impl은 CarModel의 위임되어 각 구현 클래스인 VanImpl과 SportImpl의 go()메서드를 생성된 위임자에 맞춰 호출 가능 → 특정 참조 없이 go() 사용 가능 + - 이름은 동일하지만 서로 다른 go() 메서드를 호출함으로써 객체 지향의 다향성 실현 + +- 프로퍼티 위임과 by lazy + - lazy 도 by가 사용되어 위임된 프로퍼티가 사용된 것임 + - lazy가 람다식 → 사용된 프로퍼티는 람다식에 위임되어 사용 + - lazy 동작 방식 + - lazy 람다식은 람다식을 전달받아 저장한 Lazy 인스턴스르 반환한다. + - 최초 프로퍼티의 게터 실행은 lazy에 넘겨진 람다식을 실행하고 결과를 기록한다. + - 이후 프로퍼티의 게터 실행은 이미 초기화되어 기록된 값을 반환한다. +- observable() 함수와 vetoable() 함수의 위임 + - 코틀린의 표준 위임 구현 중 하나 + - Delegates 라이브러리 사용 + import kotlin.properties.Delegates + - observable() 함수 + - 프로퍼티를 위임하는 object인 Delegates로부터 사용할 수 있는 위임자 + - 프로퍼티를 감시하고 있다가 특정 코드의 로직에서 변경이 일어날 때 호출되어 처리됨 + - 틀정 변경 이벤트에 따라 호출되므로 콜백이라고 부름 + - vetoable() 함수 + - 만환값에 따라 프로퍼티 변경을 허용하거나 취소할 수 있음 + - 이 두 위임을 생성하기 위해서는 매개변수에 기본 값을 지정해야 함 + - 두 함수의 선언부 +
+ - initialValue : 초기값을 위함 + - onChange() : 프로퍼티 값이 변경될 때 호출하는 콜백 + - vetobale() 함수는 onChange()의 람다식에 Boolean을 사용하고 있어 true : 새로운 값 지정 false : 기존 oldValue를 유지 +- observable() 함수의 사용 방법 + + ```kotlin + package chap06.section2 + + import kotlin.properties.Delegates + + class User{ + var name: String by Delegates.observable("NONAME"){ // 1 프로퍼티 위임 + prop, old, new -> //2 람다식 매개변수로 프로퍼티, 기존 값, 새로운 값 지정 + println("$old -> $new") //3 : 이 부분은 이벤트가 발생할 때만 실행 + } + } + + fun main(){ + val user = User() + user.name = "Kildong" //4 값이 변경되는 시점에서 첫 이벤트 발생 + println("name : ${user.name}") + user.name = "Dooly" //5 값이 변경되는 시점에서 두번째 이벤트 발생 + println("name : ${user.name}") + } + ``` + +
+ +- vetoable() 함수의 사용 방법 + + - 최대값 구하기 + + ```kotlin + package chap06.section2 + + import kotlin.properties.Delegates + + fun main(){ + var max: Int by Delegates.vetoable(0){ //1 초기값은 0 + prop, old, new -> + new > old //2 조건에 맞지 않으면 거부권 행사 + } + + println(max) //0 + max = 10 + println(max) //10 + + //여기서부터는 기존값이 새값보다 크므로 false,따라서 5를 재할당하지 않음 + max = 5 + println(max) //10 + } + ``` + + - 컬랙션 사용 + + ```kotlin + //List 컬렉션의 data + var data: List + notifyDataSetChange() + old != new + } + ... + //코드 어딘가에서 data 프로퍼티를 설정함 + adapter.data = ... + ``` + + - 프로퍼티의 변경이 일어나면 notifyDataSetChanged()를 실행하고 old 값이 new와 다름녀 true 반환 + - 동일하지만 재할당되는 것을 막아 불필요한 실행 비용을 낮출 수 있음 + +## 06-3 정적 변수와 컴패니언 객체 + +- 변수 : 지역변수, 전역 변수 + - 지역변수 : 특정 코드 블록 안에 사용되는 변수로서 원래 있던 코드 블록을 벗어나면 삭제됨 + - 전역 변수 : 특정 코드 블록 외부에 있는 변수로서 프로그램이 실행되는 동안 메모리에서 유지될 수 있음 +- 클래스는 인스턴스를 생성해 메모리에 동적으로 초기화해서 사용 +- 동적인 초기화 없이 사용할 수 있는 변수 : 정적 변수, 캠패니언 객체 + → 동적인 메모리에 할당 해제되는 것이 아닌 프로그램을 실행할 때 고정적으로 가지는 메모리로 객체 생성 없이 사용할 수 있음 + - 자바의 static + +### 정적 변수와 캠패니언 객체 + +- 정적 변수나 메서드를 사용하면 프로그램 실행시 메모리를 고정적으로 가지게 되어 따로 인스턴스화할 필요없이 사용할 수 있음 +- 독립적으로 값을 가지고 있기 때문에 어떠한 객체라도 동일한 참조값을 가지고 있어 해당 클래스의 상태에 상관없이 접근할 수 있음 → 모든 객체에 공유되는 효과를 가짐 + +- 컴패니언 객체 사용하기 + + - 코틀린에서는 정적 변수를 사용할 때 static 키워드가 없는 대신 컴패니언 객체를 제공함 + - 자바처럼 특정 클래스 이름의 프로퍼티로 객체를 생성하지 않고 접근할 경우 + + ```kotlin + package chap06.section2 + + class Person{ + var id:Int = 0 + var name: String = "Youngdeok" + companion object{ + var language: String = "Korean" + fun work(){ + println("working...") + } + } + } + + fun main(){ + println(Person.language) //인스턴스를 생성하지 않고 기본값 사용 + Person.language = "English" //기본값 변경 가능 + println(Person.language) //변경된 내용 출력 + Person.work() //메서드 실행 + //println(Person.name) //name은 컴페니언 객체가 아니므로 오류 + } + ``` + +
+ + - companion 으로 선언한 language와 work()는 객체의 생성 없이도 접근할 수 있음 + - companion 객체는 실제 객체의 싱글톤으로 정의됨 + - 싱글톤 : 전역 변수를 사용하지 않고 객체 하나만 생성하도록 하여 생성된 객체를 어디서든 참조할 수 있도록 하는 디자인 패턴의 하나 + → 동일한 정보를 하나의 메모리만 유지해 자원의 낭비를 줄일 수 있음 + - 디자인 패턴 : 소프트웨어 설계에서 공통적인 문제에 대한 표준적인 패턴을 만들어 적용할 수 있게 한 기법 + - 패턴의 종류는 생성과 구조 행위로 나뉨 → 싱글톤은 생성 + +- 코틀린에서 자바의 static 맴버 사용하기 + + - 자바와 연동해서 정적 변수나 메서드를 접근해야 하는 경우 + + ```java + //자바의 Customer 클래스 + package chap06.section2; + + public class Customer { + public static final String LEVEL = "BASIC"; //static 팰드 + public static void login(){ //static 매서드 + System.out.println("Login..."); + } + } + ``` + + ```kotlin + package chap06.section2 + + fun main(){ + println(Customer.LEVEL) + Customer.login() + } + ``` + + - 자바의 static 필드나 메서드를 코틀린에서도 객체 생성 없이 접근 가능 + +- 자바에서 코틀린 컴패니언 객체 사용하기 + + - 자바에서 코틀린 컴패니언 객체에 접근하려면 @JvmStatic 애노테이션 표기법을 사용해야함 + - 애노테이션(Annotation) + - @ 기호로 시작하는 애노테이션 표기는 사전적으로 ‘주석’이라는 뜻 + - 코드에서는 특수한 목적을 부여해 컴파일러가 목적에 맞추어 해석하도록 하거나 실행(런타임)할 때 특정 기능을 수행하게 할 수도 있음 + - @JvmStatic 애노테이션은 자바 소스에서 코드를 해석할 때 Companion을 생략할 수 있게 해줌 + + ```kotlin + //컴패니언 객체를 가진 코틀린의 클래스 + package chap06.section2 + + class KCustomer { + companion object{ + const val LEVEL = "INTERMEDIATE" + @JvmStatic fun login() = println("Login...") //애노테이션 표기 사용 + } + } + ``` + + ```java + package chap06.section2; + + public class KCustomerAccess { + public static void main(String[] args) { + //코틀린 클래스의 컴패니언 객체에 접근 + System.out.println(KCustomer.LEVEL); + KCustomer.login(); //애노테이션을 사용할 때 접근 방법 + KCustomer.Companion.login(); //위와 동일한 결과로 애노테이션을 사용하지 않을 때 접근 방법 + } + } + ``` + +
+ + - const : 컴파일 시간의 상수임 + - 컴파일 시간의 상수 : val과 다르게 컴파일 시간에 이미 값이 할당되는 것으로 자바에서 접근하기 위해 필요함 + - val은 실행시간에 할당 + - const는 기본형으로 사용할 자료형과 String형에만 적용 가능 + - 함수 이름 위에 작성 가능 + + ```kotlin + @JvmStatic + fun login() = println("Login...") //애노테이션 표기 사용 + ``` + + - 프로퍼티를 자바에서 사용하고자 하면 @JvmField 애노테이션 사용가능 + + ```kotlin + package chap06.section2 //KCustomer.kt + + class KCustomer { + companion object{ + const val LEVEL = "INTERMEDIATE" + @JvmStatic fun login() = println("Login...") //애노테이션 표기 사용 + @JvmField val JOB = KJob() //특정 자료형을 사용하기 위한 어노테이션 + } + } + class KJob{ + var title: String = "Programmer" + } + ``` + + ```java + package chap06.section2; + + public class KCustomerAccess { + public static void main(String[] args) { + //KJob에 대한 객체 생성 후 접근 + KJob kjob = KCustomer.JOB; + System.out.println(kjob.getTitle()); + + //KCustomer를 통한 접근 + KCustomer.JOB.setTitle("Accountant"); + System.out.println(KCustomer.JOB.getTitle()); + } + } + ``` + +
+ + - KCustomer.JOB.setTitle()과 같은 방법으로 접근하거나 KJob에 대한 객체를 만들고 접근할 수 있음 + - 컴패니언 객체는 외부 클래스에서 private 프로퍼티에도 접근할 수 있기 때문에 유틸리티 클래스 등을 만드는데 사용할 수 있음 + +### 최상위 함수 사용하기 + +- 최상위 함수 or 패키지 레벨 함수 : 클래스 없이 만든 함수로 객체 생성 없이도 main() 함수 어디에서든 실행할 수 있음 + + ```kotlin + package chap06.section2 + + fun packageLevelFunc(){ + println("Package-Level Function") + } + + fun main(){ + packageLevelFunc() + } + ``` + + - 역컴파일 해보면 JVM에서 실행되기 위해 static으로 선언되어있음을 알 수 있음 +
+ - 자동으로 클래스 생성 → 파일이름과 확장자 이름이 붙은 형태로 생성 + - 자바에서 최상위 함수 접근하기 + + ```kotlin + package chap06.section2; //PackageLevelAccess.java + + public class PackageLevelAccess { + public static void main(String[] args) { + PackageLevelFunctionKt.packageLevelFunc();; + } + } + ``` + + - 클래스 이름을 자동 생성하지 않고 코틀린 코드에서 이름을 명시할 수 있음 + + - @file:JvmName(”ClassName”)을 코드 상단에 입력 + + ```kotlin + @file:JvmName("PKLevel") + package chap06.section2 + + fun packageLevelFunc(){ + println("Package-Level Function") + } + + fun main(){ + packageLevelFunc() + } + ``` + + ```java + package chap06.section2; + + public class PackageLevelAccess { + public static void main(String[] args) { + PKLevel.packageLevelFunc();; + } + } + ``` + +### object와 싱글톤 + +- 새로 하위 클래스를 선언하지 않고 내용이 조금 변경된 객체를 생성하고 싶을 때 + - 자바 : 익명 내부 클래스를 사용해 새로운 클래스 선언을 피할 수 있음 + - 코틀린 : object 포현식이나 object 선언으로 처리가능 +- object 선언 + + ```kotlin + package chap06.section2 + + //object 키워드를 사용한 방식 + object OCustomer{ + var name = "Kildong" + fun greeting() = println("Hello World!") + val HOBBY = Hobby("Basketball") + init{ + println("Init!!") + } + } + + //컴패니언 객체를 사용한 방식 + class CCustomer{ + companion object{ + const val HELLO = "hello" //상수 표현 + var name = "Joosol" + @JvmField val HOBBY =Hobby("Football") + @JvmStatic fun greeting() = println("Hello World!") + } + } + + class Hobby(val name: String) + + fun main(){ + OCustomer.greeting() //객체의 접근 시점 + OCustomer.name = "Dooly" + println("name = ${OCustomer.name}") + println(OCustomer.HOBBY.name) + + CCustomer.greeting() + println("name = ${CCustomer.name}, Hello = ${CCustomer.HELLO}") + println(OCustomer.HOBBY.name) + } + ``` + +
+ + - object로 선언된 OCustomer는 맴버 프로퍼티와 메서드를 객체 생성 없이 이름의 점(.) 표기법으로 바로 사용할 수 있음 + - 단일 인스턴스를 생성해 처리하기 때문에 싱글톤 패턴에 이용됨 + - object 선언 방식 사용 : 접근 시점에 객체 생성 + → 생성자 호출을 하지 않음 → 주 생성자, 부생성자 사용 x + - but, 초기화 블록인 init이 들어갈 수 있음 + - object 선언에서도 클래스나 인터페이스를 상속할 수 있음 + - 자바에서 object 선언으로 생성된 인스턴스에 접근하려면 INSTANCE를 사용 +
+
+ - OCustomer 객체를 INSTANCE라는 이름으로 static 블록에서 생성되고 있음 + +- object 표현식 + + - object 선언과 달리 이름이 없으며 싱글톤이 아님 + - object 표현식이 사용될 때마다 새로운 인스턴스가 생성됨 + → 이름 없는 익명 내부 클래스로 불리는 형태를 object 표현식을 만들 수 있음 + - 하위 클래스를 만들지 않고 클래스의 특정 메서드 오버라이딩 하기 +
+
+ - pretendedMan은 Superman 클래스를 상속해 fly()메서드를 오버라이딩 하고 있음 + + - 안드로이드에서 사용되는 코드 예시 +
+ - 딱 한번만 구현되는 인터페이스 구현 클래스를 정의하기가 부담스러운 경우에 사용가능 +
+ - 객체는 필요하지만 상위 인터페이스나 클래스가 없는 경우에 다음과 같이 사용가능 +
+ - 이런 익명 객체는 지역(local)이나 private 정의 영역에서만 자료형으로 사용될 수 있음 + - public 함수의 반환 자료형이나 public 속성의 자료형으로 쓰면 함수나 속성의 실제 자료형은 익명으로 선언된 상위 자료형이 되거나 혹은 상위 자료형을 선언하지 않으면 Any 형이 됨 +
+
+ - 자바의 익명 내부 클래스와 같이 object 표현식 안의 코드는 둘러싸여 있는 범위 내부의 변수에 접근할 수 있음 +
+ - object 바로 바깥의 clickCount와 enterCount를 접근하는 것을 볼 수 있음 diff --git "a/Chapter02_\352\260\235\354\262\264 \354\247\200\355\226\245 \355\224\204\353\241\234\352\267\270\353\236\230\353\260\215/06 \355\224\204\353\241\234\355\215\274\355\213\260\354\231\200 \354\264\210\352\270\260\355\231\224/06 \355\224\204\353\241\234\355\215\274\355\213\260\354\231\200 \354\264\210\352\270\260\355\231\224/Untitled 1.png" "b/Chapter02_\352\260\235\354\262\264 \354\247\200\355\226\245 \355\224\204\353\241\234\352\267\270\353\236\230\353\260\215/06 \355\224\204\353\241\234\355\215\274\355\213\260\354\231\200 \354\264\210\352\270\260\355\231\224/06 \355\224\204\353\241\234\355\215\274\355\213\260\354\231\200 \354\264\210\352\270\260\355\231\224/Untitled 1.png" new file mode 100644 index 0000000..b1f263d Binary files /dev/null and "b/Chapter02_\352\260\235\354\262\264 \354\247\200\355\226\245 \355\224\204\353\241\234\352\267\270\353\236\230\353\260\215/06 \355\224\204\353\241\234\355\215\274\355\213\260\354\231\200 \354\264\210\352\270\260\355\231\224/06 \355\224\204\353\241\234\355\215\274\355\213\260\354\231\200 \354\264\210\352\270\260\355\231\224/Untitled 1.png" differ diff --git "a/Chapter02_\352\260\235\354\262\264 \354\247\200\355\226\245 \355\224\204\353\241\234\352\267\270\353\236\230\353\260\215/06 \355\224\204\353\241\234\355\215\274\355\213\260\354\231\200 \354\264\210\352\270\260\355\231\224/06 \355\224\204\353\241\234\355\215\274\355\213\260\354\231\200 \354\264\210\352\270\260\355\231\224/Untitled 10.png" "b/Chapter02_\352\260\235\354\262\264 \354\247\200\355\226\245 \355\224\204\353\241\234\352\267\270\353\236\230\353\260\215/06 \355\224\204\353\241\234\355\215\274\355\213\260\354\231\200 \354\264\210\352\270\260\355\231\224/06 \355\224\204\353\241\234\355\215\274\355\213\260\354\231\200 \354\264\210\352\270\260\355\231\224/Untitled 10.png" new file mode 100644 index 0000000..35f12a7 Binary files /dev/null and "b/Chapter02_\352\260\235\354\262\264 \354\247\200\355\226\245 \355\224\204\353\241\234\352\267\270\353\236\230\353\260\215/06 \355\224\204\353\241\234\355\215\274\355\213\260\354\231\200 \354\264\210\352\270\260\355\231\224/06 \355\224\204\353\241\234\355\215\274\355\213\260\354\231\200 \354\264\210\352\270\260\355\231\224/Untitled 10.png" differ diff --git "a/Chapter02_\352\260\235\354\262\264 \354\247\200\355\226\245 \355\224\204\353\241\234\352\267\270\353\236\230\353\260\215/06 \355\224\204\353\241\234\355\215\274\355\213\260\354\231\200 \354\264\210\352\270\260\355\231\224/06 \355\224\204\353\241\234\355\215\274\355\213\260\354\231\200 \354\264\210\352\270\260\355\231\224/Untitled 11.png" "b/Chapter02_\352\260\235\354\262\264 \354\247\200\355\226\245 \355\224\204\353\241\234\352\267\270\353\236\230\353\260\215/06 \355\224\204\353\241\234\355\215\274\355\213\260\354\231\200 \354\264\210\352\270\260\355\231\224/06 \355\224\204\353\241\234\355\215\274\355\213\260\354\231\200 \354\264\210\352\270\260\355\231\224/Untitled 11.png" new file mode 100644 index 0000000..2169eca Binary files /dev/null and "b/Chapter02_\352\260\235\354\262\264 \354\247\200\355\226\245 \355\224\204\353\241\234\352\267\270\353\236\230\353\260\215/06 \355\224\204\353\241\234\355\215\274\355\213\260\354\231\200 \354\264\210\352\270\260\355\231\224/06 \355\224\204\353\241\234\355\215\274\355\213\260\354\231\200 \354\264\210\352\270\260\355\231\224/Untitled 11.png" differ diff --git "a/Chapter02_\352\260\235\354\262\264 \354\247\200\355\226\245 \355\224\204\353\241\234\352\267\270\353\236\230\353\260\215/06 \355\224\204\353\241\234\355\215\274\355\213\260\354\231\200 \354\264\210\352\270\260\355\231\224/06 \355\224\204\353\241\234\355\215\274\355\213\260\354\231\200 \354\264\210\352\270\260\355\231\224/Untitled 12.png" "b/Chapter02_\352\260\235\354\262\264 \354\247\200\355\226\245 \355\224\204\353\241\234\352\267\270\353\236\230\353\260\215/06 \355\224\204\353\241\234\355\215\274\355\213\260\354\231\200 \354\264\210\352\270\260\355\231\224/06 \355\224\204\353\241\234\355\215\274\355\213\260\354\231\200 \354\264\210\352\270\260\355\231\224/Untitled 12.png" new file mode 100644 index 0000000..9eefb6c Binary files /dev/null and "b/Chapter02_\352\260\235\354\262\264 \354\247\200\355\226\245 \355\224\204\353\241\234\352\267\270\353\236\230\353\260\215/06 \355\224\204\353\241\234\355\215\274\355\213\260\354\231\200 \354\264\210\352\270\260\355\231\224/06 \355\224\204\353\241\234\355\215\274\355\213\260\354\231\200 \354\264\210\352\270\260\355\231\224/Untitled 12.png" differ diff --git "a/Chapter02_\352\260\235\354\262\264 \354\247\200\355\226\245 \355\224\204\353\241\234\352\267\270\353\236\230\353\260\215/06 \355\224\204\353\241\234\355\215\274\355\213\260\354\231\200 \354\264\210\352\270\260\355\231\224/06 \355\224\204\353\241\234\355\215\274\355\213\260\354\231\200 \354\264\210\352\270\260\355\231\224/Untitled 13.png" "b/Chapter02_\352\260\235\354\262\264 \354\247\200\355\226\245 \355\224\204\353\241\234\352\267\270\353\236\230\353\260\215/06 \355\224\204\353\241\234\355\215\274\355\213\260\354\231\200 \354\264\210\352\270\260\355\231\224/06 \355\224\204\353\241\234\355\215\274\355\213\260\354\231\200 \354\264\210\352\270\260\355\231\224/Untitled 13.png" new file mode 100644 index 0000000..e8c15b7 Binary files /dev/null and "b/Chapter02_\352\260\235\354\262\264 \354\247\200\355\226\245 \355\224\204\353\241\234\352\267\270\353\236\230\353\260\215/06 \355\224\204\353\241\234\355\215\274\355\213\260\354\231\200 \354\264\210\352\270\260\355\231\224/06 \355\224\204\353\241\234\355\215\274\355\213\260\354\231\200 \354\264\210\352\270\260\355\231\224/Untitled 13.png" differ diff --git "a/Chapter02_\352\260\235\354\262\264 \354\247\200\355\226\245 \355\224\204\353\241\234\352\267\270\353\236\230\353\260\215/06 \355\224\204\353\241\234\355\215\274\355\213\260\354\231\200 \354\264\210\352\270\260\355\231\224/06 \355\224\204\353\241\234\355\215\274\355\213\260\354\231\200 \354\264\210\352\270\260\355\231\224/Untitled 14.png" "b/Chapter02_\352\260\235\354\262\264 \354\247\200\355\226\245 \355\224\204\353\241\234\352\267\270\353\236\230\353\260\215/06 \355\224\204\353\241\234\355\215\274\355\213\260\354\231\200 \354\264\210\352\270\260\355\231\224/06 \355\224\204\353\241\234\355\215\274\355\213\260\354\231\200 \354\264\210\352\270\260\355\231\224/Untitled 14.png" new file mode 100644 index 0000000..1c7a5ff Binary files /dev/null and "b/Chapter02_\352\260\235\354\262\264 \354\247\200\355\226\245 \355\224\204\353\241\234\352\267\270\353\236\230\353\260\215/06 \355\224\204\353\241\234\355\215\274\355\213\260\354\231\200 \354\264\210\352\270\260\355\231\224/06 \355\224\204\353\241\234\355\215\274\355\213\260\354\231\200 \354\264\210\352\270\260\355\231\224/Untitled 14.png" differ diff --git "a/Chapter02_\352\260\235\354\262\264 \354\247\200\355\226\245 \355\224\204\353\241\234\352\267\270\353\236\230\353\260\215/06 \355\224\204\353\241\234\355\215\274\355\213\260\354\231\200 \354\264\210\352\270\260\355\231\224/06 \355\224\204\353\241\234\355\215\274\355\213\260\354\231\200 \354\264\210\352\270\260\355\231\224/Untitled 15.png" "b/Chapter02_\352\260\235\354\262\264 \354\247\200\355\226\245 \355\224\204\353\241\234\352\267\270\353\236\230\353\260\215/06 \355\224\204\353\241\234\355\215\274\355\213\260\354\231\200 \354\264\210\352\270\260\355\231\224/06 \355\224\204\353\241\234\355\215\274\355\213\260\354\231\200 \354\264\210\352\270\260\355\231\224/Untitled 15.png" new file mode 100644 index 0000000..44a1960 Binary files /dev/null and "b/Chapter02_\352\260\235\354\262\264 \354\247\200\355\226\245 \355\224\204\353\241\234\352\267\270\353\236\230\353\260\215/06 \355\224\204\353\241\234\355\215\274\355\213\260\354\231\200 \354\264\210\352\270\260\355\231\224/06 \355\224\204\353\241\234\355\215\274\355\213\260\354\231\200 \354\264\210\352\270\260\355\231\224/Untitled 15.png" differ diff --git "a/Chapter02_\352\260\235\354\262\264 \354\247\200\355\226\245 \355\224\204\353\241\234\352\267\270\353\236\230\353\260\215/06 \355\224\204\353\241\234\355\215\274\355\213\260\354\231\200 \354\264\210\352\270\260\355\231\224/06 \355\224\204\353\241\234\355\215\274\355\213\260\354\231\200 \354\264\210\352\270\260\355\231\224/Untitled 16.png" "b/Chapter02_\352\260\235\354\262\264 \354\247\200\355\226\245 \355\224\204\353\241\234\352\267\270\353\236\230\353\260\215/06 \355\224\204\353\241\234\355\215\274\355\213\260\354\231\200 \354\264\210\352\270\260\355\231\224/06 \355\224\204\353\241\234\355\215\274\355\213\260\354\231\200 \354\264\210\352\270\260\355\231\224/Untitled 16.png" new file mode 100644 index 0000000..cf2780f Binary files /dev/null and "b/Chapter02_\352\260\235\354\262\264 \354\247\200\355\226\245 \355\224\204\353\241\234\352\267\270\353\236\230\353\260\215/06 \355\224\204\353\241\234\355\215\274\355\213\260\354\231\200 \354\264\210\352\270\260\355\231\224/06 \355\224\204\353\241\234\355\215\274\355\213\260\354\231\200 \354\264\210\352\270\260\355\231\224/Untitled 16.png" differ diff --git "a/Chapter02_\352\260\235\354\262\264 \354\247\200\355\226\245 \355\224\204\353\241\234\352\267\270\353\236\230\353\260\215/06 \355\224\204\353\241\234\355\215\274\355\213\260\354\231\200 \354\264\210\352\270\260\355\231\224/06 \355\224\204\353\241\234\355\215\274\355\213\260\354\231\200 \354\264\210\352\270\260\355\231\224/Untitled 17.png" "b/Chapter02_\352\260\235\354\262\264 \354\247\200\355\226\245 \355\224\204\353\241\234\352\267\270\353\236\230\353\260\215/06 \355\224\204\353\241\234\355\215\274\355\213\260\354\231\200 \354\264\210\352\270\260\355\231\224/06 \355\224\204\353\241\234\355\215\274\355\213\260\354\231\200 \354\264\210\352\270\260\355\231\224/Untitled 17.png" new file mode 100644 index 0000000..c181070 Binary files /dev/null and "b/Chapter02_\352\260\235\354\262\264 \354\247\200\355\226\245 \355\224\204\353\241\234\352\267\270\353\236\230\353\260\215/06 \355\224\204\353\241\234\355\215\274\355\213\260\354\231\200 \354\264\210\352\270\260\355\231\224/06 \355\224\204\353\241\234\355\215\274\355\213\260\354\231\200 \354\264\210\352\270\260\355\231\224/Untitled 17.png" differ diff --git "a/Chapter02_\352\260\235\354\262\264 \354\247\200\355\226\245 \355\224\204\353\241\234\352\267\270\353\236\230\353\260\215/06 \355\224\204\353\241\234\355\215\274\355\213\260\354\231\200 \354\264\210\352\270\260\355\231\224/06 \355\224\204\353\241\234\355\215\274\355\213\260\354\231\200 \354\264\210\352\270\260\355\231\224/Untitled 18.png" "b/Chapter02_\352\260\235\354\262\264 \354\247\200\355\226\245 \355\224\204\353\241\234\352\267\270\353\236\230\353\260\215/06 \355\224\204\353\241\234\355\215\274\355\213\260\354\231\200 \354\264\210\352\270\260\355\231\224/06 \355\224\204\353\241\234\355\215\274\355\213\260\354\231\200 \354\264\210\352\270\260\355\231\224/Untitled 18.png" new file mode 100644 index 0000000..1cf3a62 Binary files /dev/null and "b/Chapter02_\352\260\235\354\262\264 \354\247\200\355\226\245 \355\224\204\353\241\234\352\267\270\353\236\230\353\260\215/06 \355\224\204\353\241\234\355\215\274\355\213\260\354\231\200 \354\264\210\352\270\260\355\231\224/06 \355\224\204\353\241\234\355\215\274\355\213\260\354\231\200 \354\264\210\352\270\260\355\231\224/Untitled 18.png" differ diff --git "a/Chapter02_\352\260\235\354\262\264 \354\247\200\355\226\245 \355\224\204\353\241\234\352\267\270\353\236\230\353\260\215/06 \355\224\204\353\241\234\355\215\274\355\213\260\354\231\200 \354\264\210\352\270\260\355\231\224/06 \355\224\204\353\241\234\355\215\274\355\213\260\354\231\200 \354\264\210\352\270\260\355\231\224/Untitled 19.png" "b/Chapter02_\352\260\235\354\262\264 \354\247\200\355\226\245 \355\224\204\353\241\234\352\267\270\353\236\230\353\260\215/06 \355\224\204\353\241\234\355\215\274\355\213\260\354\231\200 \354\264\210\352\270\260\355\231\224/06 \355\224\204\353\241\234\355\215\274\355\213\260\354\231\200 \354\264\210\352\270\260\355\231\224/Untitled 19.png" new file mode 100644 index 0000000..da5cb79 Binary files /dev/null and "b/Chapter02_\352\260\235\354\262\264 \354\247\200\355\226\245 \355\224\204\353\241\234\352\267\270\353\236\230\353\260\215/06 \355\224\204\353\241\234\355\215\274\355\213\260\354\231\200 \354\264\210\352\270\260\355\231\224/06 \355\224\204\353\241\234\355\215\274\355\213\260\354\231\200 \354\264\210\352\270\260\355\231\224/Untitled 19.png" differ diff --git "a/Chapter02_\352\260\235\354\262\264 \354\247\200\355\226\245 \355\224\204\353\241\234\352\267\270\353\236\230\353\260\215/06 \355\224\204\353\241\234\355\215\274\355\213\260\354\231\200 \354\264\210\352\270\260\355\231\224/06 \355\224\204\353\241\234\355\215\274\355\213\260\354\231\200 \354\264\210\352\270\260\355\231\224/Untitled 2.png" "b/Chapter02_\352\260\235\354\262\264 \354\247\200\355\226\245 \355\224\204\353\241\234\352\267\270\353\236\230\353\260\215/06 \355\224\204\353\241\234\355\215\274\355\213\260\354\231\200 \354\264\210\352\270\260\355\231\224/06 \355\224\204\353\241\234\355\215\274\355\213\260\354\231\200 \354\264\210\352\270\260\355\231\224/Untitled 2.png" new file mode 100644 index 0000000..70a7017 Binary files /dev/null and "b/Chapter02_\352\260\235\354\262\264 \354\247\200\355\226\245 \355\224\204\353\241\234\352\267\270\353\236\230\353\260\215/06 \355\224\204\353\241\234\355\215\274\355\213\260\354\231\200 \354\264\210\352\270\260\355\231\224/06 \355\224\204\353\241\234\355\215\274\355\213\260\354\231\200 \354\264\210\352\270\260\355\231\224/Untitled 2.png" differ diff --git "a/Chapter02_\352\260\235\354\262\264 \354\247\200\355\226\245 \355\224\204\353\241\234\352\267\270\353\236\230\353\260\215/06 \355\224\204\353\241\234\355\215\274\355\213\260\354\231\200 \354\264\210\352\270\260\355\231\224/06 \355\224\204\353\241\234\355\215\274\355\213\260\354\231\200 \354\264\210\352\270\260\355\231\224/Untitled 20.png" "b/Chapter02_\352\260\235\354\262\264 \354\247\200\355\226\245 \355\224\204\353\241\234\352\267\270\353\236\230\353\260\215/06 \355\224\204\353\241\234\355\215\274\355\213\260\354\231\200 \354\264\210\352\270\260\355\231\224/06 \355\224\204\353\241\234\355\215\274\355\213\260\354\231\200 \354\264\210\352\270\260\355\231\224/Untitled 20.png" new file mode 100644 index 0000000..784d599 Binary files /dev/null and "b/Chapter02_\352\260\235\354\262\264 \354\247\200\355\226\245 \355\224\204\353\241\234\352\267\270\353\236\230\353\260\215/06 \355\224\204\353\241\234\355\215\274\355\213\260\354\231\200 \354\264\210\352\270\260\355\231\224/06 \355\224\204\353\241\234\355\215\274\355\213\260\354\231\200 \354\264\210\352\270\260\355\231\224/Untitled 20.png" differ diff --git "a/Chapter02_\352\260\235\354\262\264 \354\247\200\355\226\245 \355\224\204\353\241\234\352\267\270\353\236\230\353\260\215/06 \355\224\204\353\241\234\355\215\274\355\213\260\354\231\200 \354\264\210\352\270\260\355\231\224/06 \355\224\204\353\241\234\355\215\274\355\213\260\354\231\200 \354\264\210\352\270\260\355\231\224/Untitled 21.png" "b/Chapter02_\352\260\235\354\262\264 \354\247\200\355\226\245 \355\224\204\353\241\234\352\267\270\353\236\230\353\260\215/06 \355\224\204\353\241\234\355\215\274\355\213\260\354\231\200 \354\264\210\352\270\260\355\231\224/06 \355\224\204\353\241\234\355\215\274\355\213\260\354\231\200 \354\264\210\352\270\260\355\231\224/Untitled 21.png" new file mode 100644 index 0000000..8cb3560 Binary files /dev/null and "b/Chapter02_\352\260\235\354\262\264 \354\247\200\355\226\245 \355\224\204\353\241\234\352\267\270\353\236\230\353\260\215/06 \355\224\204\353\241\234\355\215\274\355\213\260\354\231\200 \354\264\210\352\270\260\355\231\224/06 \355\224\204\353\241\234\355\215\274\355\213\260\354\231\200 \354\264\210\352\270\260\355\231\224/Untitled 21.png" differ diff --git "a/Chapter02_\352\260\235\354\262\264 \354\247\200\355\226\245 \355\224\204\353\241\234\352\267\270\353\236\230\353\260\215/06 \355\224\204\353\241\234\355\215\274\355\213\260\354\231\200 \354\264\210\352\270\260\355\231\224/06 \355\224\204\353\241\234\355\215\274\355\213\260\354\231\200 \354\264\210\352\270\260\355\231\224/Untitled 22.png" "b/Chapter02_\352\260\235\354\262\264 \354\247\200\355\226\245 \355\224\204\353\241\234\352\267\270\353\236\230\353\260\215/06 \355\224\204\353\241\234\355\215\274\355\213\260\354\231\200 \354\264\210\352\270\260\355\231\224/06 \355\224\204\353\241\234\355\215\274\355\213\260\354\231\200 \354\264\210\352\270\260\355\231\224/Untitled 22.png" new file mode 100644 index 0000000..df2a791 Binary files /dev/null and "b/Chapter02_\352\260\235\354\262\264 \354\247\200\355\226\245 \355\224\204\353\241\234\352\267\270\353\236\230\353\260\215/06 \355\224\204\353\241\234\355\215\274\355\213\260\354\231\200 \354\264\210\352\270\260\355\231\224/06 \355\224\204\353\241\234\355\215\274\355\213\260\354\231\200 \354\264\210\352\270\260\355\231\224/Untitled 22.png" differ diff --git "a/Chapter02_\352\260\235\354\262\264 \354\247\200\355\226\245 \355\224\204\353\241\234\352\267\270\353\236\230\353\260\215/06 \355\224\204\353\241\234\355\215\274\355\213\260\354\231\200 \354\264\210\352\270\260\355\231\224/06 \355\224\204\353\241\234\355\215\274\355\213\260\354\231\200 \354\264\210\352\270\260\355\231\224/Untitled 23.png" "b/Chapter02_\352\260\235\354\262\264 \354\247\200\355\226\245 \355\224\204\353\241\234\352\267\270\353\236\230\353\260\215/06 \355\224\204\353\241\234\355\215\274\355\213\260\354\231\200 \354\264\210\352\270\260\355\231\224/06 \355\224\204\353\241\234\355\215\274\355\213\260\354\231\200 \354\264\210\352\270\260\355\231\224/Untitled 23.png" new file mode 100644 index 0000000..a36725a Binary files /dev/null and "b/Chapter02_\352\260\235\354\262\264 \354\247\200\355\226\245 \355\224\204\353\241\234\352\267\270\353\236\230\353\260\215/06 \355\224\204\353\241\234\355\215\274\355\213\260\354\231\200 \354\264\210\352\270\260\355\231\224/06 \355\224\204\353\241\234\355\215\274\355\213\260\354\231\200 \354\264\210\352\270\260\355\231\224/Untitled 23.png" differ diff --git "a/Chapter02_\352\260\235\354\262\264 \354\247\200\355\226\245 \355\224\204\353\241\234\352\267\270\353\236\230\353\260\215/06 \355\224\204\353\241\234\355\215\274\355\213\260\354\231\200 \354\264\210\352\270\260\355\231\224/06 \355\224\204\353\241\234\355\215\274\355\213\260\354\231\200 \354\264\210\352\270\260\355\231\224/Untitled 24.png" "b/Chapter02_\352\260\235\354\262\264 \354\247\200\355\226\245 \355\224\204\353\241\234\352\267\270\353\236\230\353\260\215/06 \355\224\204\353\241\234\355\215\274\355\213\260\354\231\200 \354\264\210\352\270\260\355\231\224/06 \355\224\204\353\241\234\355\215\274\355\213\260\354\231\200 \354\264\210\352\270\260\355\231\224/Untitled 24.png" new file mode 100644 index 0000000..3d17b39 Binary files /dev/null and "b/Chapter02_\352\260\235\354\262\264 \354\247\200\355\226\245 \355\224\204\353\241\234\352\267\270\353\236\230\353\260\215/06 \355\224\204\353\241\234\355\215\274\355\213\260\354\231\200 \354\264\210\352\270\260\355\231\224/06 \355\224\204\353\241\234\355\215\274\355\213\260\354\231\200 \354\264\210\352\270\260\355\231\224/Untitled 24.png" differ diff --git "a/Chapter02_\352\260\235\354\262\264 \354\247\200\355\226\245 \355\224\204\353\241\234\352\267\270\353\236\230\353\260\215/06 \355\224\204\353\241\234\355\215\274\355\213\260\354\231\200 \354\264\210\352\270\260\355\231\224/06 \355\224\204\353\241\234\355\215\274\355\213\260\354\231\200 \354\264\210\352\270\260\355\231\224/Untitled 25.png" "b/Chapter02_\352\260\235\354\262\264 \354\247\200\355\226\245 \355\224\204\353\241\234\352\267\270\353\236\230\353\260\215/06 \355\224\204\353\241\234\355\215\274\355\213\260\354\231\200 \354\264\210\352\270\260\355\231\224/06 \355\224\204\353\241\234\355\215\274\355\213\260\354\231\200 \354\264\210\352\270\260\355\231\224/Untitled 25.png" new file mode 100644 index 0000000..6fc049d Binary files /dev/null and "b/Chapter02_\352\260\235\354\262\264 \354\247\200\355\226\245 \355\224\204\353\241\234\352\267\270\353\236\230\353\260\215/06 \355\224\204\353\241\234\355\215\274\355\213\260\354\231\200 \354\264\210\352\270\260\355\231\224/06 \355\224\204\353\241\234\355\215\274\355\213\260\354\231\200 \354\264\210\352\270\260\355\231\224/Untitled 25.png" differ diff --git "a/Chapter02_\352\260\235\354\262\264 \354\247\200\355\226\245 \355\224\204\353\241\234\352\267\270\353\236\230\353\260\215/06 \355\224\204\353\241\234\355\215\274\355\213\260\354\231\200 \354\264\210\352\270\260\355\231\224/06 \355\224\204\353\241\234\355\215\274\355\213\260\354\231\200 \354\264\210\352\270\260\355\231\224/Untitled 3.png" "b/Chapter02_\352\260\235\354\262\264 \354\247\200\355\226\245 \355\224\204\353\241\234\352\267\270\353\236\230\353\260\215/06 \355\224\204\353\241\234\355\215\274\355\213\260\354\231\200 \354\264\210\352\270\260\355\231\224/06 \355\224\204\353\241\234\355\215\274\355\213\260\354\231\200 \354\264\210\352\270\260\355\231\224/Untitled 3.png" new file mode 100644 index 0000000..7870b45 Binary files /dev/null and "b/Chapter02_\352\260\235\354\262\264 \354\247\200\355\226\245 \355\224\204\353\241\234\352\267\270\353\236\230\353\260\215/06 \355\224\204\353\241\234\355\215\274\355\213\260\354\231\200 \354\264\210\352\270\260\355\231\224/06 \355\224\204\353\241\234\355\215\274\355\213\260\354\231\200 \354\264\210\352\270\260\355\231\224/Untitled 3.png" differ diff --git "a/Chapter02_\352\260\235\354\262\264 \354\247\200\355\226\245 \355\224\204\353\241\234\352\267\270\353\236\230\353\260\215/06 \355\224\204\353\241\234\355\215\274\355\213\260\354\231\200 \354\264\210\352\270\260\355\231\224/06 \355\224\204\353\241\234\355\215\274\355\213\260\354\231\200 \354\264\210\352\270\260\355\231\224/Untitled 4.png" "b/Chapter02_\352\260\235\354\262\264 \354\247\200\355\226\245 \355\224\204\353\241\234\352\267\270\353\236\230\353\260\215/06 \355\224\204\353\241\234\355\215\274\355\213\260\354\231\200 \354\264\210\352\270\260\355\231\224/06 \355\224\204\353\241\234\355\215\274\355\213\260\354\231\200 \354\264\210\352\270\260\355\231\224/Untitled 4.png" new file mode 100644 index 0000000..976df6b Binary files /dev/null and "b/Chapter02_\352\260\235\354\262\264 \354\247\200\355\226\245 \355\224\204\353\241\234\352\267\270\353\236\230\353\260\215/06 \355\224\204\353\241\234\355\215\274\355\213\260\354\231\200 \354\264\210\352\270\260\355\231\224/06 \355\224\204\353\241\234\355\215\274\355\213\260\354\231\200 \354\264\210\352\270\260\355\231\224/Untitled 4.png" differ diff --git "a/Chapter02_\352\260\235\354\262\264 \354\247\200\355\226\245 \355\224\204\353\241\234\352\267\270\353\236\230\353\260\215/06 \355\224\204\353\241\234\355\215\274\355\213\260\354\231\200 \354\264\210\352\270\260\355\231\224/06 \355\224\204\353\241\234\355\215\274\355\213\260\354\231\200 \354\264\210\352\270\260\355\231\224/Untitled 5.png" "b/Chapter02_\352\260\235\354\262\264 \354\247\200\355\226\245 \355\224\204\353\241\234\352\267\270\353\236\230\353\260\215/06 \355\224\204\353\241\234\355\215\274\355\213\260\354\231\200 \354\264\210\352\270\260\355\231\224/06 \355\224\204\353\241\234\355\215\274\355\213\260\354\231\200 \354\264\210\352\270\260\355\231\224/Untitled 5.png" new file mode 100644 index 0000000..b12921f Binary files /dev/null and "b/Chapter02_\352\260\235\354\262\264 \354\247\200\355\226\245 \355\224\204\353\241\234\352\267\270\353\236\230\353\260\215/06 \355\224\204\353\241\234\355\215\274\355\213\260\354\231\200 \354\264\210\352\270\260\355\231\224/06 \355\224\204\353\241\234\355\215\274\355\213\260\354\231\200 \354\264\210\352\270\260\355\231\224/Untitled 5.png" differ diff --git "a/Chapter02_\352\260\235\354\262\264 \354\247\200\355\226\245 \355\224\204\353\241\234\352\267\270\353\236\230\353\260\215/06 \355\224\204\353\241\234\355\215\274\355\213\260\354\231\200 \354\264\210\352\270\260\355\231\224/06 \355\224\204\353\241\234\355\215\274\355\213\260\354\231\200 \354\264\210\352\270\260\355\231\224/Untitled 6.png" "b/Chapter02_\352\260\235\354\262\264 \354\247\200\355\226\245 \355\224\204\353\241\234\352\267\270\353\236\230\353\260\215/06 \355\224\204\353\241\234\355\215\274\355\213\260\354\231\200 \354\264\210\352\270\260\355\231\224/06 \355\224\204\353\241\234\355\215\274\355\213\260\354\231\200 \354\264\210\352\270\260\355\231\224/Untitled 6.png" new file mode 100644 index 0000000..6ef430c Binary files /dev/null and "b/Chapter02_\352\260\235\354\262\264 \354\247\200\355\226\245 \355\224\204\353\241\234\352\267\270\353\236\230\353\260\215/06 \355\224\204\353\241\234\355\215\274\355\213\260\354\231\200 \354\264\210\352\270\260\355\231\224/06 \355\224\204\353\241\234\355\215\274\355\213\260\354\231\200 \354\264\210\352\270\260\355\231\224/Untitled 6.png" differ diff --git "a/Chapter02_\352\260\235\354\262\264 \354\247\200\355\226\245 \355\224\204\353\241\234\352\267\270\353\236\230\353\260\215/06 \355\224\204\353\241\234\355\215\274\355\213\260\354\231\200 \354\264\210\352\270\260\355\231\224/06 \355\224\204\353\241\234\355\215\274\355\213\260\354\231\200 \354\264\210\352\270\260\355\231\224/Untitled 7.png" "b/Chapter02_\352\260\235\354\262\264 \354\247\200\355\226\245 \355\224\204\353\241\234\352\267\270\353\236\230\353\260\215/06 \355\224\204\353\241\234\355\215\274\355\213\260\354\231\200 \354\264\210\352\270\260\355\231\224/06 \355\224\204\353\241\234\355\215\274\355\213\260\354\231\200 \354\264\210\352\270\260\355\231\224/Untitled 7.png" new file mode 100644 index 0000000..f881aa7 Binary files /dev/null and "b/Chapter02_\352\260\235\354\262\264 \354\247\200\355\226\245 \355\224\204\353\241\234\352\267\270\353\236\230\353\260\215/06 \355\224\204\353\241\234\355\215\274\355\213\260\354\231\200 \354\264\210\352\270\260\355\231\224/06 \355\224\204\353\241\234\355\215\274\355\213\260\354\231\200 \354\264\210\352\270\260\355\231\224/Untitled 7.png" differ diff --git "a/Chapter02_\352\260\235\354\262\264 \354\247\200\355\226\245 \355\224\204\353\241\234\352\267\270\353\236\230\353\260\215/06 \355\224\204\353\241\234\355\215\274\355\213\260\354\231\200 \354\264\210\352\270\260\355\231\224/06 \355\224\204\353\241\234\355\215\274\355\213\260\354\231\200 \354\264\210\352\270\260\355\231\224/Untitled 8.png" "b/Chapter02_\352\260\235\354\262\264 \354\247\200\355\226\245 \355\224\204\353\241\234\352\267\270\353\236\230\353\260\215/06 \355\224\204\353\241\234\355\215\274\355\213\260\354\231\200 \354\264\210\352\270\260\355\231\224/06 \355\224\204\353\241\234\355\215\274\355\213\260\354\231\200 \354\264\210\352\270\260\355\231\224/Untitled 8.png" new file mode 100644 index 0000000..35ceff5 Binary files /dev/null and "b/Chapter02_\352\260\235\354\262\264 \354\247\200\355\226\245 \355\224\204\353\241\234\352\267\270\353\236\230\353\260\215/06 \355\224\204\353\241\234\355\215\274\355\213\260\354\231\200 \354\264\210\352\270\260\355\231\224/06 \355\224\204\353\241\234\355\215\274\355\213\260\354\231\200 \354\264\210\352\270\260\355\231\224/Untitled 8.png" differ diff --git "a/Chapter02_\352\260\235\354\262\264 \354\247\200\355\226\245 \355\224\204\353\241\234\352\267\270\353\236\230\353\260\215/06 \355\224\204\353\241\234\355\215\274\355\213\260\354\231\200 \354\264\210\352\270\260\355\231\224/06 \355\224\204\353\241\234\355\215\274\355\213\260\354\231\200 \354\264\210\352\270\260\355\231\224/Untitled 9.png" "b/Chapter02_\352\260\235\354\262\264 \354\247\200\355\226\245 \355\224\204\353\241\234\352\267\270\353\236\230\353\260\215/06 \355\224\204\353\241\234\355\215\274\355\213\260\354\231\200 \354\264\210\352\270\260\355\231\224/06 \355\224\204\353\241\234\355\215\274\355\213\260\354\231\200 \354\264\210\352\270\260\355\231\224/Untitled 9.png" new file mode 100644 index 0000000..7fe96e0 Binary files /dev/null and "b/Chapter02_\352\260\235\354\262\264 \354\247\200\355\226\245 \355\224\204\353\241\234\352\267\270\353\236\230\353\260\215/06 \355\224\204\353\241\234\355\215\274\355\213\260\354\231\200 \354\264\210\352\270\260\355\231\224/06 \355\224\204\353\241\234\355\215\274\355\213\260\354\231\200 \354\264\210\352\270\260\355\231\224/Untitled 9.png" differ diff --git "a/Chapter02_\352\260\235\354\262\264 \354\247\200\355\226\245 \355\224\204\353\241\234\352\267\270\353\236\230\353\260\215/06 \355\224\204\353\241\234\355\215\274\355\213\260\354\231\200 \354\264\210\352\270\260\355\231\224/06 \355\224\204\353\241\234\355\215\274\355\213\260\354\231\200 \354\264\210\352\270\260\355\231\224/Untitled.png" "b/Chapter02_\352\260\235\354\262\264 \354\247\200\355\226\245 \355\224\204\353\241\234\352\267\270\353\236\230\353\260\215/06 \355\224\204\353\241\234\355\215\274\355\213\260\354\231\200 \354\264\210\352\270\260\355\231\224/06 \355\224\204\353\241\234\355\215\274\355\213\260\354\231\200 \354\264\210\352\270\260\355\231\224/Untitled.png" new file mode 100644 index 0000000..0efdfd3 Binary files /dev/null and "b/Chapter02_\352\260\235\354\262\264 \354\247\200\355\226\245 \355\224\204\353\241\234\352\267\270\353\236\230\353\260\215/06 \355\224\204\353\241\234\355\215\274\355\213\260\354\231\200 \354\264\210\352\270\260\355\231\224/06 \355\224\204\353\241\234\355\215\274\355\213\260\354\231\200 \354\264\210\352\270\260\355\231\224/Untitled.png" differ diff --git "a/Chapter02_\352\260\235\354\262\264 \354\247\200\355\226\245 \355\224\204\353\241\234\352\267\270\353\236\230\353\260\215/06 \355\224\204\353\241\234\355\215\274\355\213\260\354\231\200 \354\264\210\352\270\260\355\231\224/readme.md" "b/Chapter02_\352\260\235\354\262\264 \354\247\200\355\226\245 \355\224\204\353\241\234\352\267\270\353\236\230\353\260\215/06 \355\224\204\353\241\234\355\215\274\355\213\260\354\231\200 \354\264\210\352\270\260\355\231\224/readme.md" new file mode 100644 index 0000000..a9f9034 --- /dev/null +++ "b/Chapter02_\352\260\235\354\262\264 \354\247\200\355\226\245 \355\224\204\353\241\234\352\267\270\353\236\230\353\260\215/06 \355\224\204\353\241\234\355\215\274\355\213\260\354\231\200 \354\264\210\352\270\260\355\231\224/readme.md" @@ -0,0 +1 @@ +발표자 : 이아름 diff --git "a/Chapter07_\353\213\244\354\226\221\355\225\234 \355\201\264\353\236\230\354\212\244\354\231\200 \354\235\270\355\204\260\355\216\230\354\235\264\354\212\244/1_\354\266\224\354\203\201 \355\201\264\353\236\230\354\212\244\354\231\200 \354\235\270\355\204\260\355\216\230\354\235\264\354\212\244/README.md" "b/Chapter02_\352\260\235\354\262\264 \354\247\200\355\226\245 \355\224\204\353\241\234\352\267\270\353\236\230\353\260\215/07_\353\213\244\354\226\221\355\225\234 \355\201\264\353\236\230\354\212\244\354\231\200 \354\235\270\355\204\260\355\216\230\354\235\264\354\212\244/1_\354\266\224\354\203\201 \355\201\264\353\236\230\354\212\244\354\231\200 \354\235\270\355\204\260\355\216\230\354\235\264\354\212\244/README.md" similarity index 100% rename from "Chapter07_\353\213\244\354\226\221\355\225\234 \355\201\264\353\236\230\354\212\244\354\231\200 \354\235\270\355\204\260\355\216\230\354\235\264\354\212\244/1_\354\266\224\354\203\201 \355\201\264\353\236\230\354\212\244\354\231\200 \354\235\270\355\204\260\355\216\230\354\235\264\354\212\244/README.md" rename to "Chapter02_\352\260\235\354\262\264 \354\247\200\355\226\245 \355\224\204\353\241\234\352\267\270\353\236\230\353\260\215/07_\353\213\244\354\226\221\355\225\234 \355\201\264\353\236\230\354\212\244\354\231\200 \354\235\270\355\204\260\355\216\230\354\235\264\354\212\244/1_\354\266\224\354\203\201 \355\201\264\353\236\230\354\212\244\354\231\200 \354\235\270\355\204\260\355\216\230\354\235\264\354\212\244/README.md" diff --git "a/Chapter07_\353\213\244\354\226\221\355\225\234 \355\201\264\353\236\230\354\212\244\354\231\200 \354\235\270\355\204\260\355\216\230\354\235\264\354\212\244/2_\353\215\260\354\235\264\355\204\260 \355\201\264\353\236\230\354\212\244\354\231\200 \352\270\260\355\203\200 \355\201\264\353\236\230\354\212\244/README.md" "b/Chapter02_\352\260\235\354\262\264 \354\247\200\355\226\245 \355\224\204\353\241\234\352\267\270\353\236\230\353\260\215/07_\353\213\244\354\226\221\355\225\234 \355\201\264\353\236\230\354\212\244\354\231\200 \354\235\270\355\204\260\355\216\230\354\235\264\354\212\244/2_\353\215\260\354\235\264\355\204\260 \355\201\264\353\236\230\354\212\244\354\231\200 \352\270\260\355\203\200 \355\201\264\353\236\230\354\212\244/README.md" similarity index 100% rename from "Chapter07_\353\213\244\354\226\221\355\225\234 \355\201\264\353\236\230\354\212\244\354\231\200 \354\235\270\355\204\260\355\216\230\354\235\264\354\212\244/2_\353\215\260\354\235\264\355\204\260 \355\201\264\353\236\230\354\212\244\354\231\200 \352\270\260\355\203\200 \355\201\264\353\236\230\354\212\244/README.md" rename to "Chapter02_\352\260\235\354\262\264 \354\247\200\355\226\245 \355\224\204\353\241\234\352\267\270\353\236\230\353\260\215/07_\353\213\244\354\226\221\355\225\234 \355\201\264\353\236\230\354\212\244\354\231\200 \354\235\270\355\204\260\355\216\230\354\235\264\354\212\244/2_\353\215\260\354\235\264\355\204\260 \355\201\264\353\236\230\354\212\244\354\231\200 \352\270\260\355\203\200 \355\201\264\353\236\230\354\212\244/README.md" diff --git "a/Chapter07_\353\213\244\354\226\221\355\225\234 \355\201\264\353\236\230\354\212\244\354\231\200 \354\235\270\355\204\260\355\216\230\354\235\264\354\212\244/3_\354\227\260\354\202\260\354\236\220 \354\230\244\353\262\204\353\241\234\353\224\251/README.md" "b/Chapter02_\352\260\235\354\262\264 \354\247\200\355\226\245 \355\224\204\353\241\234\352\267\270\353\236\230\353\260\215/07_\353\213\244\354\226\221\355\225\234 \355\201\264\353\236\230\354\212\244\354\231\200 \354\235\270\355\204\260\355\216\230\354\235\264\354\212\244/3_\354\227\260\354\202\260\354\236\220 \354\230\244\353\262\204\353\241\234\353\224\251/README.md" similarity index 100% rename from "Chapter07_\353\213\244\354\226\221\355\225\234 \355\201\264\353\236\230\354\212\244\354\231\200 \354\235\270\355\204\260\355\216\230\354\235\264\354\212\244/3_\354\227\260\354\202\260\354\236\220 \354\230\244\353\262\204\353\241\234\353\224\251/README.md" rename to "Chapter02_\352\260\235\354\262\264 \354\247\200\355\226\245 \355\224\204\353\241\234\352\267\270\353\236\230\353\260\215/07_\353\213\244\354\226\221\355\225\234 \355\201\264\353\236\230\354\212\244\354\231\200 \354\235\270\355\204\260\355\216\230\354\235\264\354\212\244/3_\354\227\260\354\202\260\354\236\220 \354\230\244\353\262\204\353\241\234\353\224\251/README.md" diff --git "a/Chapter03_\354\275\224\355\213\200\353\246\260 \355\221\234\354\244\200 \353\235\274\354\235\264\353\270\214\353\237\254\353\246\254 \355\231\234\354\232\251/08 \354\240\234\353\204\244\353\246\255\352\263\274 \353\260\260\354\227\264/08-1 \354\240\234\353\204\244\353\246\255 \353\213\244\353\243\250\352\270\260/README.md" "b/Chapter03_\354\275\224\355\213\200\353\246\260 \355\221\234\354\244\200 \353\235\274\354\235\264\353\270\214\353\237\254\353\246\254 \355\231\234\354\232\251/08 \354\240\234\353\204\244\353\246\255\352\263\274 \353\260\260\354\227\264/08-1 \354\240\234\353\204\244\353\246\255 \353\213\244\353\243\250\352\270\260/README.md" new file mode 100644 index 0000000..f843df8 --- /dev/null +++ "b/Chapter03_\354\275\224\355\213\200\353\246\260 \355\221\234\354\244\200 \353\235\274\354\235\264\353\270\214\353\237\254\353\246\254 \355\231\234\354\232\251/08 \354\240\234\353\204\244\353\246\255\352\263\274 \353\260\260\354\227\264/08-1 \354\240\234\353\204\244\353\246\255 \353\213\244\353\243\250\352\270\260/README.md" @@ -0,0 +1,449 @@ +# 08-1 제네릭 다루기 + +## 1-1. 제네릭이란? + +- **제네릭(Generic)**은 클래스 내부에서 사용할 자료형을 나중에 인스턴스를 생성할 때 확정한다. +- 제네릭을 사용하면 객체의 자료형을 컴파일할 때 체크하기 때문에 객체 자료형의 안전성을 높이고 형 변환의 번거로움이 줄어듬. + +## 1-2. 제네릭의 일반적인 사용 방법 + +사용방법 + +- 제네릭을 사용하기 위해 **앵글 브래킷(<>)** 사이에 형식 매개변수를 넣어 선언하며 이때 형식 매개변수는 하나 이상 지정할 수 있다. + +장점 + +- 의도하지 않은 자료형의 객체를 지정하는 것을 막고 객체를 사용할 때 원래의 자료형에서 다른 자료형으로 형 변환 시 발생할 수 있는 오류를 줄여 준다. + +간단한 제네릭의 예 + +```kotlin +class Box (t: T){ //형식 매개변수로 받은 인자를 name에 저장 + var name = t +} + +fun main(){ + val box1 : Box = Box(1) + val box2 : Box = Box("Hello") +println(box1.name) +println(box2.name) +} +``` + +실행 결과 + + 1 + + Hello + +- 제네릭에서 사용하는 형식 매개변수 이름 (Box에서 T가 바로 형식 매개변수 이름) + +| 형식 매개변수 이름 | 의미 | +| --- | --- | +| E | 요소(Element) | +| K | 키(key) | +| N | 숫자(Number) | +| T | 형식(Type) | +| V | 값(Value) | +| S, U, V etc. | 두 번째 , 세 번째, 네 번째 형식 | +- 객체 생성시 만일 생성자 에서 유추될 수 있는 자료형이 있다면 선언된 자료형인 이나 는 생략이 가능 + +```kotlin +val box3 = Box(1) // 1은 int형이므로 Box로 추론 +val box4 = Box("Hello") // Hello는 String형이므로 Box으로 추론 +``` + +제네릭을 사용하면 인자의 자료형을 고정할 수 없거나 예측할 수 없을 때 형식 매개변수인 T를 이용해 실행 시간에 자료형을 결정할 수 있게 되므로 매우 편리 + +- **제네릭 클래스** : 형식 매개변수를 1개 이상 받는 클래스. 클래스를 선언할 때 자료형을 특정하지 않고 인스턴스를 생성하는 시점에서 클래스의 자료형을 정하는 것 + + ex) Box + + +- 자료형이 특정되지 못하므로 인스터를 생성할 수 없음 → 만일 형식 매개변수를 클래스의 프로퍼티에 사용하는 경우 클래스 내부에서는 사용할 수 없다. + +```kotlin +class Myclass{ + var myProp : T // 오류) 프로퍼티는 초기화되거나 abstarct로 선언되어야 한다. +} +``` + +다음과 같이 주 생성자나 부 생성자에 형식 매개변수를 지정해 사용할 수 있다. + +```kotlin +class MyClass(val myProp : T){} //주 생성자의 프로퍼티 +``` + +```kotlin +class MyClass{ + val myProp : T // 프로퍼티 + constructor(myProp : T){ //부 생성자 이용 + this.myProp = myProp + } +} +``` + +그러면 객체 인스턴스를 생성할 때 명시적으로 자료형을 지정할 수 있다. + +```kotlin +var a = MyClass(12) //주 생성자 myProp에는 12가 할당되며 int형으로 결정됨 +println(a.myProp) //12 +println(a.javaClass) //MyClass +``` + +- 제네릭 클래스의 자료형 변환하기 + +```kotlin +open class Parent + +class Child : Parent() + +class Cup + +fun main() { + val obj1: Parent = Child() // Parent 형식의 obj1은 Child의 자료형으로 변환될 수 있음 + val obj2: Child = Parent() // 오류! 자료형 불일치 + + val obj3: Cup = Cup() // 오류! 자료형 불일치 + val obj4: Cup = Cup() // 오류! 자료형 불일치 + + val obj5 = Cup() // obj5는 Cup의 자료형이 됨 + val obj6: Cup = obj5 // 자료형이 일치하므로 OK! +} +``` + +이처럼 상,하위 클래스를 형식 매개변수에 지정해 서로의 관계에 따라 변환이 가능하게 하려면 제네릭의 가변성을 주기 위해 in,out에 대해 이해해야 한다. + +- 제네릭의 형식 매개변수는 기본적으로 null 가능한 형태로 선언됨. + +```kotlin +class GenericNull { //기본적으로 null이 허용되는 형식 매개변수 + fun eqaulityFunc(arg1 : T, arg2 : T){ +println(arg1?.equals(arg2)) + } +} + +fun main() { + val obj = GenericNull() // non-null로 선언 + obj.eqaulityFunc("Hello","World") //null이 허용되지 않음 + + val obj2 = GenericNull() // null이 가능한 형식으로 선언 + obj2.eqaulityFunc(null,10) +} +``` + +실행 결과 + +false + +null + +- 형식 매개변수 T는 기본적으로 null을 허용 (자료형에 ? 기호를 사용). 그래서 obj2에서 equals()로 비교하지 않고 null인 경우 null을 반환함. + +- 형식 매개변수에 특정 자료형을 지정 (null 허용을 제한) + +```kotlin +class GenericNull { //자료형 Any가 지정되어 null을 허용하지 않음 +} + +fun main() { + val obj2 = GenericNull() // 오류! null이 허용되지 않음 +} +``` + +- **제네릭 함수 혹은 메서드** : 형식 매개변수를 받는 함수나 메서드 + + fun<형식 매개변수[,…]> 함수 이름(매개변수 : <매개변수 자료형>[,….]): <반환 자료형> + + ex) + + ```kotlin + fun genericFunc(arg: T):T? {....} // 매개변수와 반환 자료형에 형식 매개변수 T가 사용됨 + + fun put(key:K, value: V): Unit{....} // 형식 매개변수가 2개인 경우 + ``` + + +- 형식 매개변수로 선언된 함수의 매개변수를 연산할 경우 자료형을 결정할 수 없기 때문에 오류 + +```kotlin +fun add(a: T, b: T): T { + return a + b //오류! +} +``` + +→ 람다식을 매개변수로 받으면 손쉽게 해결 + +```kotlin +fun add(a: T, b: T, op: (T, T) -> T): T { + return op(a, b) +} + +fun main() { + val result =add(2, 3,{a, b->a + b}) //add(2,3){a,b -> a+b}와 같음 + println(result) //5 +} +``` + +→ 람다식만 변수로 따로 정의해 add() 함수의 인자로 넣은 경우 + +```kotlin +var sumInt: (Int,Int) -> Int ={a,b->a+b}// 변수 선언부가 있는 경우 표현식의 자료형 생략 +var sumInt2 ={a: Int, b: Int->a+b}// 변수 선언부가 생략된 경우에는 표현식에 자료형 표기 + +println(add(2,3,sumInt)) +println(add(2,3,sumInt2)) +``` + +→ 람다식 매개변수를 typealias를 사용해 다른 이름으로 준 경우 + +```kotlin +typealias arithmetic = (T, T) -> T + +fun addAux(a: T, b: T, op: arithmetic): T { + return op(a, b) +} +``` + +## 1-3. 자료형 제한하기 + +자바에서는 extends나 super를 사용 → 코틀린에서는 콜론(:)을 사용해 제네릭 클래스나 메서드가 받는 형식 매개변수를 특정한 자료형으로 제한 + +- **클래스에서 형식 매개변수의 자료형 제한하기** + +```kotlin +class Calc{// 클래스의 형식 매개변수 제한 + fun plus(arg1: T, arg2: T): Double{ + return arg1.toDouble() + arg2.toDouble() + } +} + +fun main() { + val calc = Calc() +println(calc.plus(10,20)) //30.0 + + val calc2 = Calc() + val calc3 = Calc() + val calc4 = Calc //제한된 자료형으로 인해 오류 발생 + +println(calc2.plus(2.5,3.5)) //6.0 +println(calc3.plus(5L,10L)) //15.0 +} +``` + +- **함수에서 형식 매개변수의 자료형 제한하기** + +```kotlin +fun addLimit(a: T, b: T, op: (T, T) ->): T { + return op(a, b) +} + +valresult=addLimit("abc", "def",{a, b->a + b}) //제한된 자료형이므로 오류 +``` + +- **다수 조건의 형식 매개변수 제한하기(where 키워드 사용) : 클래스** + +```kotlin +interface InterfaceA +interface InterfaceB + +class HandlerA : InterfaceA, InterfaceB +class HandlerB : InterfaceA + +class ClassA where T:InterfaceA, T:InterfaceB // 2개의 인터페이스를 구현하는 클래스로 제한 + +fun main() { + val obj1 = ClassA() // 객체 생성 가능 + val obj2 = ClassA() // 범위에 없으므로 오류 발생(1개의 인터페이스) +} +``` + +- **다수 조건의 형식 매개변수 제한하기(where 키워드 사용) : 함수** + +```kotlin +//myMax의 인자 a,b에 들어갈 자료형을 숫자형과 비교형만으로 제한 +fun myMax(a: T, b: T): T where T : Number, T : Comparable { + return if (a > b) a else b +} +``` + +## 1-4. 상 · 하위 형식의 가변성 + +**가변성** : 형식 매개변수가 클래스 게층에 영향을 주는 것 + +**하위 형식 :** 형식 A의 값이 필요한 모든 클래스에 형식 B의 값을 넣어도 아무 문제가 없는 경우 B는 A의 하위 형식(SubType)이 됨. + +- **가변성의 세가지 유형** + +| 용어 | 의미 | +| --- | --- | +| 공변성(Covariance) | ‘T’가 T의 하위 자료형이면, C<’T’>는 C의 하위 자료형이다. 생산자 입장의 out 성질 | +| 반공변성(Contravariance) | ‘T’가 T의 하위 자료형이면, C<’T’>는 C의 하위 자료형이다. 소비자 입장의 in 성질 | +| 무변성(Invariance) | C와 C<’T’>는 아무 관계가 없다. 생산자 + 소비자 | + +![Untitled](https://user-images.githubusercontent.com/48742378/190898640-d97f8b4a-5f6c-4045-8a5b-8a2c458118c3.png) + +- **무변성 :** 제네릭 클래스를 인스턴스화할 때 서로 다른 자료형을 인자로 사용하려면 자료형 사이의 상,하위 관계를 잘 따져야 한다. (in,out 키워드 사용) + +```kotlin +class Box (val size: Int) // 무변성(Invariance) 선언 + +fun main() { + val anys: Box = Box(10) // 오류! 자료형 불일치 + val nothings: Box = Box(20) // 오류! 자료형 불일치 +} +``` + +- **공변성 :** 형식 매개변수의 상하 자료형 관계가 성립하고, 그 관계가 그대로 인스턴스 자료형의 관계로 이어지는 경우. out 키워드를 사용 + +```kotlin +class Box (val size: Int) // 공변성(Invariance) 선언 + +fun main() { + val anys: Box = Box(10) // 관계 성립으로 객체 생성 가능 + val nothings: Box = Box(20) // 오류! 자료형 불일치 +} +``` + +- **반공변성 :** in 키워드를 사용 + +```kotlin +class Box (val size: Int) // 반공변성(Invariance) 선언 + +fun main() { + val anys: Box = Box(10) // 오류! 자료형 불일치 + val nothings: Box = Box(20) // 관계 성립으로 객체 생성 가능 +} +``` + +```kotlin +// 주생성자에서 out을 사용하는 경우에 형식 매개변수를 갖는 프로퍼티는 var로 지정될 수 없음 +(val)사용 +class Box (val elem: T) +class Box2 (private var elem: T) //private로 지정하면 사용가능 +``` + +## 1-5. 자료형 프로젝션 + +- **가변성의 2가지 방법** + - **선언 지점 변성** + - 클래스를 선언하면서 클래스 자체에 가변성을 지정하는 방식(클래스에 in/out을 지정할 수 있다) → 전체적으로 지정하는 것이 되기 때문에 클래스를 사용하는 장소에서는 따로 자료형을 지정해 줄 필요가 없어 편리하다. + + ```kotlin + class Box(var item: T) + ``` + + - **사용 지점 변성** + - 메서드 매개변수에서 또는 제네릭 클래스를 생성할 때와 같이 사용 위치에서 가변성을 지정하느 방식 + + ```kotlin + class Box(var item: T) // in, out을 지정하지 않아 무변성 + + fun printObj(box: Box) { //자료형 프로젝션(특정 자료형에 in 혹은 out을 지정해 제한 + val obj: Animal = box.item // item의 값을 얻음(get) + println(obj) + } + ``` + + - 자료형 안전성을 보장, 그리고 out에 의한 게터만 허용하고 in에 의한 세터는 금지 + + ```kotlin + fun main() { + val animal : Box = Box(Animal()) + val cat : Box = Box(Cat()) + + println(animal) // 가능! + println(cat) //오류 Box<>는 무변성으로 지정되었기 때문에 cat은 animal의 하위타입 + } + ``` + + +- **스타 프로젝션 :** in과 out을 정하지 않고 *을 통해 지정하는 방식 + - Box 차이점 : Any타입은 모든 자료형의 요소를 담을 수 있음을 의미하는 반면, *는 어떤 자료형이라도 들어올 수 있으나 구체적으로 자료형이 결정되고 난 후에는 그 자료형과 하위 자료형의 요소만 담을 수 있도록 제한. + + ```kotlin + class InOutTest(t: T, u: U){ + val propT: T = t // 오류! T는 in 위치이기 때문에 out 위치에 사용 불가 + val propU: U = u // U는 out 위치로 가능 + + fun func1(u: U) // 오류! U는 out 위치이기 때문에 in 위치에 사용 불가 + fun func2(t: T){ // T는 in 위치에 사용됨 + print(t) + } + } + + fun starTestFunc(v: InOutTest<*,*>){ + v.func2(1) // in으로 정의되어 있는 형식 매개변수를 *로 받으면 in Nothing으로 간주 out은 Any?로 간주 + print(v.propU) + } + ``` + + - **Nothing 클래스 :** 코틀린의 최하위 자료형으로 아무것도 가지지 않는 클래스 + +- **자료형 프로젝션의 정리** + +| 종류 | 예 | 가변성 | 제한 | +| --- | --- | --- | --- | +| out 프로젝션 | Box | 공변성 | 형식 매개변수는 세터를 통해 값을 설정하는것이 제한된다. | +| in 프로젝션 | Box | 반공변성 | 형식 매개변수는 게터를 통해 값을 읽거나 반환할 수 있다. | +| 스타 프로젝션 | Box<*> | 모든 인스턴스는 하위 형식이 될 수 있다. | in과 out은 사용 방법에 따라 결정된다. | + +## 1-6. reified 자료형 + +```kotlin +// T 자료형은 자바처럼 실행 시간에 삭제되기 때문에 T 자체에 그래도 접근할 수 없음 +// 처럼 결정되지 않은 제네릭 자료형은 컴파일 시간에는 접근 가능 +// 함수 내부에서 사용하려면 함수의 매개변수를 넣어 c:Class처럼 지정해야만 실행 시간에 삭제되지 않고 접근 가능 +fun myGenericFun(c: Class) // Class : .class 형태로 반환 받는 객체 +코틀린 : Object::class //KClass +자바 : Object::class.java //Class +``` + +→ reified로 형식 매개변수 T를 지정하면 실행 시간에 접근할 수 있다. + +```kotlin +inline fun myGenericFun() //인라인 함수에서만 사용 가능 +``` + +ex) + +```kotlin +fun String.toKotlinObject(): T { + val mapper = jacksonObjectMapper() + return mapper.readValue(this, T::class.java) //오류! +} //readValue()는 JsonObject로 변환해 읽을 수 있게 하는 함수 +``` + +→ 명시적으로 클래스 매개변수를 이용한 경우 + +```kotlin +fun String.toKotlinObject(c:KClass): T { + val mapper = jacksonObjectMapper() + return mapper.readValue(this, c.java) //오류! +} +``` + +```kotlin +data class MyJsonType(val name: String) + +fun main() { + val json = """{"name":"example"}""" + json.toKotlinObject(MyJsonType::class) +} +``` + +→ reified 사용 + +```kotlin +inline fun String.toKotlinObject(): T { + val mapper = jacksonObjectMapper() + return mapper.readValue(this, T::class.java) +} + +data class MyJsonType(val name: String) + +fun main() { + val json = """{"name":"example"}""" + json.toKotlinObject() +} +``` diff --git "a/Chapter03_\354\275\224\355\213\200\353\246\260 \355\221\234\354\244\200 \353\235\274\354\235\264\353\270\214\353\237\254\353\246\254 \355\231\234\354\232\251/08 \354\240\234\353\204\244\353\246\255\352\263\274 \353\260\260\354\227\264/08-1 \354\240\234\353\204\244\353\246\255 \353\213\244\353\243\250\352\270\260/asset/covariance_diagram.png" "b/Chapter03_\354\275\224\355\213\200\353\246\260 \355\221\234\354\244\200 \353\235\274\354\235\264\353\270\214\353\237\254\353\246\254 \355\231\234\354\232\251/08 \354\240\234\353\204\244\353\246\255\352\263\274 \353\260\260\354\227\264/08-1 \354\240\234\353\204\244\353\246\255 \353\213\244\353\243\250\352\270\260/asset/covariance_diagram.png" new file mode 100644 index 0000000..76b0b3b Binary files /dev/null and "b/Chapter03_\354\275\224\355\213\200\353\246\260 \355\221\234\354\244\200 \353\235\274\354\235\264\353\270\214\353\237\254\353\246\254 \355\231\234\354\232\251/08 \354\240\234\353\204\244\353\246\255\352\263\274 \353\260\260\354\227\264/08-1 \354\240\234\353\204\244\353\246\255 \353\213\244\353\243\250\352\270\260/asset/covariance_diagram.png" differ diff --git "a/Chapter03_\354\275\224\355\213\200\353\246\260 \355\221\234\354\244\200 \353\235\274\354\235\264\353\270\214\353\237\254\353\246\254 \355\231\234\354\232\251/08 \354\240\234\353\204\244\353\246\255\352\263\274 \353\260\260\354\227\264/08-2 \353\260\260\354\227\264 \353\213\244\353\243\250\352\270\260/README.md" "b/Chapter03_\354\275\224\355\213\200\353\246\260 \355\221\234\354\244\200 \353\235\274\354\235\264\353\270\214\353\237\254\353\246\254 \355\231\234\354\232\251/08 \354\240\234\353\204\244\353\246\255\352\263\274 \353\260\260\354\227\264/08-2 \353\260\260\354\227\264 \353\213\244\353\243\250\352\270\260/README.md" new file mode 100644 index 0000000..458ff5e --- /dev/null +++ "b/Chapter03_\354\275\224\355\213\200\353\246\260 \355\221\234\354\244\200 \353\235\274\354\235\264\353\270\214\353\237\254\353\246\254 \355\231\234\354\232\251/08 \354\240\234\353\204\244\353\246\255\352\263\274 \353\260\260\354\227\264/08-2 \353\260\260\354\227\264 \353\213\244\353\243\250\352\270\260/README.md" @@ -0,0 +1,208 @@ +# 08-2 배열 다루기 + +## 2-1. 배열을 사용하는 방법 + +- **기본적인 배열 표현** + +```kotlin +val numbers =arrayOf(4,5,6,7) //정수형으로 초기화된 배열 +val animals =arrayOf("Cat","Dog","Lion") //문자열형으로 초기화된 배열 +``` + +- **다차원 배열** + +```kotlin +val array1 =arrayOf(1,2,3) +val array2 =arrayOf(4,5,6) +val array3 =arrayOf(7,8,9) + +val arr2d =arrayOf(array1,array2,array3) + +val arr2d = *arrayOf*( *arrayOf*(1,2,3),*arrayOf*(4,5,6),*arrayOf*(7,8,9)) //간결한 표현 + +var arr3d = arrayOf(arrayOf(arrayOf(....),....),...) //3차원 배열 +``` + +- **배열에 여러 가지 자료형 혼합하기** + +```kotlin +val mixArr =arrayOf(4, 5, 7, 3, "Chike", false) + +//데이터를 혼합해서 사용할 수 없는 경우 +val intOnlyArr1 =arrayOf(4, 5, 7, 3) +val intOnlyArr2 =intArrayOf(4, 5, 7, 3) +``` + +- **배열 요소에 접근하기** + - 배열 요소에 접근 : arr.get() = arr[] + - 배열 요소에 값 설정 : arr.set(인덱스 위치, 값), arr[] = ? + - 배열의 추가 메소드 + + ```kotlin + val arr =intArrayOf(1,2,3,4,5) + + println(Arrays.toString(arr)) //배열의 내용을 문자열로 변환 : 자바와 동일 + println(arr.size) //배열의 크기 + println(arr.sum()) //배열의 합 + ``` + + +- **표현식을 통해 배열 생성하기** + +```kotlin +//val|var 변수 이름 = Array(요소 개수, 초깃값) +val arr3 = Array(5,{i->i*2}) //배열의 크기 5 i번째 인덱스 * 2 값을 배열에 저장 +``` + +- **요소 개수가 많은 배열 생성** + +```kotlin +var a = arrayOfNulls(1000) //1000개의 null로 채워진 정수 배열 + +var b = Array(1000){0}//0으로 채워진 배열 +``` + +## 2-2. 배열 제한하고 처리하기 + +- **배열에 요소 추가하고 잘라내기** + +```kotlin +val arr1 =intArrayOf(1, 2, 3, 4, 5) +val arr2 = arr1.plus(6) //하나의 요소를 추가한 새 배열 생성 (1, 2, 3, 4, 5, 6) +val arr3 = arr1.sliceArray(0..2) //1번째부터 3번째까지의 인덱스 (1, 2, 3) +``` + +- **기타 배열 관련 API 사용하기** + +```kotlin +println(arr.first()) //첫 번째 요소 +println(arr.last()) //마지막 요소 + +println(arr.indexOf(3)) //3번째 인덱스 요소 + +println(arr.average()) //배열의 평균값 + +println(arr.count) //요소 개수 + +println(arr.contains(4)) //해당 value가 배열에 포함되어 있는지 boolean형 반환 +// arr.contains(4) = 4 in arr +``` + +- **다양한 자료형을 위한 Any로 선언된 배열** + + Any로 만들어진 배열은 기존 자료형을 다른 자료형으로 지정할 수 있다. + + ```kotlin + val b = Array(10,{0}) + b[0] = "Hello World" + b[1] = 1.1 + println("${b[0]} ${b[1]} ${b[2]}") //Hello World 1.1 0 + ``` + +- **멤버 메서드를 통한 배열 순환하기** + +```kotlin +arr.forEach{element->print("$element")}//foreach 문 +arr.forEachIndexed({i,e->println("arr[$i] = $e")}) //foreach문에서 인덱스값도 표시 +``` + +```kotlin +//iterator() 사용 +val iter: Iterator = arr.iterator() +while (iter.hasNext()) { //배열에서 참조할 다음 요소가 있는지 확인 + val e = iter.next() + print("$e ") +} +``` + +## 2-3. 배열 정렬하기 + +- **기본 배열 정렬하고 반환하기** + +```kotlin +val arr =arrayOf(8, 4, 3, 2, 5, 9, 1) + +val sortedNums = arr.sortedArray() //오름차순 정렬 +val sortedNumsDesc = arr.sortedArrayDescending() //내림차순 정렬렬 + +arr.sort(1, 3) //1번째 인덱스부터 2번째 인덱스까지 정렬 (8,3,4,2,5,9,1) +arr.sortDescending() //내림차순 정렬 + +//List로 반환 +val listSorted: List = arr.sorted() +val listDesc: List = arr.sortedDescending() + +//SortBy를 이용한 특정 표현식에 따른 정렬 +val items =arrayOf("Dog","Cat","Lion","Kangaroo","Po") +items.sortBy{items->items.length}//문자열 길이 기준으로 정렬 +``` + +- **sortBy()로 데이터 클래스 정렬하기** + +```kotlin +val product =arrayOf( + Product("Sonw Ball",870), + Product("Smart Phone",999), + Product("Drone",240), + Product("Mouse",333), + Product("Keyboard",125), + Product("Monitor",1500), + Product("Tablet",512) +) + +product.sortByDescending{ it.price}//가격을 기준으로 내림차순 정렬 +``` + +- **sortWith() 비교자로 정렬하기** + +```kotlin +//Comparator 사용 +product.sortWith(Comparator{p1, p2-> +when{ + p1.price > p2.price -> 1 + p1.price == p2.price -> 0 + else -> -1 + } +}) + +//compareBy 사용 +//이름을 기준으로 정렬하고 만약 같으면 가격 기준으로 정렬 +product.sortWith(compareBy{ it.name}.thenBy{ it.price}) +product.sortWith(compareBy({it.name},{it.price})) +``` + +- **배열 필터링하기** + +```kotlin +val arr =arrayOf(1, -2, 3, 4, -5, 0) +arr.filter{e->e > 0}.forEach{e->print("$e")}//0보다 큰 수 골라내기 +``` + +```kotlin +val fruits =arrayOf("banana", "avocado", "apple", "kiwi") +fruits //메서드 체이닝 : 메서드를 게속 연속해서 호출하는 방법 + .filter{ it.startsWith("a")}//a로 시작하는 + .sortedBy{ it }//이름순으로 정렬 + .map{ it.uppercase()}//대문자로 변경 + .forEach{println(it)} + +when{ //when문을 통해 배열에서 특정 요소가 있는지 확인하는 방법 위에 메서드 체이닝기법을 사용했을 경우 특정 메서드에서 디버깅이 어렵기때문에 사용 + "apple" in fruits -> println("Apple!") +} +``` + +```kotlin +println(products.minBy{it.price}) //최솟값 +println(products.maxBy{it.price}) //최대값 +``` + +## 2-4. 배열 평탄화하기 + +```kotlin +val numbers =arrayOf(1, 2, 3) +val strs =arrayOf("one", "two", "three") +val simpleArray =arrayOf(numbers, strs) //2차원 배열 + +val flatternSimpleArray = simpleArray.flatten() //단일 배열로 변환하기 +println(flatternSimpleArray) //Ljaba.lang.~~가 출력 X -> 1,2,3,one,two,three +``` \ No newline at end of file diff --git "a/Chapter03_\354\275\224\355\213\200\353\246\260 \355\221\234\354\244\200 \353\235\274\354\235\264\353\270\214\353\237\254\353\246\254 \355\231\234\354\232\251/08 \354\240\234\353\204\244\353\246\255\352\263\274 \353\260\260\354\227\264/08-3 \353\254\270\354\236\220\354\227\264 \353\213\244\353\243\250\352\270\260/README.md" "b/Chapter03_\354\275\224\355\213\200\353\246\260 \355\221\234\354\244\200 \353\235\274\354\235\264\353\270\214\353\237\254\353\246\254 \355\231\234\354\232\251/08 \354\240\234\353\204\244\353\246\255\352\263\274 \353\260\260\354\227\264/08-3 \353\254\270\354\236\220\354\227\264 \353\213\244\353\243\250\352\270\260/README.md" new file mode 100644 index 0000000..1a838af --- /dev/null +++ "b/Chapter03_\354\275\224\355\213\200\353\246\260 \355\221\234\354\244\200 \353\235\274\354\235\264\353\270\214\353\237\254\353\246\254 \355\231\234\354\232\251/08 \354\240\234\353\204\244\353\246\255\352\263\274 \353\260\260\354\227\264/08-3 \353\254\270\354\236\220\354\227\264 \353\213\244\353\243\250\352\270\260/README.md" @@ -0,0 +1,109 @@ +# 08-3 문자열 다루기 + +## 3-1. 문자열의 기본 처리 + +```kotlin +val hello : String = "Hello World" +println(hello[0]) //첫 번째 요소 출력 + +hello[0] = 'K' //오류! var나 val에서 하나의 요소에 새 값을 할당할 수 없음 +var s = "abcdef" +s = "xyz" //새로운 메모리 공간이 생성 기존 사용하던 메모리 공간은 GC(Garbage Collector)에 의해 제거 + +GC : 더 이상 사용하지 않게 된 객체를 메모리에서 자동적으로 제거하는 역할 +``` + +- **문자열 추출하고 병합하기** + +```kotlin +var s = "abcdef" +println(s.substring(0..2)) // abc +s = s.substring(0..1) + "x" + s.substring(3..s.length-1) // abxdef +``` + +- **문자열 비교하기** + +```kotlin +var s1 = "Hello Kotlin" +var s2 = "Hello KOTLIN" +//같으면 0, s1 < s2이면 양수, 반대면 음수를 반환 +println(s1.compareTo(s2)) //32 +println(s1.compareTo(s2,true)) //대소문자 무시 0 +``` + +- **StringBulder 사용하기** + +StringBuilder를 사용하면 문자열이 사용할 공간을 좀 더 크게 잡을 수 있기 때문에 특정 요소를 변경할 수 있다. 단 기존의 문자열보다는 처리 속도가 좀 느리고, 만일 단어를 변경하지 않고 그대로 사용하면 임시 공간인 메모리를 조금 더 사용하게 되므로 낭비된다는 단점도 존재. + +→ 문자열이 자주 변경되는 경우에 사용하면 좋다. + +```kotlin +var s = StringBuilder("Hello") +s[2] = 'x' +``` + +```kotlin +s.append("World") //HexloWorld +s.insert(10,"Added") //HexloWorldAdded +s.delete(5,10) //인덱스 5번부터 10번 까지 삭제 HexloAdded +``` + +- **기타 문자열 처리** + +```kotlin +var deli = "Welcome to Kotlin" +val sp = deli.split(" ") //공백을 기준으로 split : Welcome, to, Kotlin + +str.split("=","-") //하나 이상의 분리 문자 지정 +``` + +- **문자열을 정수로 변환하기** + +```kotlin +val number : Int = "123".toInt() // 자바에서는 Integer.parseInt(number) +toIntorNull() : 숫자가 아닌 문자열이 들어왔을 때 null로 반환 +``` + +## 3-2. 리터럴 문자열 + +- **이스케이프 문자의 종류** + +| 종류 | | | +| --- | --- | --- | +| \t 탭 | \r 캐리지 리턴 | \\ 백슬래시 | +| \b 백스페이스 | \’ 작은따옴표 | \$ 달러 기호 | +| \n 개행 | \” 큰따옴표 | | + +```kotlin +val str = "\tYou're just too \"good\" to be true\n\tI can't take my eyes off you." +//You're just too "good" to be true +//I can't take my eyes off you. +val uni = "\uAC00" //가 +``` + +```kotlin +//3중 따옴표를 사용하면 이스케이프 문자 중 개행 문자를 넣지 않고도 개행 표시 가능 +val text = """ / + |Tell me and I forget. + |Teach me and I remember. + |Involve me and I learn. + |(Benjamin Franklin) +""".trimMargin() //trim default는 | : | 기준으로 개행문자 +``` + +- **형식 문자 사용하기** + - 형식 문자의 종류 + + | 종류 | | | + | --- | --- | --- | + | %b 참과 거짓의 Boolean 유형 | %o 8진 정수 | %g 10진 혹은 E 표기법의 실수 | + | %d 부호 있는 정수 | %t 날짜나 시간 | %n 줄 구분 | + | %f 10진 실수 | %c 문자 | %s 문자열 | + | %h 해시 코드 | %e E 표기법의 실수 | %x 16진 정수 | + +```kotlin +val pi = 3.1415926 +val dec = 10 +val s = "hello" +println("pi = %.2f, %3d, %s".format(pi,dec,s)) //3.14 ( 10) hello +``` \ No newline at end of file diff --git "a/Chapter03_\354\275\224\355\213\200\353\246\260 \355\221\234\354\244\200 \353\235\274\354\235\264\353\270\214\353\237\254\353\246\254 \355\231\234\354\232\251/08 \354\240\234\353\204\244\353\246\255\352\263\274 \353\260\260\354\227\264/README.md" "b/Chapter03_\354\275\224\355\213\200\353\246\260 \355\221\234\354\244\200 \353\235\274\354\235\264\353\270\214\353\237\254\353\246\254 \355\231\234\354\232\251/08 \354\240\234\353\204\244\353\246\255\352\263\274 \353\260\260\354\227\264/README.md" new file mode 100644 index 0000000..3e35da8 --- /dev/null +++ "b/Chapter03_\354\275\224\355\213\200\353\246\260 \355\221\234\354\244\200 \353\235\274\354\235\264\353\270\214\353\237\254\353\246\254 \355\231\234\354\232\251/08 \354\240\234\353\204\244\353\246\255\352\263\274 \353\260\260\354\227\264/README.md" @@ -0,0 +1,7 @@ +# Chapter03-8. 제네릭과 배열 +- 발표일 : 2022/09/21 +- 발표자 : 조수연 +

+ + + diff --git "a/Chapter03_\354\275\224\355\213\200\353\246\260 \355\221\234\354\244\200 \353\235\274\354\235\264\353\270\214\353\237\254\353\246\254 \355\231\234\354\232\251/09_Collection/09-1 \354\273\254\353\240\211\354\205\230\354\235\230 \352\265\254\354\241\260\354\231\200 \352\270\260\353\263\270.md" "b/Chapter03_\354\275\224\355\213\200\353\246\260 \355\221\234\354\244\200 \353\235\274\354\235\264\353\270\214\353\237\254\353\246\254 \355\231\234\354\232\251/09_Collection/09-1 \354\273\254\353\240\211\354\205\230\354\235\230 \352\265\254\354\241\260\354\231\200 \352\270\260\353\263\270.md" new file mode 100644 index 0000000..0a491a3 --- /dev/null +++ "b/Chapter03_\354\275\224\355\213\200\353\246\260 \355\221\234\354\244\200 \353\235\274\354\235\264\353\270\214\353\237\254\353\246\254 \355\231\234\354\232\251/09_Collection/09-1 \354\273\254\353\240\211\354\205\230\354\235\230 \352\265\254\354\241\260\354\231\200 \352\270\260\353\263\270.md" @@ -0,0 +1,44 @@ + +# Collection + +### 컬렉션이란 자주 사용하는 기초적인 자료구조를 모아 놓은 일종의 프레임워크로 표준 라이브러리로 제공되고 있다. +_자바에서도 같이 제공되고 있음_ + +
+ +--- + +Collection에는 List, Set, Map이 있음 + +![image](https://user-images.githubusercontent.com/74912130/191014898-8ca2253e-e822-4322-b558-f9059cbe66c7.png) + +- 자바와 다르게 불변형(immutable)과 가변형(mutable)으로 나뉜다. +- 가변형은 크기가 정해져있지 않아 동적으로 계속 증가하는 것 +- 불변형은 자바에서 배열의 크기를 선언하는 것처림 정해진 크기를 넘어설 수 없도록 만들어지는 것 + +
+ +![화면 캡처 2022-09-19 212025](https://user-images.githubusercontent.com/74912130/191015829-d9f5d0ef-f1da-42d9-af20-2e013169a011.jpg) + + +list를 만들때, var를 쓰나 val을 쓰나 list를 재정의하는 것이 아니라 값을 넣는 것이라면, 상관 없이 수정이 가능함 +때문에 Collection에서는 val을 쓰도록 권장하고 있음 + + +![image](https://user-images.githubusercontent.com/74912130/191016805-fcd655bb-8cf1-4e6d-9c4e-173072757c49.png) + + +다이어그램 가장 상위에 잇는 Iterable 인터페이스는 컬렉션이 연속적인 요소를 표현할 수 있도록 한다. +_(자바의 Iterator개념과 비슷함)_ + + +iterator()는 hasNext()와 next() 메소드를 통해서 요소를 순환한게 된다. + + +![화면 캡처 2022-09-21 213135](https://user-images.githubusercontent.com/74912130/191504321-5db340b0-b88c-4f98-9f24-d3b4e2e9f314.jpg) + + + + + + diff --git "a/Chapter03_\354\275\224\355\213\200\353\246\260 \355\221\234\354\244\200 \353\235\274\354\235\264\353\270\214\353\237\254\353\246\254 \355\231\234\354\232\251/09_Collection/09-2 List \355\231\234\354\232\251\355\225\230\352\270\260.md" "b/Chapter03_\354\275\224\355\213\200\353\246\260 \355\221\234\354\244\200 \353\235\274\354\235\264\353\270\214\353\237\254\353\246\254 \355\231\234\354\232\251/09_Collection/09-2 List \355\231\234\354\232\251\355\225\230\352\270\260.md" new file mode 100644 index 0000000..d6804a2 --- /dev/null +++ "b/Chapter03_\354\275\224\355\213\200\353\246\260 \355\221\234\354\244\200 \353\235\274\354\235\264\353\270\214\353\237\254\353\246\254 \355\231\234\354\232\251/09_Collection/09-2 List \355\231\234\354\232\251\355\225\230\352\270\260.md" @@ -0,0 +1,240 @@ + +--- + +# List 활용하기 + +List는 순서에 따라 정렬된 요소를 가지는 컬렉션으로 가장 많이 사용되는 컬렉션 중에 하나이다. + +헬퍼 함수를 통해서 우리는 List를 만들어서 사용할 수 있다. +> 헬퍼 함수 : List와 같은 컬렉션은 특정 함수의 도움을 통해서 생성하는데 이때 사용하는 함수를 헬퍼함수라고 한다. + + +
+ +## 불변형 List 사용해보기. + + +``` kotlin + + var list = listOf(1, 2, 3) + println(list) + // [1, 2, 3] + + list = arrayListOf(1, 2, 3) + println(list) + // [1, 2, 3] + + list = mutableListOf(1, 2, 3) + println(list) + // [1, 2, 3] + +``` + +
+ +**불변형 이지만 mixedTypes 형식 매개변수가 <Any>를 가진다.** +![image](https://user-images.githubusercontent.com/74912130/191020916-e03b022a-d2c1-4b05-a832-8039ca1a3927.png) + + +``` kotlin +var list2 = listOf("Hello", 1, 2.445, 's') +// [Hello, 1, 2.445, s] + +println(list2.javaClass) +// class java.util.Arrays$ArrayList + +``` + + +
+ +### List 순환 + + +**1. 일반적인 for문 in 사용하기** + +``` kotlin + + list = mutableListOf(1, 2, 3, 4, 5, 6, 7) + for(item in list) { + println(item) + } + +// 1 +// 2 +// 3 +// 4 +// 5 +// 6 +// 7 + + +``` + +
+ +**2. indices 사용** + +**index를 통해서 list를 접근하기 위해서는 .indices멤버를 추가하면 됩니다.** + +``` kotlin + + // 인덱스를 통한 접근 + for(index in list.indices) { + println("list[$index] = ${list[index]}") + } + +// list[0] = 1 +// list[1] = 2 +// list[2] = 3 +// list[3] = 4 +// list[4] = 5 +// list[5] = 6 +// list[6] = 7 + +``` + +
+ +### 비어있는 List생성 + + +**emptyList<>()**를 사용할 수 있다. 이때는 반드시 형식 매개변수를 지정한다. + +``` kotlin + + val emptyList : List = emptyList(); + println(emptyList) + // [] + +``` + +
+ +listOfNotNull()로 초기화하면 null을 제외한 요소만 반환해 List를 구성할 수 있다. + +``` kotlin + + // null이 아닌 요소만 출력 + val nonNullsList: List = listOfNotNull(2, 45, null, 5, null) + println(nonNullsList) + // [2, 45, 5] + +``` + +
+ + +**Collection 인터페이스의 메소드를 오버라이딩 해서 구현을 하고있다.** + +``` kotlin + + var names: List = listOf("one", "two", "three") + + println(names.size) // 리스트 사이즈 + println(names.get(0)) // 리스트 요소 가져오기 + println(names.indexOf("three")) // 해당 value와 일치하는 인덱스를 반환 + println(names.contains("two")) // 해당 value를 포함하고 있는지 여부 확인 + +// 3 +// one +// 2 +// true + + +``` + +코틀린에서는 get()을 굳이 쓰지 않고 배열처럼 []를 통해서도 값을 집어넣거나 불러올 수 있습니다. + +``` kotlin + + println(names[0]) + // one + +``` + +
+ +--- + +## 가변형 List 생성하기 + + +**가변형 arrayListOf()** + + + +``` kotlin + + val stringList = arrayListOf("Hello", "Kotlin", "Java") + stringList.add("Java") + stringList.remove("Hello") + println("stringList : ${stringList}") +// stringList : [Kotlin, Java, Java] + +``` + +- 가변형, 크기가 정해져있지 않기 때문에 add, remove를 통해서 요소를 추가하거나 삭제할 수 있다. +- 컴파일할 때, 반환되는 자료형은 List가 아닌 자바의 ArrayList이다. + +
+ +**가변형 mutableListOf()** + +``` kotlin + + mutableList.add("Ben") + mutableList.removeAt(1) + mutableList[0] = "Sean" + println(mutableList) + // [Sean, Cheolsu, Ben] + + val mutableListMixed = mutableListOf("Android", "Apple", 5, 6, 'X') + println(mutableListMixed) + // [Android, Apple, 5, 6, X] + +``` + +
+ + +- mutableListOf는 ArrayList가 아닌 MutableList를 반환하고 있다. +- add(), removeAt()을 통해서 추가 삭제를 할 수 있다. +- set()을 통해서 요소를 바로 변경할 수 있다. +- 기존의 불변형 List를 가변형으로 변경하려면 toMutableList()를 사용할 수 있는데, 이렇게 하면 기존의 List는 그대로 두고 새로운 공간을 만들어 낸다. + +``` kotlin + + val names = listOf("one", "two", "three") + val mutableNames = names.toMutableList() + mutableNames.add("four") + println("mutableNames : ${mutableNames}") + // mutableNames : [one, two, three, four] + +``` + +
+ +**List와 배열 array의 차이** + +- List는 배열을 위해 사용한 Array와 사용 방법이 비슷하다. +- Array 클래스에 의해 생성된 배열 객체는 내부 구조상 고정된 크기의 메모리를 가지고 있다. +- 코틀린에서는 List와 MutableList는 인터페이스로 설계되어 있고 따라서 특정한 자료구조에 따라 성능이 달라진다. + + - List로 구현한 LinkedList와 ArrayList는 성능이 다르다. + + +
+ + +- List는 Array처럼 메모리 크기가 고정된 것이 아니기 때문에 자료구조에 따라 늘리거나 줄이는 것이 가능하다. + +- Array는 제네릭 관점에서 상, 하위 자료형 관계가 성립하지 않는 무변성이기 때문에 Array는 Array와 무관하다. + +- 코틀린의 MutableList도 이와 동일하다. + + - 하지만, List는 공변성이기 때문에 하위인 List가 List에 지정될 수 있다. + + + + diff --git "a/Chapter03_\354\275\224\355\213\200\353\246\260 \355\221\234\354\244\200 \353\235\274\354\235\264\353\270\214\353\237\254\353\246\254 \355\231\234\354\232\251/09_Collection/09-3 Set\352\263\274 Map\355\231\234\354\232\251\355\225\230\352\270\260.md" "b/Chapter03_\354\275\224\355\213\200\353\246\260 \355\221\234\354\244\200 \353\235\274\354\235\264\353\270\214\353\237\254\353\246\254 \355\231\234\354\232\251/09_Collection/09-3 Set\352\263\274 Map\355\231\234\354\232\251\355\225\230\352\270\260.md" new file mode 100644 index 0000000..e7037ed --- /dev/null +++ "b/Chapter03_\354\275\224\355\213\200\353\246\260 \355\221\234\354\244\200 \353\235\274\354\235\264\353\270\214\353\237\254\353\246\254 \355\231\234\354\232\251/09_Collection/09-3 Set\352\263\274 Map\355\231\234\354\232\251\355\225\230\352\270\260.md" @@ -0,0 +1,250 @@ + +
+ +# Set과 Map활용하기 +--- + +Set은 정해진 순서가 없는 요소들의 집합, 중복값이 없음 즉, 모든 요소가 unique함 + +Map은 요소가 키와 값의 쌍 형태로 저장된다. 키는 중복될 수 없고 유일합니다. 하지만 값은 중복해서 사용할 수 있다. + + +
+ +--- +## Set 활용 + +Set은 setOf()를 사용해서 Set을 생성하고, mutableSetOf()를 이용해서 가변형 Set을 생성할 수 있다. + +**불변형 setOf()** + +``` kotlin + + val mixedTypeSet = setOf("Hello", "5", 1, 3.14) + println(mixedTypeSet) + // [Hello, 5, 1, 3.14] + + + + val inSet: Set = setOf(1, 2, 5, 5, 5) + println(inSet) + // [1, 2, 5] + +``` + +setOf에서는 자료형을 혼합하거나 특정 자료형을 지정할 수 있다. +중복요소를 허용하지 않기 때문에 intSet에서는 5가 한번만 나타난다. + +
+ +**가변형 mutableSetOf()** + +mutableSetOf()함수로 요소의 추가 삭제가 가능 mutableSetOf()는 MutableSet인터페이스 자료형을 반환하는데, 내부적으로 자바의 LinkedHashSet을 만들어냅니다. + +``` kotlin + + val animals = mutableSetOf("String", "Dog", "Cat") + println(animals) + + animals.add("Dog") + animals.add("Dog2") + println(animals) + + animals.remove("Python") + println(animals) + +// [String, Dog, Cat] +// [String, Dog, Cat, Dog2] +// [String, Dog, Cat, Dog2] + + +``` + +
+ +--- +### Set의 여러가지 자료구조 + +**hashSetOf()** + +- 해시 테이블에 요소를 저장할 수 있는 HashSet 컬렉션을 만든다. +- hashSetOf()는 HashSet을 반환하는데 HashSEt는 불변성 선언이 없기 때문에 추가 및 삭제 등의 기능을 수행할 수 있습니다. + + +``` kotlin + + val intsHashSet: HashSet = hashSetOf(6, 3, 4, 8); + intsHashSet.add(5) + intsHashSet.remove(6) + + println(intsHashSet) + // [8, 3, 4, 5] + +``` + +
+ +**sortedSetOf()** + +- 자바의 TreeSet컬렉션을 정렬된 상태를 반환한다. + +``` kotlin + + val instSortedSet: TreeSet = sortedSetOf(4, 1, 2, 7) + instSortedSet.add(6) + instSortedSet.remove(6) + println("instSortedSet : ${instSortedSet}") + + // instSortedSet : [1, 2, 4, 7] + +``` + +
+ +**linkedSetOf()** + +- 자바의 LinkedHashSet 자료형을 반환하는 헬퍼함수. +- 해시 테이블에 요소를 저장하는데, 저장된 순서에 따라 값이 저장되어 정렬된다. +- TreeSet, HashSet보다는 확실히 느리지만, 포인터 연결을 통해 메모리를 효율적으로 사용할 수 있다. + +``` kotlin + + val instLinkedHashSet: LinkedHashSet = linkedSetOf(1, 2, 7, 2, 9) + instLinkedHashSet.add(4) + instLinkedHashSet.remove(2) + + println("instLinkedHashSet : ${instLinkedHashSet}") + // instLinkedHashSet : [1, 7, 9, 4] + +``` + +
+ +--- +## Map 활용 + +### 불변형 mapOf() + +- mapOf()는 불변형 Map컬렉션을 만들 수 있다. + +``` kotlin + + val langMap = mapOf(11 to "Java", 22 to "Kotlin", 33 to "C++") + println(langMap) + // {11=Java, 22=Kotlin, 33=C++} + + +``` + + +**Key와 Value 따로 출력하기** + +방법1. $사인 사용하기. + +``` kotlin + + for((key, value) in langMap) { + println("key : ${key} , value = ${value}") + } +// key : 11 , value = Java +// key : 22 , value = Kotlin +// key : 33 , value = C++ + + +``` + +
+ +방법2. it사용 + +``` kotlin + + langMap.forEach { + println("key : " + it.key + ", value : " + it.value) + } +// key : 11, value : Java +// key : 22, value : Kotlin +// key : 33, value : C++ + + +``` + + +**Map에서 사용하는 멤버 프로퍼티와 메소드** + +![](https://velog.velcdn.com/images/lifeisbeautiful/post/d4071240-19c4-4a3b-bfde-4032001b4003/image.png) + +
+ + +### 가변형 mutableMapOf() + +**mutableMapOf()**는 추가, 삭제가 가능한 가변형 Map을 정의합니다. 이 함수는 MutableMap(K, V) 인터페이스 자료형을 반환합니다. + + +- put은 삽입, remove는 삭제 + +``` kotlin + + val capitalCityMap: MutableMap = mutableMapOf() + + capitalCityMap.put("UK", "London") + capitalCityMap.put("JP", "Tokyo") + capitalCityMap.put("CH", "Beijing") + println(capitalCityMap) + // {UK=London, JP=Tokyo, CH=Beijing} + + + capitalCityMap.remove("CH") + println(capitalCityMap) + // {UK=London, JP=Tokyo} + +``` + +
+ +- **putAll()**을 사용하면 Map객체를 통합할 수 있다 + +``` kotlin + + val appData = mutableMapOf("USA" to "Washington") + capitalCityMap.putAll(appData) + + println(capitalCityMap) + // {UK=London, JP=Tokyo, USA=Washington} + + +``` + +
+ + +--- +### Map의 기타 자료구조 + +- Map은 hashMapOf(), sortedMapOf(), linkedMapOf()로 각각 초기화 할 수 있다. +- SortedMap은 기본적으로 키에 대해 오름차순 정렬된 형태로 사용된다. +- 나머지는 Set과 똑같음 + +``` kotlin + + val hashMap = hashMapOf(1 to "Hello", 2 to "World") + println("hashMap : ${hashMap}") + // hashMap : {1=Hello, 2=World} + + + val sortedMap = sortedMapOf(1 to "Apple", 2 to "Banana", 0 to "PineApple") + println("sortedMap : ${sortedMap}") + // sortedMap : {0=PineApple, 1=Apple, 2=Banana} + + + val linkedHash = linkedMapOf(1 to "Computer", 2 to "Mouse") + println("linkedHash : ${linkedHash}") + // linkedHash : {1=Computer, 2=Mouse} + + +``` + + + + diff --git "a/Chapter03_\354\275\224\355\213\200\353\246\260 \355\221\234\354\244\200 \353\235\274\354\235\264\353\270\214\353\237\254\353\246\254 \355\231\234\354\232\251/09_Collection/09-4 \354\273\254\353\240\211\354\205\230\354\235\230 \355\231\225\354\236\245 \355\225\250\354\210\230.md" "b/Chapter03_\354\275\224\355\213\200\353\246\260 \355\221\234\354\244\200 \353\235\274\354\235\264\353\270\214\353\237\254\353\246\254 \355\231\234\354\232\251/09_Collection/09-4 \354\273\254\353\240\211\354\205\230\354\235\230 \355\231\225\354\236\245 \355\225\250\354\210\230.md" new file mode 100644 index 0000000..a24f3ff --- /dev/null +++ "b/Chapter03_\354\275\224\355\213\200\353\246\260 \355\221\234\354\244\200 \353\235\274\354\235\264\353\270\214\353\237\254\353\246\254 \355\231\234\354\232\251/09_Collection/09-4 \354\273\254\353\240\211\354\205\230\354\235\230 \355\231\225\354\236\245 \355\225\250\354\210\230.md" @@ -0,0 +1,406 @@ +# 컬렉션의 확장 함수 + +_코틀린에는 자바 기본 컬렉션을 구현한 것 뿐 아니라 많은 확장 함수를 제공한다._ +_기능을 기준으로 범주를 나눌 수 있다._ + +![](https://velog.velcdn.com/images/lifeisbeautiful/post/dfaf598d-5708-45f0-980b-52e92d040a0d/image.png) + +
+ +--- + +### 컬렉션의 연산 + +연산자를 사용하면 컬렉션에 대해 더하거나 빼는 등의 기능을 수행할 수 있다. + +``` kotlin + + val list4 = listOf("one", "two", "three") + val list5 = listOf(1, 3, 5) + val map1 = mapOf("h1" to 1, "hello" to 2) + + println(list4 + "four") // [one, two, three, four] + println(list5 + "1") // [1, 3, 5, 1] + println(list4 + "listOf(5, 6 ,7)") // [one, two, three, listOf(5, 6 ,7)] + println(list4 - 1) // [one, two, three] + + + println(map1 + Pair("Bye", 4)) // {h1=1, hello=2, Bye=4} + println(map1 + mapOf("Apple" to 4, "Orange" to 5)) // {h1=1, hello=2, Apple=4, Orange=5} + println(map1 - listOf("hi" to "hello")) // {h1=1, hello=2} + + +``` + +위와 마찬가지로 연산자 - + 등을 통해서도 컬렉션을 관리할 수 있다. + + +
+ +### 요소의 처리와 집계 + +요소를 집계하는 확장 함수로는 forEach, forEachIndexed, onEach, count, max, min, maxBy, minBy, fold, reduce, sumBy 등이있다. + + +``` kotlin + + val listPair = listOf(Pair("A", 300), Pair("B", 200), Pair("C", 100)) + println(listPair) + // [(A, 300), (B, 200), (C, 100)] + +``` + + +
+ + +**요소의 순환 람다식** +``` kotlin + + list.forEachIndexed { index, value -> + println("index[${index}] : ${value}") + } + +// index[0] : 1 +// index[1] : 2 +// index[2] : 3 +// index[3] : 4 +// index[4] : 5 +// index[5] : 6 +// index[6] : 7 + + +``` + +# + +## 요소의 순환 람다식** + +- forEach, forEachIndexed, onEach, count, max, min, maxBy, minBy, fold, reduce, sumBy() 등이 있습니다. + +``` kotlin + + val list7 = listOf(1, 2, 3, 4, 5, 6, 7) + val listPair2 = listOf(Pair("A", 300), Pair("B", 200), Pair("C", 100)) + val map = mapOf(11 to "Java", 22 to "Kotlin", 30 to "C++") + + println(list7) + println(listPair2) + println(map) + +// [1, 2, 3, 4, 5, 6, 7] +// [(A, 300), (B, 200), (C, 100)] +// {11=Java, 22=Kotlin, 30=C++} + + +``` + +# + +**요소의 순환** +``` kotlin + + list6.forEach { println("$it") } + println() + list6.forEachIndexed { index,value -> println("index[$index] : $value ") } + println(list6) + +// [1, 2, 3, 4, 5, 6, 7] +// [(A, 300), (B, 200), (C, 100)] +// {11=Java, 22=Kotlin, 30=C++} + +``` + +# + +forEach는 각 요소를 람다식으로 처리한 후 컬렉션을 반환하지 않습니다. + +``` kotlin + + + +``` + + +**요소의 개수 반환** + +``` kotlin + + val list8 = listOf(1, 2, 3, 4, 5, 6, 7) + + println(list8.count { it % 2 == 0 }) + // 3 + + +``` + +# + +**최댓값과 최솟값 요소 반환** + +``` kotlin + + + // 이거 왜 max랑 min안됨? + println(list8.maxOrNull()) + // 7 + + println(list8.minOrNull()) + // 1 + + println("maxBy : " + map.maxByOrNull { it.key }) + // maxBy : 30=C++ + + println("minBy : " + map.minByOrNull { it.key }) + // minBy : 11=Java + + +``` + +# + +**각 요소에 정해진 식 적용하기** +초깃값과 정해진 식에 따라 요소에 처리하기 위해 fold와 reduece를 사용합니다. +fold는 초깃값과 정해진 식에 따라 처음 요소부터 끝 요소에 적용해 값을 반환하며, reduce는 fold와 동일하지만, 초깃값을 사용하지 않는다. + + +``` kotlin + + println(" list8.fold : ${list8.fold(4) { total, next -> total + next }}") + // list8.fold : 32 + + println(" list8.fold(1) : ${list8.fold(1) { total, next -> total * next }}") + // list8.fold(1) : 5040 + + println(list8.foldRight(4) { total, next -> total + next }) + // 32 + + println(list8.foldRight(1) { total, next -> total + next }) + // 29 + + println(list8.reduce { total, next -> total + next }) + // 28 + + println(list8.reduceRight { total, next -> total + next }) + // 28 + + +``` + +list의 모든 요소에 대해 fold(4)는 초깃값 4가 정해지고, 1 + 2 + 3 + .. + 6의 모든 요소가 더해진다. + +foldRight(1)은 모두 동일하나 요소의 오른쪽부터 더해진다. + +reduce, reduceRight의 작동 방법도 동일하나 초깃값이 없습니다. + + +# + +**모든 요소 합산하기** +식에서 도출된 모든 요소를 합한 결과를 반환하려면 sumBy를 사용합니다. + +``` kotlin + + println(listPair) + println(listPair.sumBy { it.second }) +// [(A, 300), (B, 200), (C, 100)] +// 600 + +``` + +# +
+ +--- + +## 요소의 검사 + +``` kotlin + + val list = listOf(1, 2, 3, 4, 5, 6, 7) + val listPair2 = listOf(Pair("A", 300), Pair("B", 200), Pair("C", 100)) + val map2 = mapOf(11 to "Java", 22 to "Kotlin") + + println(list.all { it < 10 }) + // true + + println(list.all { it % 2 == 0 }) + // false + + + println(list.any { it < 10 }) + // true + + println(list.any { it % 2 == 0 }) + // true + +``` + +- list.all은 모든 요소가 일치할 때, true를 반환한다. + +- list.any는 최소한 하나 혹은 그 이상의 특정 요소가 일치하면 true를 반환한다. + + +
+ +**특정 요소의 포함 및 존재 여부 검사하기** + +- 컬렉션에 특정 요소가 포함되어 있는지를 검사하기 위해 contains()를 사용할 수 있다. + + - 요소가 포함되어 있으면 true, 없으면 false + +- contains는 번위 연산자 in을 사용해서 요소의 포함 여부를 확인할 수도 있다. + + - 모든 요소가 포함되어 있는지 검사하려면 containsAll()을 사용한다. + + + +``` kotlin + + println("contains : ${list.contains(2)}") + // contains : true + + println(2 in list) + // true + + println(map.contains(11)) + // true + + println(11 in map) + // true + + println("containsAll : ${list.containsAll(listOf(1, 2, 3))}") + // containsAll : true + +``` + +
+ +- 컬렉션에 요소가 존재하는지를 검사 + + - none(), isEmpty(), isNotEmpty() + + - none() : 요소가 없으면 true, 있으면 false + - isEmpty()와 isNotEmpty()는 컬렉션이 비어있는지 아닌지에 따라 true를 반환 + + +``` kotlin + + println("none : ${list.none()}") + // none : false + + println("none : ${list.none { it > 6 }}") + // none : false + + println(list.isEmpty()) + // false + + println(list.isNotEmpty()) + // true + +``` + +
+ + +--- + +## 요소의 필터와 추출 + +**특정 요소 골라내기** + +![](https://velog.velcdn.com/images/lifeisbeautiful/post/30fa3de4-dca6-4b20-8c6d-24d5eb186fa6/image.png) + + +
+ +- filter()에서 주어진 컬렉션을 it으로 받아 % 2를 통해 짝수만 골라낼 수 있다. + + - filterNot()으로 하면 짝수가 아닌 것, + + +- null이 포함되어 있는 컬렉션인 listWithNull에서 null을 제외하기 위해서는 filterNotNull을 사용 + + +- filterIndexed를 사용하면 2개의 인자를 람다식에서 받아서 각각 인덱스와 값에 대해 특정 수식에 맞는 조건을 골라낼 수 있다. + + - 메소드 이름에 To가 붙은 filterIndexedTo는 filterIndexed에 컬렉션으로 반환되는 기능이 추가 되어있다. + + +![](https://velog.velcdn.com/images/lifeisbeautiful/post/af94e087-e460-4f82-b315-da0b758a587e/image.png) + +
+ + +- filterKeys는 요소를 it로 받아서 키에 대한 조건에 맞는 부분을 반환 + + - 반대로 filterValues를 사용하면 값에 의한 조건식을 만들고 그에 맞는 부분을 반환 + +- 여러 자료형 중 원하는 자료형을 골라낼 수 있는 filterIsInstance() + + - 여러가지 자료형이 포함된 listMixed에서 String형만 골라서 추출한다. + + + ![](https://velog.velcdn.com/images/lifeisbeautiful/post/9b6d462c-7ef5-40eb-9f62-741f87c67e37/image.png) + + +![](https://velog.velcdn.com/images/lifeisbeautiful/post/4df6096c-6b33-43ec-a03a-c460d730bdaa/image.png) + + +
+ + + +**특정 범위를 잘라내거나 반환하기** + +- slice()는 특정 범위의 인덱스를 가진 List를 인자로 사용해 기존 List에서 요소들을 잘라낼 수 있다. + + - 인덱스 0~2에 해당하는 1, 2 ,3이 추출되어 반환 + +![](https://velog.velcdn.com/images/lifeisbeautiful/post/1dbbad34-0065-4bdb-9ff4-662a9c6b7305/image.png) + + + + +
+ +--- + +## 요소의 필터와 추출 + +**특정 요소 골라내기** + +![](https://velog.velcdn.com/images/lifeisbeautiful/post/30fa3de4-dca6-4b20-8c6d-24d5eb186fa6/image.png) + + +
+ +--- + + +## 컬렉션의 분리와 병합 + +- union()은 두 List컬렉션을 병합하고 중복된 요소 값은 하나만 유지 +- plus()나 + 연산자를 사용하면 중복요소를 포함하여 합친다. + +![](https://velog.velcdn.com/images/lifeisbeautiful/post/80280db6-4431-4452-88dc-0631692ce63c/image.png) + + + + +
+ +--- + +## 순서와 정렬 + +- 컬렉션 목록을 뒤집거나 오름차순 혹은 내림차순으로 정렬 할 수 있다. +- reversed()는 요소의 순서를 거꾸로해서 반환 +- sorted()는 요소를 작은 수에서 큰 수로 반환 +- sortedBy()는 특정한 비교식에 의해 정렬된 컬렉션을 반환 +- sortedByDescending은 요소를 큰 수에서 작은 수로, z부터 a순서로 정렬해서 반환 + + +![](https://velog.velcdn.com/images/lifeisbeautiful/post/b3835158-228a-4801-b0c3-db38d7bd8e20/image.png) + + diff --git "a/Chapter03_\354\275\224\355\213\200\353\246\260 \355\221\234\354\244\200 \353\235\274\354\235\264\353\270\214\353\237\254\353\246\254 \355\231\234\354\232\251/09_Collection/09-5 \354\213\234\355\200\200\354\212\244 \355\231\234\354\232\251\355\225\230\352\270\260.md" "b/Chapter03_\354\275\224\355\213\200\353\246\260 \355\221\234\354\244\200 \353\235\274\354\235\264\353\270\214\353\237\254\353\246\254 \355\231\234\354\232\251/09_Collection/09-5 \354\213\234\355\200\200\354\212\244 \355\231\234\354\232\251\355\225\230\352\270\260.md" new file mode 100644 index 0000000..14218b6 --- /dev/null +++ "b/Chapter03_\354\275\224\355\213\200\353\246\260 \355\221\234\354\244\200 \353\235\274\354\235\264\353\270\214\353\237\254\353\246\254 \355\231\234\354\232\251/09_Collection/09-5 \354\213\234\355\200\200\354\212\244 \355\231\234\354\232\251\355\225\230\352\270\260.md" @@ -0,0 +1,139 @@ + +
+ +# 시퀀스 활용하기 + +--- + +- 시퀀스는 순차적인 컬렉션으로 요소의 크기를 특정하지 않고, 나중에 결정할 수 있는 특수한 컬렉션이다. + + - Ex) _특정 파일에서 줄 단위로 읽어서 요소를 만들 때 해당 파일의 끝을 모르면 줄이 언제 끝나는지 알 수 없는 경우가 있는데 이럴 때 사용할 수 있다._ + + +- 시퀀스는 계산하고 있지 않다가 toList()나 count() 같은 최종연산에 의해 결정됨 + +
+ + + + + +# + + +## 요소값 생성하기 + +generateSequence()로 생성 + - 특정 값을 생성하기 위해 generateSequence()를 사용할 수 있다. + - seed 인수에 의해 시작 요소의 값이 결정된다. + +# + + +``` kotlin + + // 시드 값 1을 시작으로 1씩 증가하는 시퀀스 정의 + val nums: Sequence = generateSequence(1) { + it + 1 + } + + println(nums.take(10).toList()) + // [1, 2, 3, 4, 5, 6, 7, 8, 9, 10] + +```` + +take(10)을 통해서 10개 만큼 요소가 저장되고, toList()를 통해서 List컬렉션으로 반환된다. + + +# + +``` kotlin + + val squares = generateSequence(1) { + it + 1 + }.map { it * it } + + println(squares.take(10).toList()) + // [1, 4, 9, 16, 25, 36, 49, 64, 81, 100] + + ``` + +# + + + +## 요소 값 가져오기 + +**메소드 체이닝의 중간 결과 생성** + +- 중간 연산 결과 없이 한번에 끝까지 연산 후 결과를 반환하려면 asSequence()를 사용할 수 있습니다 +- 특히 filter나 map을 메소드 체이닝해서 사용할 경우 순차적 연산이기 때문에 시간이 많이 걸릴 수 있지만, asSequence()를 사용하면 병렬 처리되기 때문에 처리 성능이 좋아진다. + + +**시퀀스를 사용하지 않은 메소드 체이닝** + +``` kotlin + + val list6 = listOf(1, 2, 3, 4) + val listDefault = list6.map { + println("map : ${it}"); it * it + } + .filter { + println("filter : ${it}"); it % 2 == 0 + } + + println("listDefault : ${listDefault}") +// map : 1 +// map : 2 +// map : 3 +// map : 4 +// filter : 1 +// filter : 4 +// filter : 9 +// filter : 16 +// listDefault : [4, 16] + +``` + +# + +**asSequence()를 통해 가져오기** + +``` kotlin + + val listSeq = list6.asSequence() + .map { + println("map : ${it}"); it * it + } + .filter { + println("filter : ${it}"); it % 2 == 0 + }.toList() + + println("listSeq : ${listSeq}") +// map : 1 +// filter : 1 +// map : 2 +// filter : 4 +// map : 3 +// filter : 9 +// map : 4 +// filter : 16 +// listSeq : [4, 16] + +``` + +- asSequence()를 사용한 다음 바로 map,filter를 이용하여 메소드 체이닝을 했다. + +- .filter와 .map으로는 결과를 만들어 낼 수 없고, 마지막 toList()를 통해서 결과를 List를 반환할 때 모든 연산이 수행되고 결과물이 새로운 리스트인 listSeq에 지정된다. + +- 아까와 다르게 병렬처리 이기때문에 결과가 동시에 수행되면서 나옴 + + - 따라서, 시퀀스를 사용하는 것이 메모리나 속도 측면에서 훨씬 더 좋은 성능을 낼 수 있다. + + +![](https://velog.velcdn.com/images/lifeisbeautiful/post/0a521e74-3786-4d43-92de-b554673befb2/image.png) + +- 시퀀스를 사용하지 않은 것과 시퀀스를 사용한 결과의 시간차이. +- 작은 컬렉션에는 시퀀스를 사용하지 않는 것이 좋다. + + - filter()등은 인라인 함수로 설계되어 있는데, 시퀀스를 사용하면 람다식을 저장하는 객체로 표현되기 때문에 인라인되지 않아 작은 컬렉션에서는 오히려 좋지 않다. diff --git "a/Chapter03_\354\275\224\355\213\200\353\246\260 \355\221\234\354\244\200 \353\235\274\354\235\264\353\270\214\353\237\254\353\246\254 \355\231\234\354\232\251/10_\355\221\234\354\244\200 \355\225\250\354\210\230\354\231\200 \355\214\214\354\235\274 \354\236\205\354\266\234\353\240\245/10-1 \354\275\224\355\213\200\353\246\260 \355\221\234\354\244\200 \355\225\250\354\210\230/README.md" "b/Chapter03_\354\275\224\355\213\200\353\246\260 \355\221\234\354\244\200 \353\235\274\354\235\264\353\270\214\353\237\254\353\246\254 \355\231\234\354\232\251/10_\355\221\234\354\244\200 \355\225\250\354\210\230\354\231\200 \355\214\214\354\235\274 \354\236\205\354\266\234\353\240\245/10-1 \354\275\224\355\213\200\353\246\260 \355\221\234\354\244\200 \355\225\250\354\210\230/README.md" new file mode 100644 index 0000000..c2df138 --- /dev/null +++ "b/Chapter03_\354\275\224\355\213\200\353\246\260 \355\221\234\354\244\200 \353\235\274\354\235\264\353\270\214\353\237\254\353\246\254 \355\231\234\354\232\251/10_\355\221\234\354\244\200 \355\225\250\354\210\230\354\231\200 \355\214\214\354\235\274 \354\236\205\354\266\234\353\240\245/10-1 \354\275\224\355\213\200\353\246\260 \355\221\234\354\244\200 \355\225\250\354\210\230/README.md" @@ -0,0 +1,485 @@ +# 10-1 코틀린 표준 함수 + +### 람다식과 고차 함수 복습하기 + +- 람다식 + + ```kotlin + val 변수이름: 자료형선언 = { 매개변수[,...] -> 람다식 본문 } + val sum: (Int, Int) -> Int = { x,y -> x + y } + val mul = { x: Int, y, Int -> x * y } + ``` + + - 매개변수가 1개인 경우, 매개변수를 생략하고 it으로 표기 + + ```kotlin + val add: (Int) -> Int = {it + 1} + ``` + + - 추론된 반환 자료형이 Unit이 아닌 경우에는 본문의 마지막 표현식이 반환값 + + ```kotlin + val isPositive: (Int) -> Boolean = { + val isPositive = it > 0 + isPositive // 마지막 표현식이 반환됨 + } + + val isPositiveLabel: (Int) -> Boolean = number@ { + val isPositive = it > 0 + return@number isPositive // 라벨을 사용해 반환됨 + } + ``` + +- 고차 함수 + - 고차 함수는 함수의 매개변수로 함수를 받거나 함수 자체를 반환할 수 있는 함수 + + ```kotlin + fun inc(x: Int): Int { + return x + 1 + } + + fun high(name: String, body: (Int) -> Int): Int { + println("name: $name") + val x = 0 + return body(x) + } + // body는 람다식 함수로 받을 수 있다 + val result1 = high("Sean", { x -> inc(x + 3) }) // 함수를 이용한 람다식 + val result2 = high("Sean") { inc(it + 3) } // 소괄호 바깥으로 빼내고 생략 + val result3 = high("Kim", ::inc) // 매개변수 없이 함수의 이름만 사용할 때 + val result4 = high("Sean") { x -> x + 3 } // 람다식 자체를 넘겨준 형태 + val result5 = high("Sean") { it + 3 } // 매개변수가 1개인 경우 생략 + ``` + + +### 클로저 + +- 람다식으로 표현된 내부 함수에서 외부 범위에 선언된 변수에 접근할 수 있는 개념 +- 람다식 안에 있는 외부 변수는 값을 유지하기 위해 람다식이 포획한 변수라 부른다. +- 기본적으로 함수 안에 정의된 변수는 지역 변수로 스택에 저장되어 있다. +- 하지만 클로저 개념에서 포획한 변수는 참조가 유지되어 함수가 종료되어도 사라지지 않고 +함수의 변수에 접근하거나 수정할 수 있게 한다. +- 클로저의 조건 + - final 변수를 포획한 경우 변수 값을 람다식과 함께 저장한다. + - final이 아닌 변수를 포획한 경우 변수를 특정 wrapper로 감싸서 나중에 변경하거나 읽을 수 있게 한다.이때 래퍼에 대한 참조를 람다식과 함께 저장한다. +- 클로저 테스트하기 + - 코틀린에서 final이 아닌 변수를 사용하면 내부적으로 변환된 자바 코드에서 + final로 지정해 사용된다. + + ```kotlin + fun main() { + val calc = Calc() + var result = 0 // 외부의 변수 + calc.addNum(2, 3) { x, y -> result = x + y } + println(result) + } + + class Calc { + fun addNum(a: Int, b: Int, add: (Int, Int) -> Unit) { + add(a,b) // 람다식 add에는 반환값이 없다 + } + } + ``` + + - var로 선언된 result는 addNum()이 호출되면 자신의 유효 범위를 벗어나 삭제되어야 하지만 + 클로저의 개념에 의해 독립된 복사본을 가진다. + - 람다식에서 변환 값은 Unit으로 선언되어 반환 값이 없지만, 포획된 변수 result에 값을 저장할 수 있다. + + ![filteredNames.png](./asset/filteredNames.png) + + - length는 람다식 바깥의 변수로 인자로 입력받은 길이에 일치하는 요소 목록을 반환해 + filterResult에 저장하고 출력한다. +- 클로저를 사용하면 내부의 람다식에서 외부 함수의 변수에 접근해 처리할 수 있어 효율성이 높다. +- 완잔히 다른 함수에서 변수에 접근하는 것을 제한할 수 있다. +- 코틀린의 표준 라이브러리는 이러한 개념이 사용되어 설계되었다. + +### 코틀린의 표준 라이브러리 + +![lambdaFun.png](./asset/lambdaFun.png) + +### let() 함수 활용하기 + +- let() 함수는 함수를 호출하는 객체 T를 이어지는 block의 인자로 넘기고 block의 결괏값 R을 반환한다. + + ```kotlin + public inline fun T.let(block: (T) -> R: R {...return block(this)} + ``` + + ```kotlin + fun main() { + val score: Int? = 32 + // var score = null + + // 일반적인 null 검사 + fun checkScore() { + if (score != null) { + println("Score: $score") + } + } + + // let 함수를 사용해 null 검사를 제거 + fun checkScoreLet() { + score?.let { println("Score: $it") } // 1번 + val str = score.let { it.toString() } // 2번 + println(str) + } + + checkScore() + checkScoreLet() + } + ``` + + 1번은 세이프 콜을 사용했으므로 score = null 이면 람다식은 실행되지 않는다. + + 2번은 it을 문자열로 변환한 후 반환된 값을 str에 할당했으므로 null이 출력된다. + +- 커스텀 뷰에서 let() 함수 활용하기 + + ```kotlin + // 1. + val padding = TypedValue.applyDimension( + TypedValue.COMPLEX_UNIT_DIP, 16f, resources.displayMetrics).toInt() + + setPadding(padding, 0, padding, 0) + + // 2. val padding을 없애서 효율적으로 + TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, 16f, resources.displayMetrics) + .toInt().let { padding -> setPadding(padding, 0, padding, 0) } + + // 3. 인자가 하나 밖에 없으므로 padding을 it으로 + TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, 16f, resources.displayMetrics) + .toInt().let { setPadding(it, 0, it, 0) } + ``` + +- null 가능성 있는 객체에서 let() 함수 활용하기 + + ```kotlin + var obj: String? + if (null != obj) { + Toast.makeText(applicationContext, obj, Toast.LENGTH_LONG).show() + } + + obj?.let { + Toast.makeText(applicationContext, it, Toast.LENGTH_LONG).show() + } + ``` + + ```kotlin + val firstName: String? + var lastName: String + + // if를 사용 + if (null != firstName) { + print("$firstName $lastName") + } else { + print("$lastName") + } + + // let을 사용 + firstName?.let { print("$it $lastName") } ?: print("$lastName") + ``` + +- 메서드 체이닝을 사용할 때 let() 함수 활용하기 + - 메서드 체이닝이란 여러 메서드 혹은 함수를 연속적으로 호출하는 기법 + + ```kotlin + var a = 1 + var b = 2 + + a = a.let { it + 2 }.let { + val i = it + b + i // 마지막 식 반환 + } + println(a) // 5 + ``` + + +### also() 함수 활용하기 + +- also() 함수는 함수를 호출하는 객체 T를 이어지는 block에 전달하고 객체 T 자체를 반환한다. + + ```kotlin + public inline fun T.let(block: (T) -> R: R = block(this) + public inline fun T.also(block: (T) -> Unit): T { block(this); return this } + ``` + + ```kotlin + var m = 1 + m = m.also { it + 3 } + println(m) // 원본 값 1 + ``` + +- let() 함수와 also() 함수 비교 + + ```kotlin + fun main() { + data class Person(var name: String, var skills: String) + var person = Person("Kildong", "Kotlin") + val a = person.let { // 1번 + it.skills = "Android" + "success" + } + println(person) + println("a: $a") // String + val b = person.also { // 2번 + it.skills = "Java" + "success" + } + println(person) + println("b: $b") // Person의 객체 b + } + /* + 출력 + Person(name=Kildong, skills=Android) + a: success + Person(name=Kildong, skills=Java) + b: Person(name=Kildong, skills=Java) + */ + ``` + + 1. let() 함수와 also() 함수를 비교해 보면 let() 함수는 person 객체에서 skills를 변경하고 + 마지막 표현식인 “success”를 반환해 a에 할당한다. + 2. 반면 also()는 람다식이 본문을 처리하지만 마지막 표현식이 b에 할당되는 것이 아닌 person 객체 + 자신에 할당된다. 따라서 b는 Person의 객체 person을 반환하고 새로운 객체 b가 할당된다. +- 특정 단위의 동작 분리 + + ```kotlin + // 기존의 디렉터리 생성 함수 + fun makeDir(path: String): File { + val result = File(path) + result.mkdirs() + return result + } + + // let()과 alse()를 통해 개선된 함수 + fun makeDir(path: String) = path.let{ File(it) }.also { it.mkdirs() } + ``` + + +### apply() 함수 활용하기 + +- apply() 함수는 also() 함수와 마찬가지로 호출하는 객체 T를 이어지는 block으로 전달하고 +객체 자체인 this를 반환한다. + + ```kotlin + public inline fun T.let(block: (T) -> R): R = block(this) + public inline fun T.also(block: (T) -> Unit): T {block(this); return this} + public inline fun T.apply(block: T.() -> Unit): T { block(); return this } + ``` + + - apply() 함수는 특정 객체를 생성하며 함께 호출해야되는 초기화 코드가 있을 때 사용할 수 있다. + - apply() 함수와 also() 함수의 다른 점은 T.()와 같은 표현에서 람다식이 확장함수로 처리된다. + + ```kotlin + fun main() { + data class Person(var name: String, var skills: String) + var person = Person("Kildong", "Kotlin") + person.apply { this.skills = "Swift" } // 1.this는 person 객체 + println(person) + + val returnObj = person.apply { // 2번 + name = "Sean" // this는 생략할 수 있음 + skills = "Java" // this 없이 객체의 멤버에 여러 번 접근 + } + println(person) + println(returnObj) + } + /* + 출력 + Person(name=Kildong, skills=Swift) + Person(name=Sean, skills=Java) + Person(name=Sean, skills=Java) + */ + ``` + + 1. apply()는 확장 함수로서 person을 this로 받아오는데 클로저를 사용하는 방식과 같다. + - 객체의 프로퍼티를 변경하면 원본 객체에 반영되고 this로 반환된다. + 2. this로 반환된 객체를 returnObj에 할당 + 3. also() 함수는 it을 사용해 멤버에 접근한다. +- 레이아웃을 초기화할 때 apply() 함수 활용하기 + + ```kotlin + // 기존의 코드 + val param = LinearLayout.LayoutParams(0, LinearLayout.LayoutParams.WRAP_CONTENT) + param.gravity = Gravity.CENTER_HORIZONTAL + param.weight = 1f + param.topMargin = 100 + param.bottomMargin = 100 + + val param = LinearLayout.LayoutParams(0, LinearLayout.LayoutParams.WRAP_CONTENT).apply { + gravity = Gravity.CENTER_HORIZONTAL + weight = 1f + topMargin = 100 + bottomMargin = 100 // param. 없이도 값을 지정할 수 있다. + } + ``` + +- 디렉터리를 생성할 때 apply() 함수 활용하기 + + ```kotlin + // 기존의 디렉터리 생성 함수 + fun makeDir(path: String): File { + val result = File(path) + result.mkdirs() + return result + } + + File(path).apply { mkdirs() } + ``` + + +### run() 함수 활용하기 + +- run() 함수는 인자가 없는 익명 함수처럼 동작하는 형태와 객체에서 호출하는 형태 +두 가지로 사용할 수 있다. + + ```kotlin + public inline fun run(block: () -> R): R = return block() + public inline fun T.run(block: T.() -> R): R = return block() + ``` + + - block이 독립적으로 사용된다. 이어지는 block 내에서 처리할 작업을 넣어 줄 수 있다. + - 일반 함수와 마찬가지로 값을 반환하지 않거나 특정 값을 반환할 수도 있다. + + ```kotlin + var skills = "Kotlin" + println(skills) + + val a = 10 + skills = run { + val level = "Kotlin Level:" + a + level // 마지막 표현식이 반환됨 + } + println(skills) // Kotlin Level:10 + ``` + + ```kotlin + fun main() { + data class Person(var name: String, var skills: String) + var person = Person("Kildong", "Kotlin") + val returnObj = person.apply { + name = "Sean" + skills = "Java" + "success" + } + val returnObj2 = person.run { + this.name = "Dooly" + this.skills = "C#" + "success" // 없으면 Unit이 반환됨 + } + println(person) + println("returnObj2: $returnObj2") + } + /* + 출력: + Person(name=Dooly, skills=C#) + returnObj2: success + */ + ``` + + +### with() 함수 활용하기 + +- with() 함수는 인자로 받는 객체를 이어지는 block의 receiver로 전달하며 결괏값을 반환한다. +run() 과 비슷하지만 receiver의 유무가 다르다. + + ```kotlin + public inline fun with(receiver: T, block: T.() -> R): R = receiver.block() + ``` + + - with() 함수는 매개변수가 2개이므로 with() {…} 와 같은 형태로 넣어준다. + - 세이프 콜을 지원하지 않으므로 let과 같이 쓰기도 한다. + + ```kotlin + fun main() { + data class User(val name: String, var skills: String, var email: String? = null) + val user = User("Kildong", "default") + + val result = with (user) { + skills = "Kotlin" + email = "kildong@example.com" + // "Success" + } + println(user) + println("result: $result") + } + /* + 출력: + User(name=Kildong, skills=Kotlin, email=kildong@example.com) + result: kotlin.Unit + */ + ``` + + +### use() 함수 활용하기 + +- use() 함수를 사용하면 객체를 사용한 후 close() 함수를 자동적으로 호출해 닫을 수 있다. +- T는 Closeable? 이므로 block은 닫힐 수 있는 객체를 지정해야한다. + +```kotlin +public inline fun T.use(block: (T) -> R): R +``` + +```kotlin +fun main() { + // output.txt에 hello 출력하고 use()로 파일을 닫는다. + PrintWriter(FileOutputStream("d:\\test\\output.txt")).use { + it.println("hello") + } + // contents.txt를 열고 내용을 읽어온다. + val file = File("d:\\test\\contents.txt") + file.bufferedReader().use { + println(it.readText()) + } +} +``` + +### 기타 함수의 활용 + +- takeIf() 함수와 takeUnless() 함수의 활용 + - takeIf() 함수는 람다식이 true면 결과를 반환하고, takeUnless() 함수는 람다식이 false면 결과를 반환한다. + + ```kotlin + public inline fun T.takeIf(predicate: (T) -> Boolean): T? + = if (predicate(this)) this else null + ``` + + ```kotlin + // 기존 코드 + if (someObject != null && someObject.status) { + doThis() + } + // 개선한 코드 + if (someObject?.status == true) { + doThis() + } + // takeIf() + someObject?.takeIf { it.status }?.apply { doThis() } + ``` + + ```kotlin + val input = "Kotlin" + val keyword = "in" + + // 입력 문자열에 키워드가 있으면 인덱스를 반환하는 함수 + input.indexOf(keyword).takeIf { it >= 0 } ?: error("keyword not found") + + input.indexOf(keyword).takeUnless { it < 0 } ?: error("keyword not found") + + ``` + +- 시간의 측정 + + ```kotlin + val executionTime = measureTimeMillis { + // some codes.... + } + println("Execution Time = $executionTime ms") + ``` + +- 난수 생성하기 + + ```kotlin + val number = Random.nextInt(21) + // 0~21 사이의 난수 제공 + ``` diff --git "a/Chapter03_\354\275\224\355\213\200\353\246\260 \355\221\234\354\244\200 \353\235\274\354\235\264\353\270\214\353\237\254\353\246\254 \355\231\234\354\232\251/10_\355\221\234\354\244\200 \355\225\250\354\210\230\354\231\200 \355\214\214\354\235\274 \354\236\205\354\266\234\353\240\245/10-1 \354\275\224\355\213\200\353\246\260 \355\221\234\354\244\200 \355\225\250\354\210\230/asset/filteredNames.png" "b/Chapter03_\354\275\224\355\213\200\353\246\260 \355\221\234\354\244\200 \353\235\274\354\235\264\353\270\214\353\237\254\353\246\254 \355\231\234\354\232\251/10_\355\221\234\354\244\200 \355\225\250\354\210\230\354\231\200 \355\214\214\354\235\274 \354\236\205\354\266\234\353\240\245/10-1 \354\275\224\355\213\200\353\246\260 \355\221\234\354\244\200 \355\225\250\354\210\230/asset/filteredNames.png" new file mode 100644 index 0000000..a6aa351 Binary files /dev/null and "b/Chapter03_\354\275\224\355\213\200\353\246\260 \355\221\234\354\244\200 \353\235\274\354\235\264\353\270\214\353\237\254\353\246\254 \355\231\234\354\232\251/10_\355\221\234\354\244\200 \355\225\250\354\210\230\354\231\200 \355\214\214\354\235\274 \354\236\205\354\266\234\353\240\245/10-1 \354\275\224\355\213\200\353\246\260 \355\221\234\354\244\200 \355\225\250\354\210\230/asset/filteredNames.png" differ diff --git "a/Chapter03_\354\275\224\355\213\200\353\246\260 \355\221\234\354\244\200 \353\235\274\354\235\264\353\270\214\353\237\254\353\246\254 \355\231\234\354\232\251/10_\355\221\234\354\244\200 \355\225\250\354\210\230\354\231\200 \355\214\214\354\235\274 \354\236\205\354\266\234\353\240\245/10-1 \354\275\224\355\213\200\353\246\260 \355\221\234\354\244\200 \355\225\250\354\210\230/asset/lambdaFun.png" "b/Chapter03_\354\275\224\355\213\200\353\246\260 \355\221\234\354\244\200 \353\235\274\354\235\264\353\270\214\353\237\254\353\246\254 \355\231\234\354\232\251/10_\355\221\234\354\244\200 \355\225\250\354\210\230\354\231\200 \355\214\214\354\235\274 \354\236\205\354\266\234\353\240\245/10-1 \354\275\224\355\213\200\353\246\260 \355\221\234\354\244\200 \355\225\250\354\210\230/asset/lambdaFun.png" new file mode 100644 index 0000000..6763929 Binary files /dev/null and "b/Chapter03_\354\275\224\355\213\200\353\246\260 \355\221\234\354\244\200 \353\235\274\354\235\264\353\270\214\353\237\254\353\246\254 \355\231\234\354\232\251/10_\355\221\234\354\244\200 \355\225\250\354\210\230\354\231\200 \355\214\214\354\235\274 \354\236\205\354\266\234\353\240\245/10-1 \354\275\224\355\213\200\353\246\260 \355\221\234\354\244\200 \355\225\250\354\210\230/asset/lambdaFun.png" differ diff --git "a/Chapter03_\354\275\224\355\213\200\353\246\260 \355\221\234\354\244\200 \353\235\274\354\235\264\353\270\214\353\237\254\353\246\254 \355\231\234\354\232\251/10_\355\221\234\354\244\200 \355\225\250\354\210\230\354\231\200 \355\214\214\354\235\274 \354\236\205\354\266\234\353\240\245/10-2 \353\236\214\353\213\244\354\213\235\352\263\274 DSL/README.md" "b/Chapter03_\354\275\224\355\213\200\353\246\260 \355\221\234\354\244\200 \353\235\274\354\235\264\353\270\214\353\237\254\353\246\254 \355\231\234\354\232\251/10_\355\221\234\354\244\200 \355\225\250\354\210\230\354\231\200 \355\214\214\354\235\274 \354\236\205\354\266\234\353\240\245/10-2 \353\236\214\353\213\244\354\213\235\352\263\274 DSL/README.md" new file mode 100644 index 0000000..2c9b077 --- /dev/null +++ "b/Chapter03_\354\275\224\355\213\200\353\246\260 \355\221\234\354\244\200 \353\235\274\354\235\264\353\270\214\353\237\254\353\246\254 \355\231\234\354\232\251/10_\355\221\234\354\244\200 \355\225\250\354\210\230\354\231\200 \355\214\214\354\235\274 \354\236\205\354\266\234\353\240\245/10-2 \353\236\214\353\213\244\354\213\235\352\263\274 DSL/README.md" @@ -0,0 +1,70 @@ +# 10-2 람다식과 DSL + +## Domain-Specific Language + +- 특정 애플리케이션의 도메인을 위해 특화된 언어 +- ex) 데이터베이스만을 다루는 SQL + +### 코틀린에서 DSL 사용하기 + +- DSL 경험해 보기 + + ```kotlin + // 먼저 데이터 모델 생성 + data class Person( + var name: String? = null, + var age: Int? = null, + var job: Job? = null) + + data class Job( + var category: String? = null, + var position: String? = null, + var extension: Int? = null) + + // Version 1 - also + //fun person(block: (Person) -> Unit): Person { + // val p = Person() + // block(p) + // return p + //} + + // Version 2 - apply + //fun person(block: Person.() -> Unit): Person { + // val p = Person() + // p의 block을 객체에 넣어준다. + // p.block() + // return p + //} + + // Version 3 + fun person(block: Person.() -> Unit): Person = Person().apply(block) + + // block을 job에 넣어준다 + fun Person.job(block: Job.() -> Unit) { + job = Job().apply(block) + } + + fun main() { + val person = person { + name = "Kildong" + age = 40 + job { + category = "IT" + position = "Android Developer" + extension = 1234 + } + } + + println(person) + } + ``` + +- DSL을 구현하는 요소 + + ![DSL_Element.png](./asset/DSL_Element.png) + + +### DSL을 사용한 사례 + +- Spring 프레임워크 +- Ktor 프레임워크 diff --git "a/Chapter03_\354\275\224\355\213\200\353\246\260 \355\221\234\354\244\200 \353\235\274\354\235\264\353\270\214\353\237\254\353\246\254 \355\231\234\354\232\251/10_\355\221\234\354\244\200 \355\225\250\354\210\230\354\231\200 \355\214\214\354\235\274 \354\236\205\354\266\234\353\240\245/10-2 \353\236\214\353\213\244\354\213\235\352\263\274 DSL/asset/DSL_Element.png" "b/Chapter03_\354\275\224\355\213\200\353\246\260 \355\221\234\354\244\200 \353\235\274\354\235\264\353\270\214\353\237\254\353\246\254 \355\231\234\354\232\251/10_\355\221\234\354\244\200 \355\225\250\354\210\230\354\231\200 \355\214\214\354\235\274 \354\236\205\354\266\234\353\240\245/10-2 \353\236\214\353\213\244\354\213\235\352\263\274 DSL/asset/DSL_Element.png" new file mode 100644 index 0000000..58faf75 Binary files /dev/null and "b/Chapter03_\354\275\224\355\213\200\353\246\260 \355\221\234\354\244\200 \353\235\274\354\235\264\353\270\214\353\237\254\353\246\254 \355\231\234\354\232\251/10_\355\221\234\354\244\200 \355\225\250\354\210\230\354\231\200 \355\214\214\354\235\274 \354\236\205\354\266\234\353\240\245/10-2 \353\236\214\353\213\244\354\213\235\352\263\274 DSL/asset/DSL_Element.png" differ diff --git "a/Chapter03_\354\275\224\355\213\200\353\246\260 \355\221\234\354\244\200 \353\235\274\354\235\264\353\270\214\353\237\254\353\246\254 \355\231\234\354\232\251/10_\355\221\234\354\244\200 \355\225\250\354\210\230\354\231\200 \355\214\214\354\235\274 \354\236\205\354\266\234\353\240\245/10-3 \355\214\214\354\235\274 \354\236\205\354\266\234\353\240\245/README.md" "b/Chapter03_\354\275\224\355\213\200\353\246\260 \355\221\234\354\244\200 \353\235\274\354\235\264\353\270\214\353\237\254\353\246\254 \355\231\234\354\232\251/10_\355\221\234\354\244\200 \355\225\250\354\210\230\354\231\200 \355\214\214\354\235\274 \354\236\205\354\266\234\353\240\245/10-3 \355\214\214\354\235\274 \354\236\205\354\266\234\353\240\245/README.md" new file mode 100644 index 0000000..2fe64f1 --- /dev/null +++ "b/Chapter03_\354\275\224\355\213\200\353\246\260 \355\221\234\354\244\200 \353\235\274\354\235\264\353\270\214\353\237\254\353\246\254 \355\231\234\354\232\251/10_\355\221\234\354\244\200 \355\225\250\354\210\230\354\231\200 \355\214\214\354\235\274 \354\236\205\354\266\234\353\240\245/10-3 \355\214\214\354\235\274 \354\236\205\354\266\234\353\240\245/README.md" @@ -0,0 +1,235 @@ +# 10-3 파일 입출력 + +### 표준 입출력의 기본 개념 + +- 입력에 실패할 경우 null 가능성이 생기기 때문에 !! 혹은 ?로 NPE를 처리한다. + +```kotlin +import java.util.* + +fun main() { + // 기본 코틀린의 readLine(), 기본값은 문자열 + print("Enter: ") + val input = readLine()!! + // 입력 직후 정수형으로 바꾸고 싶을 때, readLine()!!.toInt() + println("You entered: $input") + + // 자바의 Scanner를 통한 입력 + val reader = Scanner(System.`in`) + var integer:Int = reader.nextInt() + println("You entered: $integer") +} +``` + +- nextInt(), nextLong(), nextFloat(), nextDouble(), nextBoolean(), nextLine() 등등… +- Kotlin의 입출력 API + - [kotlin.io](http://kotlin.io) 패키지는 자바 라이브러리를 확장한 것. + + ![in_output_package.png](./asset/in_output_package.png) + + - 간단한 데이터: readBytes, readLines, readText + - 대량의 데이터: copyTo, forEachBlock, forEachLine + - InputStream, Reader, Writer를 쓰고 나서 항상 닫아줘야 한다. +- 자바의 io, nio의 개념 + - 코틀린에서는 자바 라이브러리를 그대로 사용할 수 있으니 알아두자 + - 비동기 관련 루틴은 코루틴에서 지원하고 있다. (11장) + - io, nio의 기본적인 차이점은 버퍼 사용 여부 + + ![io_nio_diff.png](./asset/io_nio_diff.png) + +- 스트림과 채널 + - 입력 스트림과 출력 스트림으로 구분 + - 채널 방식은 양방향으로 입출력이 가능 + + ![nio_package.png](./asset/nio_package.png) + +- 넌버퍼와 버퍼 방식 + - 스트림에선 1바이트 씩 읽기 때문에 버퍼를 사용해서 다수의 데이터를 읽는 것보다 느림. + - io 방식에서는 버퍼와 병합한 BufferedInputStream과 BufferedOutputStream + - nio에서는 기본적으로 버퍼를 사용하기 때문에 데이터를 일일이 읽는 것보다 더 낫다. +- 블로킹과 넌블로킹 + + ![blocking.png](./asset/blocking.png) + + - 쓸 공간이 없을 때, 읽을 내용이 없을 때 대기하고 있는 상대를 블로킹이라고 한다. + - 메인 코드의 흐름을 방해하지 않도록 입출력 작업 시 스레드나 비동기 루틴에 맡겨 + 별개의 흐름으로 작업하는 것을 넌블로킹이라 한다. + +### 파일에 쓰기 + +- Files 클래스와 Paths, StandardOpenOption + +```kotlin +import java.io.IOException +import java.nio.file.Files +import java.nio.file.Paths +import java.nio.file.StandardOpenOption + +fun main() { + + val path = "D:\\test\\hello.txt" // 파일을 생성할 경로 + val text = "안녕하세요! Hello World!\n" + + try { + Files.write(Paths.get(path), text.toByteArray(), StandardOpenOption.CREATE) + } catch (e: IOException) { + } +} +``` + +- StandardOpenOption의 주요 옵션: READ, WRITE, APPEND, CREATE +- 파일 경로는 문자열도 가능하지만 URI 객체도 가능하다. +- File의 PrintWriter 사용하기 + - 콘솔에 출력하듯 바이트 단위로 파일에 쓸 수 있다. + + ```kotlin + import java.io.File + import java.io.FileWriter + import java.io.PrintWriter + + fun main() { + val outString: String = "안녕하세요!\tHello\r\nWorld!." // CR LF + val path = "D:\\test\\testfile.txt" + + val file = File(path) + val printWriter = PrintWriter(file) + + printWriter.println(outString) // 파일에 출력 + printWriter.close() + + // 람다식으로 줄인 표현 + // File(path).printWriter().use { it.println(outString) } + } + ``` + +- File의 BufferedWriter 이용하기 + - PrintWriter와 동일하지만 버퍼를 사용한다는 것이 다르다. + + ```kotlin + File(path).bufferedWriter().use { it.println(outString) } + ``` + +- File의 writeText() 사용하기 (p490) + + ```kotlin + val file = File(path) + file.writeText(outString) + file.appendText("\nDo great work!") // 파일에 문자열 추가 + ``` + + ![writeText.png](./asset/writeText.png) + + - writeText()의 상위 클래스로 올라가보면 FileOutputStream에서 표준 함수 use()를 이용해 write()가 사용된다. + - printWrite()는 null을 파일에 쓸 수 있지만, bufferedWriter()는 NPE가 발생할 수 있다. + - writeText()를 사용하는 경우에는 자료형 불일치 오류가 발생할 수 있다. +- FileWriter 사용하기 + + ```kotlin + val writer = FileWriter(path, true) + try { + writer.write(outString) + } catch (e: Exception) { + // 오류 발생! + } finally { + writer.close() + } + + FileWriter(path, true).use { it.write(outString) } + ``` + + +### 파일에서 읽기 + +- File의 FileReader 사용하기 + + ```kotlin + fun main() { + val path = "D:\\test\\Over the Rainbow.txt" + + try { + val read = FileReader(path) + println(read.readText()) + } catch (e: Exception) { + println(e.message) + } + } + ``` + +- 자바의 파일 읽기를 코틀린으로 변경하기 + + ```kotlin + fun main() { + val path = "D:\\test\\Over the Rainbow.txt" + + // 단순 변환 + /* + val file = File(path) + val inputStream: InputStream = file.inputStream() + val inputStreamReader = InputStreamReader(inputStream) + val sb = StringBuilder() + var line: String? + val br = BufferedReader(inputStreamReader) + try { + line = br.readLine() + while (line != null) { + sb.append(line, '\n') + line = br.readLine() + } + println(sb) + } catch (e:Exception){ + } finally { + br.close() + } + */ + + // use를 사용한 축소 + val file = File(path) + val inputStream: InputStream = file.inputStream() + // 자바의 InputStream에는 bufferedReader()가 없지만 kotlin.io 라이브러리 패키지에서 + // 확장함수 기법으로 InputStream에 추가되었다. + val text = inputStream.bufferedReader().use { it.readText() } + println(text) + + // BufferedReader만 사용해서 읽기 + val bufferedReader: BufferedReader = File(path).bufferedReader() + val inputString = bufferedReader.use { it.readText() } + println(inputString) + + // use 대신 useLines를 이용해서 줄 단위로 처리 + val bufferedReader = File(path).bufferedReader() + val lineList = mutableListOf() + bufferedReader.useLines { lines -> lines.forEach { lineList.add(it) } } + lineList.forEach { println("> " + it) } + + // BufferedReader의 생략, mutableListOf 사용 + val lineList = mutableListOf() + File(path).useLines { lines -> lines.forEach { lineList.add(it) } } + lineList.forEach { println("> " + it) } + ``` + +- copyTo() 사용하기 + + ```kotlin + public fun File.copyTo(target: File, overwrite: Boolean = false, + bufferSize: Int = DEFAULT_BUFFER_SIZE): File + + File(path).copyTo(File("D:\\test\\file2.txt")) + // path가 없으면 FileNotFoundException + ``` + + ```kotlin + // 파일의 내용 출력하기 + File(path).forEachLine { println(it) } + + // 바이트 단위로 읽기 (쓰기는 writeBytes()) + val bytes = File(path).readBytes() + println(Arrays.toString(bytes)) + + // 줄 단위로 읽기 + val lines = File(path).readLines() + lines.forEach { println(it) } + + // 텍스트 단위로 읽기 (쓰기는 writeText()) + val text = File(path).readText() + println(text) + ``` diff --git "a/Chapter03_\354\275\224\355\213\200\353\246\260 \355\221\234\354\244\200 \353\235\274\354\235\264\353\270\214\353\237\254\353\246\254 \355\231\234\354\232\251/10_\355\221\234\354\244\200 \355\225\250\354\210\230\354\231\200 \355\214\214\354\235\274 \354\236\205\354\266\234\353\240\245/10-3 \355\214\214\354\235\274 \354\236\205\354\266\234\353\240\245/asset/blocking.png" "b/Chapter03_\354\275\224\355\213\200\353\246\260 \355\221\234\354\244\200 \353\235\274\354\235\264\353\270\214\353\237\254\353\246\254 \355\231\234\354\232\251/10_\355\221\234\354\244\200 \355\225\250\354\210\230\354\231\200 \355\214\214\354\235\274 \354\236\205\354\266\234\353\240\245/10-3 \355\214\214\354\235\274 \354\236\205\354\266\234\353\240\245/asset/blocking.png" new file mode 100644 index 0000000..c55955f Binary files /dev/null and "b/Chapter03_\354\275\224\355\213\200\353\246\260 \355\221\234\354\244\200 \353\235\274\354\235\264\353\270\214\353\237\254\353\246\254 \355\231\234\354\232\251/10_\355\221\234\354\244\200 \355\225\250\354\210\230\354\231\200 \355\214\214\354\235\274 \354\236\205\354\266\234\353\240\245/10-3 \355\214\214\354\235\274 \354\236\205\354\266\234\353\240\245/asset/blocking.png" differ diff --git "a/Chapter03_\354\275\224\355\213\200\353\246\260 \355\221\234\354\244\200 \353\235\274\354\235\264\353\270\214\353\237\254\353\246\254 \355\231\234\354\232\251/10_\355\221\234\354\244\200 \355\225\250\354\210\230\354\231\200 \355\214\214\354\235\274 \354\236\205\354\266\234\353\240\245/10-3 \355\214\214\354\235\274 \354\236\205\354\266\234\353\240\245/asset/in_output_package.png" "b/Chapter03_\354\275\224\355\213\200\353\246\260 \355\221\234\354\244\200 \353\235\274\354\235\264\353\270\214\353\237\254\353\246\254 \355\231\234\354\232\251/10_\355\221\234\354\244\200 \355\225\250\354\210\230\354\231\200 \355\214\214\354\235\274 \354\236\205\354\266\234\353\240\245/10-3 \355\214\214\354\235\274 \354\236\205\354\266\234\353\240\245/asset/in_output_package.png" new file mode 100644 index 0000000..f3a2c8f Binary files /dev/null and "b/Chapter03_\354\275\224\355\213\200\353\246\260 \355\221\234\354\244\200 \353\235\274\354\235\264\353\270\214\353\237\254\353\246\254 \355\231\234\354\232\251/10_\355\221\234\354\244\200 \355\225\250\354\210\230\354\231\200 \355\214\214\354\235\274 \354\236\205\354\266\234\353\240\245/10-3 \355\214\214\354\235\274 \354\236\205\354\266\234\353\240\245/asset/in_output_package.png" differ diff --git "a/Chapter03_\354\275\224\355\213\200\353\246\260 \355\221\234\354\244\200 \353\235\274\354\235\264\353\270\214\353\237\254\353\246\254 \355\231\234\354\232\251/10_\355\221\234\354\244\200 \355\225\250\354\210\230\354\231\200 \355\214\214\354\235\274 \354\236\205\354\266\234\353\240\245/10-3 \355\214\214\354\235\274 \354\236\205\354\266\234\353\240\245/asset/io_nio_diff.png" "b/Chapter03_\354\275\224\355\213\200\353\246\260 \355\221\234\354\244\200 \353\235\274\354\235\264\353\270\214\353\237\254\353\246\254 \355\231\234\354\232\251/10_\355\221\234\354\244\200 \355\225\250\354\210\230\354\231\200 \355\214\214\354\235\274 \354\236\205\354\266\234\353\240\245/10-3 \355\214\214\354\235\274 \354\236\205\354\266\234\353\240\245/asset/io_nio_diff.png" new file mode 100644 index 0000000..93dc399 Binary files /dev/null and "b/Chapter03_\354\275\224\355\213\200\353\246\260 \355\221\234\354\244\200 \353\235\274\354\235\264\353\270\214\353\237\254\353\246\254 \355\231\234\354\232\251/10_\355\221\234\354\244\200 \355\225\250\354\210\230\354\231\200 \355\214\214\354\235\274 \354\236\205\354\266\234\353\240\245/10-3 \355\214\214\354\235\274 \354\236\205\354\266\234\353\240\245/asset/io_nio_diff.png" differ diff --git "a/Chapter03_\354\275\224\355\213\200\353\246\260 \355\221\234\354\244\200 \353\235\274\354\235\264\353\270\214\353\237\254\353\246\254 \355\231\234\354\232\251/10_\355\221\234\354\244\200 \355\225\250\354\210\230\354\231\200 \355\214\214\354\235\274 \354\236\205\354\266\234\353\240\245/10-3 \355\214\214\354\235\274 \354\236\205\354\266\234\353\240\245/asset/nio_package.png" "b/Chapter03_\354\275\224\355\213\200\353\246\260 \355\221\234\354\244\200 \353\235\274\354\235\264\353\270\214\353\237\254\353\246\254 \355\231\234\354\232\251/10_\355\221\234\354\244\200 \355\225\250\354\210\230\354\231\200 \355\214\214\354\235\274 \354\236\205\354\266\234\353\240\245/10-3 \355\214\214\354\235\274 \354\236\205\354\266\234\353\240\245/asset/nio_package.png" new file mode 100644 index 0000000..740e282 Binary files /dev/null and "b/Chapter03_\354\275\224\355\213\200\353\246\260 \355\221\234\354\244\200 \353\235\274\354\235\264\353\270\214\353\237\254\353\246\254 \355\231\234\354\232\251/10_\355\221\234\354\244\200 \355\225\250\354\210\230\354\231\200 \355\214\214\354\235\274 \354\236\205\354\266\234\353\240\245/10-3 \355\214\214\354\235\274 \354\236\205\354\266\234\353\240\245/asset/nio_package.png" differ diff --git "a/Chapter03_\354\275\224\355\213\200\353\246\260 \355\221\234\354\244\200 \353\235\274\354\235\264\353\270\214\353\237\254\353\246\254 \355\231\234\354\232\251/10_\355\221\234\354\244\200 \355\225\250\354\210\230\354\231\200 \355\214\214\354\235\274 \354\236\205\354\266\234\353\240\245/10-3 \355\214\214\354\235\274 \354\236\205\354\266\234\353\240\245/asset/writeText.png" "b/Chapter03_\354\275\224\355\213\200\353\246\260 \355\221\234\354\244\200 \353\235\274\354\235\264\353\270\214\353\237\254\353\246\254 \355\231\234\354\232\251/10_\355\221\234\354\244\200 \355\225\250\354\210\230\354\231\200 \355\214\214\354\235\274 \354\236\205\354\266\234\353\240\245/10-3 \355\214\214\354\235\274 \354\236\205\354\266\234\353\240\245/asset/writeText.png" new file mode 100644 index 0000000..480fd34 Binary files /dev/null and "b/Chapter03_\354\275\224\355\213\200\353\246\260 \355\221\234\354\244\200 \353\235\274\354\235\264\353\270\214\353\237\254\353\246\254 \355\231\234\354\232\251/10_\355\221\234\354\244\200 \355\225\250\354\210\230\354\231\200 \355\214\214\354\235\274 \354\236\205\354\266\234\353\240\245/10-3 \355\214\214\354\235\274 \354\236\205\354\266\234\353\240\245/asset/writeText.png" differ diff --git "a/Chapter03_\354\275\224\355\213\200\353\246\260 \355\221\234\354\244\200 \353\235\274\354\235\264\353\270\214\353\237\254\353\246\254 \355\231\234\354\232\251/10_\355\221\234\354\244\200 \355\225\250\354\210\230\354\231\200 \355\214\214\354\235\274 \354\236\205\354\266\234\353\240\245/README.md" "b/Chapter03_\354\275\224\355\213\200\353\246\260 \355\221\234\354\244\200 \353\235\274\354\235\264\353\270\214\353\237\254\353\246\254 \355\231\234\354\232\251/10_\355\221\234\354\244\200 \355\225\250\354\210\230\354\231\200 \355\214\214\354\235\274 \354\236\205\354\266\234\353\240\245/README.md" new file mode 100644 index 0000000..922a77d --- /dev/null +++ "b/Chapter03_\354\275\224\355\213\200\353\246\260 \355\221\234\354\244\200 \353\235\274\354\235\264\353\270\214\353\237\254\353\246\254 \355\231\234\354\232\251/10_\355\221\234\354\244\200 \355\225\250\354\210\230\354\231\200 \355\214\214\354\235\274 \354\236\205\354\266\234\353\240\245/README.md" @@ -0,0 +1,5 @@ +# Chapter03-10. 표준 함수와 파일 입출력 + +- 발표일 : 2022/09/25 +- 발표자 : 송기훈 +

diff --git "a/Chapter03_\354\275\224\355\213\200\353\246\260 \355\221\234\354\244\200 \353\235\274\354\235\264\353\270\214\353\237\254\353\246\254 \355\231\234\354\232\251/11_\354\275\224\353\243\250\355\213\264\352\263\274 \353\217\231\354\213\234\354\204\261 \355\224\204\353\241\234\352\267\270\353\236\230\353\260\215/11-1 \353\217\231\354\213\234\354\204\261 \355\224\204\353\241\234\352\267\270\353\236\230\353\260\215.md" "b/Chapter03_\354\275\224\355\213\200\353\246\260 \355\221\234\354\244\200 \353\235\274\354\235\264\353\270\214\353\237\254\353\246\254 \355\231\234\354\232\251/11_\354\275\224\353\243\250\355\213\264\352\263\274 \353\217\231\354\213\234\354\204\261 \355\224\204\353\241\234\352\267\270\353\236\230\353\260\215/11-1 \353\217\231\354\213\234\354\204\261 \355\224\204\353\241\234\352\267\270\353\236\230\353\260\215.md" new file mode 100644 index 0000000..6b92799 --- /dev/null +++ "b/Chapter03_\354\275\224\355\213\200\353\246\260 \355\221\234\354\244\200 \353\235\274\354\235\264\353\270\214\353\237\254\353\246\254 \355\231\234\354\232\251/11_\354\275\224\353\243\250\355\213\264\352\263\274 \353\217\231\354\213\234\354\204\261 \355\224\204\353\241\234\352\267\270\353\236\230\353\260\215/11-1 \353\217\231\354\213\234\354\204\261 \355\224\204\353\241\234\352\267\270\353\236\230\353\260\215.md" @@ -0,0 +1,41 @@ +> ```여러개의 실행 루틴을 동작시키는 멀티스레드 대신 좀 더 다루기 쉬운 코루틴을 사용한다.``` + +# 11-1 동시성 프로그래밍 + + +#### 동기적 +한개의 루틴을 완료한 후 다른 루틴을 실행하는 방식 + +#### 비동기적 +여러개의 루틴이 선행 작업의 순서나 완료여부와 상관없이 실행되는 방식 + +----------- +## 코루틴 +>**co**(함께 동시에) + **routine** (하나의 개별적인 작업) +넌블로킹 또는 비동기 코드를 동기코드처럼 쉽게 작성하면서도 비동기 효과를 낼 수 있다. + +### [용어 알아보기] +#### 블로킹 +![](https://velog.velcdn.com/images/5905kjh/post/54170006-96db-4456-93fb-050355f65106/image.png) +- 과정이 수행될 때까지 A의 코드가 진행되지 않고 내부 메모리 영역에서 작업이 마무리 될때까지 코드가 멈추게됨 => 블로킹하고 있다. +#### 넌블로킹 +![](https://velog.velcdn.com/images/5905kjh/post/a9bb0243-e9b3-409d-b643-bf1a4cb9c3ca/image.png) +- EAGAIN과 같은 시그널을 A가 받아서 실행을 재개 할 수 있음 +- 완료 시그널을 받을 후 콜백 루틴등을 호출해 완료된 이후의 일을 처리 할 수 있음 +=> 좀더 좋은 성능 +- 위 그림에서는 동시 수행처럼 보이지만 프로세서 코어수에 따라 동시에 수행될 수도 있고 두개의 태스크를 자주 교환해 동시에 수행 되는것 처럼 보이게 할 수 있다 (병행수행) + +#### 프로세스 +프로세스는 실행중인 프로그램 (os로 부터 필요한 자원을 받아 프로세스가 됨) , 실제로 작업을 수행하는 것이 스레드(프로세스 내에서 stack만 할당받고 코드 데이터 힙은 공유) + +- 하나의 프로그램이 실행되면 프로세스가 시작된다. +- 스레드에 비해 많은 비용이든다. +#### 스레드 +- 자신의 스택만 독립적으로 가지고 나머지는 대부분 스레드끼리공유해 비용이 낮다. +- 멀티 스레드를 구현하면 코드가 복잡해진다. + +=> 코루틴은 _문맥교환_이 없고 최적화된 비동기 함수를 통해 비선점형으로 작동하는 특징이 있어 _협력형 멀티태스킹_을 구현할 수 있게 해준다. + + + 문맥교환: (하나의 태스크가 CPU를 사용하고 있는 상태에서 다른 태스크가 CPU를 사용하도록 하기위해 이전의 프로세스의 상태를 보관하고 새로운 프로세스의 상태를 적재하는 과정) + 협력형 멀티태스킹 :(태스크들이 자발적으로 양보하며 실행을 바꿀수있는개념) + diff --git "a/Chapter03_\354\275\224\355\213\200\353\246\260 \355\221\234\354\244\200 \353\235\274\354\235\264\353\270\214\353\237\254\353\246\254 \355\231\234\354\232\251/11_\354\275\224\353\243\250\355\213\264\352\263\274 \353\217\231\354\213\234\354\204\261 \355\224\204\353\241\234\352\267\270\353\236\230\353\260\215/11-2 \354\275\224\353\243\250\355\213\264\354\235\230 \352\260\234\353\205\220\352\263\274 \354\202\254\354\232\251 \353\260\251\353\262\225.md" "b/Chapter03_\354\275\224\355\213\200\353\246\260 \355\221\234\354\244\200 \353\235\274\354\235\264\353\270\214\353\237\254\353\246\254 \355\231\234\354\232\251/11_\354\275\224\353\243\250\355\213\264\352\263\274 \353\217\231\354\213\234\354\204\261 \355\224\204\353\241\234\352\267\270\353\236\230\353\260\215/11-2 \354\275\224\353\243\250\355\213\264\354\235\230 \352\260\234\353\205\220\352\263\274 \354\202\254\354\232\251 \353\260\251\353\262\225.md" new file mode 100644 index 0000000..6ec9661 --- /dev/null +++ "b/Chapter03_\354\275\224\355\213\200\353\246\260 \355\221\234\354\244\200 \353\235\274\354\235\264\353\270\214\353\237\254\353\246\254 \355\231\234\354\232\251/11_\354\275\224\353\243\250\355\213\264\352\263\274 \353\217\231\354\213\234\354\204\261 \355\224\204\353\241\234\352\267\270\353\236\230\353\260\215/11-2 \354\275\224\353\243\250\355\213\264\354\235\230 \352\260\234\353\205\220\352\263\274 \354\202\254\354\232\251 \353\260\251\353\262\225.md" @@ -0,0 +1,140 @@ +# 11-2 코루틴의 개념과 사용방법 +```코루틴은 비용이 많이 드는 문맥교환 없이 해당 루틴을 **일시 중단**해서 이러한 비용을 줄일 수 있다.``` +=> 운영체제가 스케줄링에 개입하는 과정이 필요없다. +`+` 일시중단을 사용자가 제어할 수 있다. + +#### 코루틴의 주요 패키지 +kotlinx.coroutines.* 로 접근한다. +![](https://velog.velcdn.com/images/5905kjh/post/cd89ad30-db63-44ce-afbe-4a8f56b1638b/image.png) +![](https://velog.velcdn.com/images/5905kjh/post/f71636b6-ffe0-471b-89e6-21a9fe68d08e/image.png) + +#### 코루틴 기본 예제 +![](https://velog.velcdn.com/images/5905kjh/post/f0bb5c4c-c02d-48e5-a4a2-fc022bf90883/image.png) +- launch를 통해 코틀린 블록을 만들어냄 +- Hello 부분은 메인 스레드에 의해 바로 출력된다. +- World! 부분은 코루틴부분으로 메인스레드와 분리되어 1초 뒤에 실행된다. (넌블로킹 코드) +- suspend()로 선언된 지연함수여야 코루틴 기능을 사용할 수 있음 (코루틴 블록에서만 사용가능) suspend로 일시 중단 될 수 있다. (delay 뜯어보면 suspend선언) +- **지연함수는 또 다른 지연 함수 내에서 사용하거나 코루틴 블록에서만 사용해야한다.** + +#### launch +- launch를 통해 코루틴 블록을 만들어내는것을 빌더의 생성이라고 한다. +- job객체를 반환한다. (job: 백그라운드에서 실행되는 작업을 가리킴) + +```kotlin +fun main() { + val job = GlobalScope.launch { // Job 객체의 반환 + delay(1000L) println("World!") + } + println("Hello,") + println("job.isActive: ${job.isActive}, completed: ${job.isCompleted}") //코루틴의 상태 반환 + Thread.sleep(2000L) + println("job.isActive: ${job.isActive}, completed: ${job.isCompleted}") +} +``` +![](https://velog.velcdn.com/images/5905kjh/post/db2315b4-80ae-40a3-9439-28c8d27387e1/image.png) + +GlobalScope가 지정되어있으므로 코루틴의 생명주기는 프로그램의 생명주기에 의존된다 main()이 종료되면 같이 종료된다. + +```kotlin +suspend fun doWork1( ): String { + delay(1000) return "Work1" + } +suspend fun doWork2( ): String { + delay(3000) return "Work2" + } +private fun worksInSerial() { // 순차적 실행 + GlobalScope.launch { + val one = doWork1( ) + val two = doWork2( ) + println("Kotlin One : $one") + println("Kotlin Two : $two") + } + } +fun main() { + worksInSerial( ) + readLine( ) // main()이 먼저 종료되는 것을 방지하기 + //실행 결과 Kotlin One : Work1 Kotlin Two : Work2 + } +``` + +launch에 정의된 doWork1()과 doWork2() 함수는 순차적으로 표현 할 수 있다. 내부적으로 비동기 코드로서 동작 할 수 있지만 코드만 봤을때 순차적으로 실행되는 것처럼 표현함으로서 프로그래밍의 복잡도를 낮춘다. + +#### async +코루틴 빌더를 생성할 수있는 또하나의 키워드 +- lanch와 다른점 +Deferred를 통해 결과 값을 반환한다. + 지연된 결괏값을 받기 위해 await()를 사용한다. + + + ```kotlin + // 위의 코드 아래에 써져있다고 가정 + + private fun worksInParallel() { + // Deferred를 통해 결괏값을 반환 + val one = GlobalScope.async { + doWork1( ) + } + val two = GlobalScope.async { + doWork2( ) + } + GlobalScope.launch { + val combined = one.await( ) + "_" + two.await() + println("Kotlin Combined : $combined") + } + } + ... + workInParellel( ) + + //실행 결과 Kotlin Combined : Work1_Work2 + ``` + + - doWork1()과 2는 async에 의해 감싸져 있으므로 완전히 병행 수행 할 수 있다. + - 이 코드에서는 1초 더 지연시킨 doWork1이 먼저 종료될것이라고 예상 가능하다. + - 하지만 복잡한 루틴을 작성하는 경우에는 많은 태스크들과 같이 병행 수행되므로 어떤 루틴이 먼저 종료될지 알기 어려움 + => await를 사용해 태스크가 종료되는 시점을 기다렸다가 결과를 받도록한다. 그러면 현재 스레드의 블로킹 없이 먼저 종료되면 결과를 가져올 수 있다. + + -사용 예시 : 안드로이드 UI스레드에서 블로킹 가능성이 있는 코드를 사용하면 앱이 중단되거나 멈출수 있음 + 이경우 await를 사용하면 ui를 제외한 루틴만 블로킹 되므로 ui멈춤을 해결할 수 있다. + + +### 코루틴의 문맥 + + 코루린의 문맥은 CorutineContext에 의해 정의된다. + 1. launch {..} 와 같이 인자가 없는 경우 CoroutineScope에서 상위의 context가 상속되어 결정되고 + 2. launch(Dispatchers.Default){..}와 같이 사용되면 GlobalScope에서 실행되는 context와 동일하게 사용된다. + 코루틴이 사용할 스레드의 공동 pool을 사용한다. 이미 초기화 되어있는 스레드중에서 택하기 때문에 스레드를 생성하는 오버헤드가 없어 빠르다 + + ### 시작지점 정하기 + ![](https://velog.velcdn.com/images/5905kjh/post/45d6ad39-0c3b-4de5-a211-83f1ed8f9315/image.png) + ```kotlin + val job=async(start=CoroutineStart.LAZY){doWork1()} + + -> start()나 await()가 호출될때 실제 루틴이 시작됨 + ``` + + ### runBlocking + 새로운 코루틴을 실행하고 완료되기 까지 현재 스레드를 블로킹한다. + + ```kotlin + 앞에서는 메인 스레드가 종료되는것을 막기위해 readLine()을 했지만 + 메인 스레드 자체를 잡아두기 위해 main 자체를 블로킹 모드에서 실행 할 수있다. + + fun main() = runBlocking { // main() 함수가 코루틴 환경에서 실행 + launch { // 백그라운드로 코루틴 실행 + delay(1000L) println("World!") + } + println("Hello") // 즉시 이어서 실행됨 + // delay(2000L) + // delay( ) 함수를 사용하지 않아도 main() 함수내부의 코루틴이 모두 작동할 때까지 블로킹 + } + ``` + + #### 작업 완료 기다리기 + Job 객체의 join함수를 사용한다. job에서 지정한 작업이 완료될때까지 기다린다. + + #### 코루틴과 시퀀스 + + sequence() : 표준라이브러리중 하나. 아주 많은 값을 만들어 내는 코드로 부터 특정 값의 범위를 가져올 수 있다. + sequence() 내부에 지연함수를 사용해 코루틴과 함께 최종 형태를 나중에 결정할 수 있는 lazy 시퀀스를 만들 수 있다. + 무한 스크롤링을 구현하는 ui에 적용할 목록을 가져올 때 이용할 수 있다~ + diff --git "a/Chapter03_\354\275\224\355\213\200\353\246\260 \355\221\234\354\244\200 \353\235\274\354\235\264\353\270\214\353\237\254\353\246\254 \355\231\234\354\232\251/11_\354\275\224\353\243\250\355\213\264\352\263\274 \353\217\231\354\213\234\354\204\261 \355\224\204\353\241\234\352\267\270\353\236\230\353\260\215/11-3 \341\204\217\341\205\251\341\204\205\341\205\256\341\204\220\341\205\265\341\206\253 \341\204\203\341\205\251\341\206\274\341\204\214\341\205\241\341\206\250 \341\204\214\341\205\246\341\204\213\341\205\245\341\204\222\341\205\241\341\204\200\341\205\265.md" "b/Chapter03_\354\275\224\355\213\200\353\246\260 \355\221\234\354\244\200 \353\235\274\354\235\264\353\270\214\353\237\254\353\246\254 \355\231\234\354\232\251/11_\354\275\224\353\243\250\355\213\264\352\263\274 \353\217\231\354\213\234\354\204\261 \355\224\204\353\241\234\352\267\270\353\236\230\353\260\215/11-3 \341\204\217\341\205\251\341\204\205\341\205\256\341\204\220\341\205\265\341\206\253 \341\204\203\341\205\251\341\206\274\341\204\214\341\205\241\341\206\250 \341\204\214\341\205\246\341\204\213\341\205\245\341\204\222\341\205\241\341\204\200\341\205\265.md" new file mode 100644 index 0000000..d59082f --- /dev/null +++ "b/Chapter03_\354\275\224\355\213\200\353\246\260 \355\221\234\354\244\200 \353\235\274\354\235\264\353\270\214\353\237\254\353\246\254 \355\231\234\354\232\251/11_\354\275\224\353\243\250\355\213\264\352\263\274 \353\217\231\354\213\234\354\204\261 \355\224\204\353\241\234\352\267\270\353\236\230\353\260\215/11-3 \341\204\217\341\205\251\341\204\205\341\205\256\341\204\220\341\205\265\341\206\253 \341\204\203\341\205\251\341\206\274\341\204\214\341\205\241\341\206\250 \341\204\214\341\205\246\341\204\213\341\205\245\341\204\222\341\205\241\341\204\200\341\205\265.md" @@ -0,0 +1,329 @@ +# 11-3 코루틴 동작 제어하기 + + + +## 1. 코루틴의 문맥 + +- 코루틴은 항상 특정 문맥에서 실행 : `CoroutineContext`로 구현 + +- **디스패처(Dispatcher)** : CoroutineDispatcher 라는 추상 클래스 + + - 어떤 문맥에서 코루틴을 실행할지 결정 + - dispatch : 보내다 + - 사용자로부터 코루틴 실행 요청이 들어오면 스레드 풀에있는 적절한 스레드에 코루틴을 보내줌 (알아서 쉬고있는 스레드 선택) +image-20220923233144638 +image-20220923233234632 +image-20220923233300359 + + +- CoroutineDispatcher 객체 종류 + + - **기본 문맥 : Dispatchers.Default** + - CommonPool(기본문맥)에서 실행되고, GlobalScope로도 표현 + - `launch(Dispatchers.Default) {...}` == `GlobalScope.launch{...}` + - **I/O를 위한 문맥** : 입출력 위주 동작하는 코드 (블로킹 많은 파일, 소켓 I/O처리) + - Unconfined 문맥 : 호출자 스레드에서 코루틴 시작하지만 첫번째 지연점까지만 실행, 이후 호출된 지연 함수에 의해 다시 재개 (권장❌) + - **새 스레드를 생성하는 문맥** : 사용자가 직접 새 스레드 풀 만듦, 비용 多, 사용후 해제or종료 (성능👎) + - 코루틴 내에 또 다른 코루틴 정의시 부모 자식 구조가 되며 부모 취소(cancle)시 자식도 재귀적으로 취소됨, join()으로 명시적으로 기다릴 수 있음 + +- 실행 문맥 지정 코드 예시 + + - ```kotlin + fun main() = runBlocking { + val jobs = arrayListOf() + jobs += launch(Dispatchers.Unconfined) { // main 스레드에서 작업 + println("Unconfined:\t\t ${Thread.currentThread().name}") + } + jobs += launch(coroutineContext) { // 부모의 문맥, 여기서는 runBlocking의 문맥 + println("coroutineContext:\t ${Thread.currentThread().name}") + } + jobs += launch(Dispatchers.Default) { // 디스패처의 기본값 + println("Default:\t\t ${Thread.currentThread().name}") + } + jobs += launch(Dispatchers.IO) { // 입출력 중심의 문맥 + println("IO:\t\t ${Thread.currentThread().name}") + } + jobs += launch { // 아무런 인자가 없을 때 + println("main runBlocking: ${Thread.currentThread().name}") + } + jobs += launch(newSingleThreadContext("MyThread")) { // 새 스레드를 생성함 + println("MyThread:\t\t ${Thread.currentThread().name}") + } + jobs.forEach { it.join() } + } + ``` + + + +## 2. 기본 동작 제어하기 + +### repeat() : 지속적으로 반복하는 코드 작성 + +- 일종의 데몬(daemon) 스레드 구성 가능 + +- ```kotlin + fun main() = runBlocking { + val job = launch { + repeat(1000) { i -> + println("I'm sleeping $i ...") + delay(500L) + } + } + delay(1300L) + job.cancel() + } + ``` + + - 메인이 1.3초 기다린 후 종료 + +### cancle() : 코루틴 작업 취소 + +- join()만 쓰면 main()이 job을 기다림. 이때 cancel() 사용시 job 취소함 + +```kotlin +fun main() = runBlocking { + val startTime = System.currentTimeMillis() + val job = launch(Dispatchers.Default) { + var nextPrintTime = startTime + var i = 0 + while (i < 5) { // computation loop, just wastes CPU + // print a message twice a second + if (System.currentTimeMillis() >= nextPrintTime) { + println("I'm sleeping ${i++} ...") + nextPrintTime += 500L + } + } + } + delay(1300L) // delay a bit + println("main: I'm tired of waiting!") + //job.cancel() // cancel() 후에 join() 안해주면 race condition이 발새 + job.cancelAndJoin() // cancels the job and waits for its completion + println("main: Now I can quit.") +} +``` + +### finally 블록에서 코루틴 종료 과정 처리 + +```kotlin +fun main() = runBlocking { + val job = launch { + try { + repeat(1000) { i -> + println("I'm sleeping $i ...") + delay(500L) + } + } finally { + println("Bye!") + delay(100) // repeat()몇개 수행하다가 코루틴 취소되어서 메인까지 종료됨 + println("another") + // finally의 완전한 실행을 보장함 + /*withContext(NonCancellable) { + println("I'm running finally") + delay(1000L) + println("Non-cancellable") // 1 초를 지연해도 취소되지 않는다. + }*/ + } + } + delay(1300L) + job.cancelAndJoin() // 작업을 취소하고 완료될 때까지 기다림 + println("main: Quit!") +} +``` + +- 일반적인 finally 블록에서 지연함수 사용 불가 (코루틴이 취소됨) + - 파일 닫기나 통신 채널 닫기 등은 가능 +- 시간이 걸리는 작업, 지연함수 사용할 경우 **NonCancellable** 문맥에서 작동하도록 해야함 `/*주석부분*/` + + + +### 코루틴에 조건식 넣으면 연산이 끝날때까지 루틴이 중단되지 않음 + +```kotlin +fun main() = runBlocking { + val startTime = System.currentTimeMillis() + val job = GlobalScope.launch { + var nextPrintTime = startTime + var i = 0 + while (i < 5) { // 조건을 계산에 의해 반복 (끝나기 전까진 종료 안됨) // while(isActive) 로 바꾸면 종료됨 + if (System.currentTimeMillis() >= nextPrintTime) { + println("I'm sleeping ${i++} ...") + nextPrintTime += 500L + } + } + } + delay(1300L) + println("main: I'm tired of waiting!") + job.cancelAndJoin() // 1.3초 지나서 제어가 여기로 와도 while()문 실행중이므로 종료 안됨 + println("main: Now I can quit.") +} +``` + +- while() {} 작업이 끝날때 까지는 루틴이 끝나지 않음 +- job.cancelAndJoin()와 같은 취소 시그널로 연산을 종료시키고 싶으면 **while(isActive)**로 수정하면 됨 + + + +### 코루틴의 시간 만료 + +- 일정 실행 시간 뒤에 코루틴 취소 : **withTimeout()** + +```kotlin +fun main() = runBlocking { + try { + withTimeout(1300L) { // Timeout 예외 발생, null로 처리하는 경우 withTimeoutOrNull을 사용한다. + repeat(1000) { i -> + println("I'm sleeping $i ...") + delay(500L) + } + } + } catch (e: TimeoutCancellationException) { // 시간 만료시 block{}을 취소시키고 오류 발생시킴 + println("timed out with $e") + } +} +``` + + + + + +## 3. 채널의 동작 + +- 채널(Channel) : 자료를 서로 주고받기 위해 약속된 일종의 통로 역할, 넌블로킹 전송 개념 + + - 넌블로킹 : A함수가 B함수 호출해도 제어권은 자신이 가지고 있음 (cf. 블로킹) + + - ![image-20220924002635075](../../Library/Application%20Support/typora-user-images/image-20220924002635075.png) + + + + - 구현 : **SendChannel, ReceiveChannel 인터페이스** 이용 + + - 사용 방법 : 채널을 통해 send()로 값을 보냄, receive()에서 값을 받음 + - 더이상 전달할 요소가 없으면 채널을 닫을 수 있음 (cf.큐) + - image-20220924002635075 + + + - 예제 + + - ```kotlin + fun main() = runBlocking { + val channel = Channel() + // 1. 일반적인 send()와 receive()의 역할 + launch { + // 여기에 다량의 CPU 연산작업이나 비동기 로직을 둘 수 있다. + for (x in 1..5) channel.send(x * x) + } + // 5개의 값을 채널로부터 받는다 + repeat(5) { println(channel.receive()) } + println("Done!") + /////////////////////////////////////////////////////////////////////////// + // 2. close()로 닫고 반복문을 사용해 읽기 + launch { + for (x in 1..5) channel.send(x * x) + channel.close() // 모두 보내고 닫기 명시 + } + // for 루프를 사용해 끝까지 읽기 + for (element in channel) println(element) + println("Done!") + } + ``` + + - 결과 : 1 4 9 16 25 Done! *2 + +- **중요 ) 송신자와 수신자의 상태!** + + - **송신자는 isFull이 true**이면 **일시지연(suspend)**, **수신자는** **isEmpty가 true**이면 **일시지연** 됨 + + - 송신자: `close()`로 닫으면 `isClosedForSend가` true됨 + + - 수신자: `close()`로 닫으면 `isClosedForReceive` 가 false됨 + + - 채널은 버퍼가 없음 => send(), receive() 의 짝을 맞춰줘야 함 + +- 메소드 + + - `SendChannel.offer(element: E): Boolean` : 가능하면 요소를 채널에 추가. 채널이 꽉 차면 false 반환 + - `ReceiveChannel.poll(): E?` : 요소를 반환. 채널이 비어있으면 null을 반환 + +- 확장된 채널 자료형 + - **RendezvousChannel** : 내부에 버퍼를 두지 않음. send 동작은 receive가 즉각 가져가기 전까지는 일시중단, receive도 누군가 send하기 전까지 일시중단 + - **ArrayChannel** : 특정한 크기로 고정된 버퍼를 가진 채널. 꽉 차기 전까진 send계속 가능, receive도 버퍼가 빌 때 까지 계속 받기 가능 + - **LinkedListChannel** : 버퍼 크기 제한 없음 send시 일시중단 없으나 계속할 경우 메모리 부족 오류 가능함, receive는 비어있으면 잠시 중단 + - **ConflatedChannel** : 버퍼는 하나의 요소만 허용, 모든 send동작은 일시지연되지 않음. but 기존의 값을 덮어 씌움 + + + +### Producer-consumer pattern 생산자-소비자 패턴 + +- **produce : 채널이 붙어 있는 코루틴,** 생산자 측면의 코드를 쉽게 구성 가능. 채널에 값을 보내면 생산자임 +- 소비자는 for문 대신 `consumeEach` 함수를 확장해서 저장된 요소를 소비 가능 + +```kotlin +// 생산자를 위한 함수 생성 +fun CoroutineScope.producer(): ReceiveChannel = produce { + var total: Int = 0 + for (x in 1..5) { + total += x + send(total) + } +} + +fun main() = runBlocking { + val result = producer() // 값의 생산 + result.consumeEach { print("$it ") } // 소비자 루틴 구성 +} +``` + +실행 결과 : 1 3 6 10 15 + + + +### 버퍼를 가진 채널 + +- **채널에는 기본 버퍼가 없음** => send() 함수 먼저 호출시 receive()가 받기 전까지 send()는 일시 지연됨 + - 반대) receive()함수가 먼저 호출시 send()가 보내기 전까지 receive()는 일시 지연 + - 해결책 : 채널 버퍼에 크기를 주어 지연 없이 여러개 요소를 보냄 **Channel()생성자의 capacity 매개변수** 이용 + +```kotlin +fun main() = runBlocking { + val channel = Channel(3) // 버퍼 capacity 값을 준다 (3) + val sender = launch(coroutineContext) { // 송신자 측 + repeat(10) { + println("Sending $it") + channel.send(it) // 지속적으로 보내다가 꽉 차면 일시 지연됨 + // 3개 보내고 receive() 함수에서 받을때까지 멈춤 (3은 아직 안보내짐) + } + } + delay(1000) // 아무것도 안받고 1초가 잠시 기다린 후 + sender.cancel() // 송신자의 작업을 취소 한다. +} +``` + +실행결과 : Sending 0 Sending 1 Sending 2 Sending 3 + + + +### select 표현식 : 채널에서 결과를 받아옴 + +- channel의 특성상 send-receive는 pair이기 때문에 여러개의 채널중 어떤 채널이 먼저 receive가 가능해 질지를 예상하기 어려움 +- **여러개의 채널에서 가장 먼저 도착하는 채널을 받아야 할때 select를 사용하고, 그 채널의 값을 꺼낼 때 onReceive 사용** + +```kotlin +fun main() = runBlocking { + val routine1 = GlobalScope.produce { + delay(Random().nextInt(1000).toLong()) + send("A") + } + val routine2 = GlobalScope.produce { + delay(Random().nextInt(1000).toLong()) + send("B") + } + val result = select { // routine1, 2 중 먼저 수행된 것을 받게 된다. + routine1.onReceive { result -> result } + routine2.onReceive { result -> result } + } + println("Result was $result") +} +``` + +실행결과: Result was B diff --git "a/Chapter03_\354\275\224\355\213\200\353\246\260 \355\221\234\354\244\200 \353\235\274\354\235\264\353\270\214\353\237\254\353\246\254 \355\231\234\354\232\251/11_\354\275\224\353\243\250\355\213\264\352\263\274 \353\217\231\354\213\234\354\204\261 \355\224\204\353\241\234\352\267\270\353\236\230\353\260\215/11-4 \341\204\200\341\205\251\341\206\274\341\204\213\341\205\262 \341\204\203\341\205\246\341\204\213\341\205\265\341\204\220\341\205\245 \341\204\206\341\205\256\341\206\253\341\204\214\341\205\246 \341\204\213\341\205\241\341\206\257\341\204\213\341\205\241\341\204\207\341\205\251\341\204\200\341\205\265.md" "b/Chapter03_\354\275\224\355\213\200\353\246\260 \355\221\234\354\244\200 \353\235\274\354\235\264\353\270\214\353\237\254\353\246\254 \355\231\234\354\232\251/11_\354\275\224\353\243\250\355\213\264\352\263\274 \353\217\231\354\213\234\354\204\261 \355\224\204\353\241\234\352\267\270\353\236\230\353\260\215/11-4 \341\204\200\341\205\251\341\206\274\341\204\213\341\205\262 \341\204\203\341\205\246\341\204\213\341\205\265\341\204\220\341\205\245 \341\204\206\341\205\256\341\206\253\341\204\214\341\205\246 \341\204\213\341\205\241\341\206\257\341\204\213\341\205\241\341\204\207\341\205\251\341\204\200\341\205\265.md" new file mode 100644 index 0000000..0aa0afa --- /dev/null +++ "b/Chapter03_\354\275\224\355\213\200\353\246\260 \355\221\234\354\244\200 \353\235\274\354\235\264\353\270\214\353\237\254\353\246\254 \355\231\234\354\232\251/11_\354\275\224\353\243\250\355\213\264\352\263\274 \353\217\231\354\213\234\354\204\261 \355\224\204\353\241\234\352\267\270\353\236\230\353\260\215/11-4 \341\204\200\341\205\251\341\206\274\341\204\213\341\205\262 \341\204\203\341\205\246\341\204\213\341\205\265\341\204\220\341\205\245 \341\204\206\341\205\256\341\206\253\341\204\214\341\205\246 \341\204\213\341\205\241\341\206\257\341\204\213\341\205\241\341\204\207\341\205\251\341\204\200\341\205\265.md" @@ -0,0 +1,316 @@ +# 11-4 공유 데이터 문제 알아보기 + +- 병행 프로그래밍에서는 전역 변수 등 **수정 가능한 공유 자원에 접근할 때 값의 무결성을 보장해야 함** + - Java) synchronized 키워드로 메서드나 특정 코드를 동기화하고 보호 + - C) mutex lock, semaphore 사용 + - Kotlin) synchronized, 원자 변수(Atomic Variable), 스레드 가두기(Thread Confinement), 상호 배제(Mutual Exclusion) + + + +## 1. 동기화 기법 + +### synchronized 메서드, 블록 + +- Java에서 쓰는 것과 같음 +- 스레드 간 공유 데이터가 있을때 동기화해서 데이터의 안정성을 보장 => **특정 스레드가 이미 자원을 사용하는 중일 경우 나머지 스레드의 접근을 막음** +- 사용법 : **`@Synchronized`** 애노테이션 사용 + +```kotlin +var global = 20 + +@Synchronized fun synchronizedMethod() { + global += 1 + println("${Thread.currentThread()} global = $global") +} + +fun main() { + synchronizedMethod() +} +``` + + + +### 자바의 volatile + +- Java에서 쓰는 것과 같음 + +- 보통 변수는 성능 때문에 데이터를 캐시에 넣어두고 작업하는데 스레드로부터 값을 읽거나 쓰면 데이터가 일치하지 않고 깨짐 + +- **데이터를 캐시에 넣지 않도록 volatile 키워드와 변수를 함께 선언** + +- 코드 최적화, 순서가 바뀌는 경우 방지 => 프로그래머의 의도대로 읽기 및 쓰기 수행 + +- **`@Volatile`** : 값 쓰기는 보장안됨 => 원자성 보장 필요 + + - 참조링크: https://kotlinlang.org/docs/shared-mutable-state-and-concurrency.html#volatiles-are-of-no-help + +- **주의점: 2개의 스레드에서 공유변수에 대한 읽기/쓰기 연산 : synchronized + volatile 같이 써야함** => 원자성 보장 + + - ```kotlin + @Volatile private var running = false + private var count = 0 + + fun start() { + running = true + thread(start = true) { + while (running) println("${Thread.currentThread()}, count: ${count++}") + } + } + + fun stop() { running = false } + + fun main() { + start() + start() + Thread.sleep(10) + stop() // 여기서 상태를 바꾼다 + } + ``` + + - 실행 결과 image-20220924005743978 + - cf) 1개 스레드는 읽기/쓰기, 1개 쓰레드는 읽기만 수행 => volatile 변수가 항상 최근의 값만 읽어옴을 보장 + - + +### 원자 변수 (Atomic Variable) + +- 특정 변수의 증가나 감소, 더하기, 빼기가 **단일 기계어 명령**으로 수행 + + - 단일 기계어 명령 : CPU가 명령을 처리할 때의 최소 단위 (일반적으로 읽기/쓰기 operation은 항상 atomic하게 수행됨) + +- 연산 수행 도중 누구도 방해 불가 => 값의 무결성을 보장 + +- 원자 변수 사용 전->후 + +- ```kotlin + //var counter = 0 // 병행 처리중 문제가 발생할 수 있는 변수 + var counter = AtomicInteger(0) // 원자 변수로 초기화 + + suspend fun massiveRun(action: suspend () -> Unit) { + val n = 1000 // 실행할 코루틴의 수 + val k = 1000 // 각 코루틴을 반복할 수 + val time = measureTimeMillis { + val jobs = List(n) { + GlobalScope.launch { + repeat(k) { action() } + } + } + jobs.forEach { it.join() } + } + println("Completed ${n * k} actions in $time ms") + } + + fun main() = runBlocking { + massiveRun { + // counter++ // 증가 연산 시 값의 무결성에 문제가 발생할 수 있음 + counter.incrementAndGet() // 원자변수의 멤버 메서드를 사용해 증가 + } + // println("Counter = $counter") + println("Counter = ${counter.get()}") // 값 읽기 + } + ``` + + - 원자 변수를 사용하지 않았을 때 실행 결과)![image-20220925145618318](https://user-images.githubusercontent.com/55802790/192146888-c4f2ff88-9835-41df-8bcd-dfb59fbd33ad.png) + - 다른 코루틴이 ++된 값을 쓰기 전의 **이전 값**을 다른 코루틴이 읽어가서 ++시켜버림 => 실행마다 결과가 다름 + - 원자 변수 사용했을 때 실행 결과) ![image-20220925145638081](https://user-images.githubusercontent.com/55802790/192146892-55096f52-4186-4ac5-9617-09f06866c863.png) + - 전용메소드 사용: **증가시킨후 값 가져올때 : `incrementAndGet()`, 값 가져올 때 : `get()`** + +### 스레드 가두기 (Thread Confinement) + +- 특정 문맥에서 작동하도록 **단일 스레드에 가두는 방법** : **`newSingleThreadContext`** 이용 + +- UI 어플리케이션에서 UI상태는 단일 이벤트에 따라 작동해야 함 + +- **단점) 실행 시간이 오래 걸림** : 방법1 실행시간 2106ms + + - **해결책: `massiveRun( )` 에 스레드 가두기를 직접 적용** : 방법 2 실행시간 : 67 ms + +- ```kotlin + val counterContext = newSingleThreadContext("CounterContext") // 단일 스레드 문맥을 선언한다 + var counter = 0 + + suspend fun massiveRun(action: suspend () -> Unit) { + val n = 1000 + val k = 1000 + val time = measureTimeMillis { + coroutineScope { + repeat(n) { + launch { + repeat(k) {action()} + } + } + } + } + println("Completed ${n * k} actions in $time ms") + } + + fun main() = runBlocking { + // 방법1 : 스레드 가두기 (매우 오래 걸림) + massiveRun { + withContext(counterContext) { // 단일 스레드에 가둔다 + counter++ + } + } + // 방법 2 (그나마 빠름) + withContext(counterContext) { + massiveRun { + counter++ + } + } + println("Counter = $counter") + } + ``` + + - 그래도 원자 변수 방법보다 더 느림 + - 느린 이유 : 스레드가 문맥상 counter를 독립적으로 가지고 처리해서 공간이 더 필요함. + +### 상호 배제 (Mutual Exclusion) + +- 코드가 임계 구역(Critical Section)에 있는 경우 절대로 동시성이 일어나지 않게 하고 하나의 루틴만 접근하는 것을 보장 + +- 임계 구역(공유 변수 영역) + + - 병렬 컴퓨팅에서 둘 이상의 스레드가 동시에 접근해서는 안 되는 배타적 공유 자원의 영역 + - **2개 이상의 프로세스 (혹은 스레드)가 동일한 데이터에 접근하는 코드 영역 (다른 코드일 수도 있음)** + - 임계 구역에 들어온 루틴은 다른 루틴이 들어오지 못하도록 잠가야 함 + +![image-20220925151353740](https://user-images.githubusercontent.com/55802790/192146809-d729c901-02ea-454f-8ba9-2d35028a2bcd.png) ![image-20220925151657757](https://user-images.githubusercontent.com/55802790/192146811-8e460a25-dfd0-4735-92cb-6875d74bd6e4.png) + +- 소유자(Owner)개념 : 일단 잠근 루틴만이 잠금을 해제 할 수 있다 + +- Kotlin : **Mutex의 lock, unlock을 사용해서 임계 구역**을 만듦 + + - cf) Java : **synchronized** 키워드로 코드 보호 + + - 메소드 : **`tryLock( )`** :이미 잠겨있으면 false 리턴, **`holdsLock( )`** 소유자에 의한 잠금인지 확인 + + - ```kotlin + val mutex = Mutex() + ... + mutex.lock() + ... // 임계 구역 코드 + mutex.unlock() + ... + ``` + +- **람다식 `withLock` :** `mutex.lock(); try { ... } finally { mutex.unlock() }`을 쉽게 사용 가능 +- Mutex의 `isLock` 프로퍼티 : mutex가 잠금 상태일 때 true 반환 + + + +```kotlin +val mutex = Mutex() +var counter = 0 + +suspend fun massiveRun(action: suspend () -> Unit) { + val n = 1000 + val k = 1000 + val time = measureTimeMillis { + val jobs = List(n) { + GlobalScope.launch { + repeat(k) { action() } + } + } + jobs.forEach { it.join() } + } + println("Completed ${n * k} actions in $time ms") +} + +fun main() = runBlocking { + massiveRun { + mutex.withLock { // 상호배제를 사용한 보호 + counter++ + } + } + println("Counter = $counter") +} +``` + +![image-20220925153857987](https://user-images.githubusercontent.com/55802790/192146861-223c309a-6f81-4e3f-884d-bcf096ad9029.png) + + + +## 2. actor 코루틴 빌더 + +- **actor: 코루틴과 채널에서 통신하거나 상태를 관리** + +- 다른 언어의 actor 개념은 들어오고 나가는 메일 박스 기능과 비슷, **코틀린은 들어오는 메일 박스 기능만 함** + + - 메일 박스 : 특정 연산, 상태, 메시지 등을 담아 보내는 것, 위치에 상관없이 완전히 비동기 수행 + +- **한 번에 1개의 메시지만 처리하는 것을 보장** + +- ```kotlin + data class Task (val desc: String) + + val me = actor { + while(!isClosedForReceive) { // 닫힌 상태가 아닐 경우 + println(receive().desc.repeat(5)) // desc 출력 + } + } + ``` + + - 채널이 닫히게 되면 **`ClosedSendChannelException`** 을 만남 + +- 문맥 전환이 없어서 lock 기법보다 유용함 + +- 어떤 특정 상태를 관리하기 위한 백그라운드 태스크에 유용 + +```kotlin + suspend fun CoroutineScope.massiveRun(action: suspend () -> Unit) { + val n = 100 + val k = 1000 + val time = measureTimeMillis { + val jobs = List(n) { + launch { + repeat(k) { action() } + } + } + jobs.forEach { it.join() } + } + println("Completed ${n * k} actions in $time ms") +} + +// counterActor를 위한 메시지 +sealed class CounterMsg +object IncCounter : CounterMsg() // counter를 증가하기 위한 단방향 메시지 +class GetCounter(val response: CompletableDeferred) : CounterMsg() // 응답 요청 + +// // 새로운 counter actor를 위한 함수 +fun CoroutineScope.counterActor() = actor { // + var counter = 0 // 카운터 + for (msg in channel) { // 들어오는 메시지에 대한 처리 반복문 + when (msg) { + is IncCounter -> counter++ + is GetCounter -> msg.response.complete(counter) // + } + } +} + +fun main() = runBlocking { + val counter = counterActor() // actor의 생성 + GlobalScope.massiveRun { + counter.send(IncCounter) // + } + // actor의 카운터값을 얻기 위해 요쳥 + val response = CompletableDeferred() + counter.send(GetCounter(response)) + println("Counter = ${response.await()}") + counter.close() // actor의 중단 +} +``` + +- ??? + +## 3. 이벤트 루프 (Event Loop) + +- 넌블로킹, 비동기 프로그래밍 구현시 관련 라이브러리를 많이 사용함 + - Kovenant : 코틀린 + Promises + - Quasar : 가벼운 스레드 + - libuv : 비동기 이벤트 방식의 I/O모델 지원 +- 라이브러리를 통해 이벤트 처리를 위한 프로그래밍 모델을 만들기 위해 이벤트 루프를 사용함 +- 이벤트 루프의 역할 + - 사용자와 상호작용 하기 위해 항상 이벤트를 감시하는 주체(wait for event) + - 이벤트 발생시 처리(dispatch)하기 위해 특정 루틴을 동작시킴 + - 각 이벤트 요청에 대한 이벤트 큐를 가지고 있음. 이벤트 요청시 각 이벤트에 맞는 워커 스레드를 결정해서 실행시킴 -> 이벤트 큐의 실행이 끝나면 다시 돌아감 + +![image-20220925155503019](https://user-images.githubusercontent.com/55802790/192146872-f35a3de5-8563-4eef-b8e2-7d86bc4c40c6.png) diff --git "a/Chapter03_\354\275\224\355\213\200\353\246\260 \355\221\234\354\244\200 \353\235\274\354\235\264\353\270\214\353\237\254\353\246\254 \355\231\234\354\232\251/README.md" "b/Chapter03_\354\275\224\355\213\200\353\246\260 \355\221\234\354\244\200 \353\235\274\354\235\264\353\270\214\353\237\254\353\246\254 \355\231\234\354\232\251/README.md" new file mode 100644 index 0000000..5b38421 --- /dev/null +++ "b/Chapter03_\354\275\224\355\213\200\353\246\260 \355\221\234\354\244\200 \353\235\274\354\235\264\353\270\214\353\237\254\353\246\254 \355\231\234\354\232\251/README.md" @@ -0,0 +1,23 @@ +## Contents +##### 03-8. 제네릭과 배열 +1. 제네릭 다루기 +2. 배열 다루기 +3. 문자열 다루기 + +##### 03-9. 컬렉션 +1. 컬렉션의 구조와 기본 +2. List 활용하기 +3. Set과 Map 활용하기 +4. 컬렉션의 확장 함수 +5. 시퀀스 활용하기 + +##### 03-10. 표준 함수와 파일 입출력 +1. 코틀린 표준 함수 +2. 람다식과 DSL +3. 파일 입출력 + +##### 03-11. 코루틴과 동시성 프로그래밍 +1. 동시성 프로그래밍 +2. 코루틴의 개념과 사용 방법 +3. 코루틴 동작 제어하기 +4. 공유 데이터 문제 diff --git a/README.md b/README.md index ef04536..aabf95f 100644 --- a/README.md +++ b/README.md @@ -29,8 +29,8 @@ - 최강 최창영 - 그외 + 최강 최창영 폐하 + 그 외