From b3e64f41314811933c781b5603299fe410da59a2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EC=9D=B4=EC=A3=BC=EC=84=B1?= <32183520@dankook.ac.kr> Date: Sat, 26 Apr 2025 15:17:57 +0900 Subject: [PATCH 1/6] ~ 336 --- Chapter07/millo/README.md | 304 ++++++++++++++++++++++++++++++++++++++ Chapter07/millo/img.png | Bin 0 -> 27238 bytes 2 files changed, 304 insertions(+) create mode 100644 Chapter07/millo/README.md create mode 100644 Chapter07/millo/img.png diff --git a/Chapter07/millo/README.md b/Chapter07/millo/README.md new file mode 100644 index 0000000..a58a652 --- /dev/null +++ b/Chapter07/millo/README.md @@ -0,0 +1,304 @@ + +## 널이 될 수 있는 값 + +**다루는 내용** +- 널이 될 수 있는 타입 +- 널이 될 가능성이 있는 값을 다루는 구문의 문법 +- 널이 될 수 있는 타입과 널이 될 수 없는 타입의 변환 +- 코틀린의 널 가능성 개념과 자바 코드 사이의 상호 운용성 + +### 널 가능성: NPE 피하기 +- 널 가능성은 NPE 오류를 피할 수 있도록 한 코틀린 타입 시스템이다 +- 자바 사용 시 런타임에서 마주하는 `java.lang.NullPointerException`을 컴파일 시점으로 옮길 수 있게 해준다 +- 널이 될 수 있는지 여부를 타입 시스템에 추가해서 컴파일러가 컴파일 시 미리 감지하도록 한다 +- 10억짜리 에러라 불리는 NPE를 피할 수 있는 아주 좋은 타입 시스템 + +### 널이 될 수 있는 타입 +- 프로퍼티나 변수에 null을 허용하려면 타입 이름 뒤에 '?'를 붙여 널이 될 수 있는 타입임을 명시해야 한다 +```kotlin +fun strLen(s: String) = s.length +strLen(null) // 컴파일 에러: Null can not be a value of a non-null type String +``` + +- 널이 될 수 있는 타입이 지정되면 NPE가 발생할 수 있는 메서드를 호출할 수 없게 되어 NPE를 방지할 수 있다 +```kotlin +fun strLen(s: String?) = s.length +// 컴파일 에러: Only safe (?.) or non-null asserted (!!.) calls are allowed on a nullable receiver of type String? +``` + +- 널이 될 수 있는 값을 널이 될 수 없는 타입의 변수에 대입할 수 없다. +```kotlin +val x: String? = null +val y: String = x +// 컴파일 에러: Null can not be a value of a non-null type String +``` + +- 널이 될 수 있는 타입의 값을 널이 될 수 없는 타입의 파라미터를 받는 함수에 전달할 수 없다. +```kotlin +fun strLen(s: String) = s.length +val x: String? = null +println(strLen(x)) +// 타입이 일치하지 않습니다. 필요 항목: String 발견된 항목: String? +``` +- 즉 같은 타입 이름이어도 뒤에 '?' 여부에 따라 완전히 다른 타입으로 취급된다** + +**널이 될 수 있는 값 다루기** +- 제약이 많은 널이 될 수 있는 값을 null과 비교해서 null이 아님을 증명하면 컴파일러는 그 사실을 기억하고 널이 아닌 타입처럼 사용할 수 있다 +```kotlin +fun strLenSafe(s: String?) = + if (s != null) s.length else 0 // s가 null이 아님이 증명되어서 s.length가 가능한 것 +``` + +### 타입의 의미 자세히 살펴보기 +**Java 타입 시스템의 문제점** +- 자바에서 참조 타입 변수는 해당 타입의 객체를 참조하거나 null일 수 있지만 null은 어떤 타입의 인스턴스도 아니라서 런타임에서는 타입 시스템과는 별개로 null을 전혀 다른 존재로 취급한다. + - 실제로 `instanceof` 연산자는 피연산자가 `null`인 경우 항상 `false`를 반환하는데 이는 `null`을 어떤 타입의 인스턴스로도 보지 않는다는 뜻이다 +- 참조 타입에 null이 들어갈 수 있는 이상, 타입만으로는 메서드 호출이 안전한지 알 수 없다. + - null 여부를 항상 검사해주지 않으면 실행 시점에 NPE가 발생할 수 있는 것 + +**NPE를 다루는 다른 방법** +- 자바에도 NPE를 해결하기 위해 `@Nullable`이나 `@NotNull`과 같은 어노테이션을 제공한다 +- 어노테이션을 활용해 NPE가 발생할 수 있는 위치를 찾아주는 도구도 있다 (ex: 인텔리제이 코드 검사기) + - 표준 자바 컴파일 절차의 일부는 아니기에 일관성있게 적용된다는 보장은 없다 +- 절대 null을 안 쓰고 `Optional`처럼 null을 감싼 래퍼 타입을 사용하는 방법도 있다 + - 코드가 지저분해지고 실행 시점에 성능이 저하될 수 있긴하다 + +**우수한 Kotlin 타입 시스템** +- 널이 될 수 있는 타입과 그렇지 않은 타입을 구분해 명확하게 가능한 연산들을 구분한다 +- 실행 시점에 NPE가 발생할 수 있는 연산을 미리 판단하고 아예 연산을 금지시킬 수 있다 +- 실행 시점에는 널이 될 수 있는 타입이나 널이 아닌 타입의 객체는 같다 + - 즉, 널이 될 수 있는 타입은 널이 아닌 타입을 감싼 래퍼 타입이 아니라서 실행 시점에 부가 비용이 들지 않는다 + +### null 검사와 호출을 동시에 ?! +- 세이프 콜 `?.`을 사용하면 null 검사와 메서드 호출을 한 연산으로 수행할 수 있다 +- 세이프 콜 연산자는 null이 아닐 때만 메서드를 호출하고 null이면 null을 반환한다 +- 세이프 콜을 연쇄적으로 사용하면 null이 아닌 객체를 찾을 때까지 계속 호출할 수도 있다 +```kotlin +class Employee(val name: String, val manager: Employee?) + +fun managerName(employee: Employee): String? = employee.manager?.name + +fun main() { + val ceo = Employee("Da Boss", null) + val developer = Employee("Bob Smith", ceo) + + println(managerName(developer)) // Da Boss + println(managerName(ceo)) // null +} +``` + +### 엘비스 연산자 `?:` +- 엘비스 연산자를 사용하면 `null` 대신 사용할 기본값을 제공할 수 있다 +- 세이프 콜과 함께 사용하면 null이 아닐 때는 메서드를 호출하고 null이면 기본값을 반환한다 +```kotlin +fun strLenSafe(s: String?) = s?.length ?: 0 + +fun main() { + println(strLenSafe("abc")) // 3 + println(strLenSafe(null)) // 0 +} +``` + +**엘비스 연산자(`?:`)의 기본값은 표현식(expression)이면 전부 가능** +- `return`이나 `throw`도 expression이기 때문에 기본값으로 사용할 수 있다 + - 함수의 인자로 들어온 값이 null일 경우 즉시 반환하거나 예외를 던져 전제 조건을 명확하게 검사하는데 유용하다 +```kotlin +class Address(val streetAddress: String, val zipCode: Int, val city: String, val country: String) + +class Company(val name: String, val address: Address?) + +class Person(val name: String, val company: Company?) + +fun printShippingLabel(person: Person) { + val address = person.company?.address + ?: throw IllegalArgumentException("No address") + + with(address) { + println(streetAddress) + println("$zipCode, $city, $country") + } +} + +fun main() { + val address = Address("Elsestr, 47", 80687, "Munich", "Germany") + val company = Company("JetBrains", address) + val person = Person("John Doe", company) + + printShippingLabel(person) + // Elsestr, 47 + // 80687, Munich, Germany + + printShippingLabel(Person("John Doe", null)) + // IllegalArgumentException: No address +} +``` + +### 안전한 타입 캐스를 위한 `as?` +- `as`는 지정한 타입으로 변환하는데 이 때 변환이 불가능하면 `ClassCastException`이 발생한다 + - 물론 'is'로 먼저 변환이 가능한지 검사한 뒤에 `as`로 캐스트해 방지할 수 있다 +- `as?`를 사용하면 자바의 `instanceof`와 비슷하게 변환이 불가능하면 null을 반환한다 + +**안전한 캐스트를 위해선 보통 `as?`로 캐스트를 시도하고 `?:`로 실패한 경우를 대비한다** +```kotlin +class Person(val firstName: String, val lastName: String) { + override fun equals(other: Any?): Boolean { + val otherPerson = other as? Person ?: return false // 안전한 캐스트 시도 + return otherPerson.firstName == firstName && otherPerson.lastName == lastName // 인전한 캐스트 성공 이후엔 스마트 캐스트된 상태인 Person으로 사용 가능 + } +} + +fun main() { + val p1 = Person("John", "Doe") + val p2 = Person("John", "Doe") + val p3 = Person("Jane", "Doe") + + println(p1 == p2) // true + println(p1 == p3) // false + + println(p1.equals(null)) // false + println(p1.equals("abc")) // false +} +``` + +### 절대 널이 아니야 `!!` +- 널이 될 수 있는 타입이지만 개발자가 절대 널이 아님을 확신할 때 `!!`를 사용해 널이 아닌 타입으로 강제로 변환할 수 있다 +- 개발자의 판단으로 null이 아님을 단언하는 것이므로 null일 경우 예외가 발생해도 감수하겠다는 의미를 가진다 + - 마치 컴파일러의 안전 장치를 무시하고 떼쓰는 느낌이라 코틀린 설계자들은 최소한으로 사용하길 권장한다 +- 어떤 함수가 값이 `null`이 아님을 이미 체크했지만 다른 함수를 호출해도 컴파일러는 여전히 null일 수 있다고 판단할 때가 있는데 보통 이럴 때 `!!`를 사용해 강제로 널이 아님을 단언한다 +```kotlin +class SelectableTextList( + val contents: List, + var selectedIndex: Int? = null +) + +class CopyRowAction(val list: SelectableTextList) { + fun isActionEnabled() = list.selectedIndex != null + + // isActionEnabled()가 true일 때만 호출한다고 가정 + fun executeCopyRow() { + val index = list.selectedIndex!! // 절대 null이 아님을 단언 + // val index = list.selecetedIndex ?: return 중복 검증이지만 더 안전할 수 있다 + val value = list.contents[index] + println("Copying $value") + } +} +``` + +**널에 대해 `!!`를 사용해 예외가 발생했을 경우 어떤 식에서 발생했는지는 기록되지 않는다** +- 스택 트레이스에는 어떤 파일의 몇 번째 줄인지에 대한 정보만 들어있으므로 `!!`를 사용할 때에는 한 줄에 연쇄적으로 사용하지 않는 것이 좋다 +```kotlin +person.company!!.address!!.country // 어떤 식에서 발생한 NPE인지 알 수 없다 +``` + +### `let` 함수 +```kotlin +@kotlin.internal.InlineOnly +public inline fun T.let(block: (T) -> R): R { + contract { + callsInPlace(block, InvocationKind.EXACTLY_ONCE) + } + return block(this) +} +``` +- `let`은 인라인 함수로 수신 객체에 대해 람다를 받아 람다 결과를 반환한다 +- `T` 타입의 수신 객체 `this`에 확장함수 형태로 붙어서 사용된다 +- `contract`는 코틀린의 계약 시스템으로 컴파일러에게 힌트를 주는 정보이다 + - `let`의 경우 block 람다가 정확히 한번 호출될 것임을 알려주고 있다 +- 수신 객체 `this`는 block에 전달해서 실행 결과로 반환한다 + +**세이프 콜(`?.`)로 `let` 함수 호출해 널 안전하게 작업하기** +- 세이프 콜을 통해 식이 null인지 검사한 다음 널이 아닌 경우에만 `let` 함수 블럭을 진행할 수 있다 + - 식의 결과를 저장할 변수를 따로 만들 필요 없이 바로 수행할 수 있어 좋다 +```kotlin +fun sendEmailTo(email: String) { + println("Sending email to $email") +} + +fun main() { + var email: String? = "js.lee@naver.com" + email?.let { sendEmailTo(it) } + // Sending email to js.lee@naver.com + + email = null + email?.let { sendEmailTo(it) } +} +``` + +**언제 어떤 스코프 함수를 써야할까** +![img.png](img.png) + +반환값으로 다시 분류해보면 +- 람다의 결과를 반환 + - `let`: 전달 받은 객체를 가공해서 새로운 값으로 줄게 + - `run`: 전달 받은 객체를 기준으로 이것저것 계산하고 새로운 값으로 줄게 +- 객체 자체를 반환 + - `also`: 전달 받은 객체는 그대로 돌려줄게. 대신 뭔가 확인하거나 출력하기만 할게 + - `apply`: 전달 받은 객체의 속성들만 좀 손보고 그대로 돌려줄게 + +그럼 `it`이랑 `this` 구분은 어떻게 되는걸까? +- 까보면 `let`, `also`는 수신객체로 일반 객체를(`T`) 받고, `run`, `apply`는 수신객체로 확장 함수 형태인 람다를(`T.()`) 받는다 +- 일반 객체 `T`로 인자를 받으면 단순히 넘겨받은 외부 객체이므로`it`으로 접근하고 확장함수 `T.()`로 받으면 그 객체가 나 자신이 되어버리는 느낌이라 `this`로 접근한다(생략가능) + - 넘겨받은 외부인은 `it`, 확장함수로 내 몸에 합체된 나 자신은 `this` + +**책에서의 스코프 함수 활용처** +- 널이 아닌 경우에만 코드 블록을 실행하고 싶으면 `?.let`을 사용하라 +- 빌더 스타일 API를 사용해 객체의 프로퍼티를 설정할 때에는 `apply`를 사용하라 +- 객체에 어떤 동작을 실행한 후 원래의 객체를 다른 연산에 사용하고 싶을 때에는 `also`를 사용하라 +- 객체를 설정한 다음 별도의 결과를 돌려주고 싶을 때에는 `run`을 사용하라 + +**`run` vs `with`** +- `with`는 `run`과 비슷하게 수신 객체를 기준으로 이것저것 계산하고 마지막 연산의 결과를 반환한다 +- 다른 점은 `with`는 확장 함수가 아니며 수신 객체를 인자로 넘겨주고, `run`은 확장함수로 수신 객체로 직접 호출한다는 점이다 +- `run`은 객체에서 자연스럽게 체이닝해서 그 객체를 주체로 함수형 스타일로 실행하는 느낌 +- `with`는 인자로 넘겨받은 객체에 일들을 시키는 느낌 + +### 지연 초기화 프로퍼티 +- 코틀린은 클래스 안의 널이 아닌 프로퍼티를 생성자 이외의 곳에서 바로 초기화할 수 없다. +- 초기 값을 즉시 줄 수 없다면, nullable 타입을 사용할 수밖에 없다 + - 이러면 모든 접근 시 null 체크(`?`., `!!`)를 강제해야 한다. +- 이를 해결하기 위해 코틀린은 초기화는 나중에 하고 타입은 non-nullable로 유지할 수 있도록 `lateinit var` 키워드를 제공한다 + - 여기서 `val`은 파이널 필드로 컴파일되며 생성자 안에서 반드시 초기화되어야 해서 사용할 수 없다는 점! +- 만약 지연 초기화로 해두고 초기화 안 하고 접근하면 `UninitializedPropertyAccessException`이 발생한다 +```kotlin +class MyService { + fun performAction() = "Action Done!" +} + +@TestInstance(TestInstance.Lifecycle.PER_CLASS) +class MyTest { + private lateinit var service: MyService // non-null 타입으로 지연 초기화 + + @BeforeAll + fun setUp() { + service = MyService() // 이 때 프로퍼티 초기화 + } + + @Test + fun test() { + assertEquals("Action Done!", service.performAction()) + } +} +``` + +### 세이프 콜 없이 타입 확장 +- 일반적으로는 세이프 콜을 써야 null-safe하게 접근할 수 있는데 nullable한 타입에 대한 확장 함수를 정의하면 세이프 콜 없이 호출할 수 있다 +- null 체크 대신 확장함수 호출로 nullable한 수신 객체를 받고 내부에서 null을 처리하게 할 수 있다 + +**확장함수에서만 가능한 이유** +- 코틀린에서 확장함수는 정적 static 함수로 컴파일 시점에 타입만 보고 확장 함수를 호출할지 결정한다 + - 즉, 인스턴스의 실제 값(null 여부)와 관계 없이 그냥 그 타입이면 확장함수를 호출할 수 있는 것 +- 반면 멤버 호출은 동적으로 진짜 객체 인스턴스에 연결되어 있어야 해서 객체가 null이면 호출 자체가 불가능하다 + - 그래서 멤버 함수의 경우 먼저 null 체크를 한 뒤에 호출해야 한다 + + + + + + + + + +lateinit : "나중에 수동으로 직접 초기화할게." +lazy : "처음 쓰일 때 자동으로 초기화할게." + + diff --git a/Chapter07/millo/img.png b/Chapter07/millo/img.png new file mode 100644 index 0000000000000000000000000000000000000000..db306b61e68d0de31990187623204f4a125eb55e GIT binary patch literal 27238 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 rB;ypp|G!9xe60A%ijT+Da> Date: Sat, 26 Apr 2025 16:02:31 +0900 Subject: [PATCH 2/6] ~ 339 --- Chapter07/millo/README.md | 27 +++++++++++++++++++++++++-- 1 file changed, 25 insertions(+), 2 deletions(-) diff --git a/Chapter07/millo/README.md b/Chapter07/millo/README.md index a58a652..3a3758f 100644 --- a/Chapter07/millo/README.md +++ b/Chapter07/millo/README.md @@ -287,18 +287,41 @@ class MyTest { **확장함수에서만 가능한 이유** - 코틀린에서 확장함수는 정적 static 함수로 컴파일 시점에 타입만 보고 확장 함수를 호출할지 결정한다 - 즉, 인스턴스의 실제 값(null 여부)와 관계 없이 그냥 그 타입이면 확장함수를 호출할 수 있는 것 + - 스스로 수신 객체가 null일 때 어떻게 처리해야하는지 알고 있어서 세이프 콜이 필요없다 - 반면 멤버 호출은 동적으로 진짜 객체 인스턴스에 연결되어 있어야 해서 객체가 null이면 호출 자체가 불가능하다 - 그래서 멤버 함수의 경우 먼저 null 체크를 한 뒤에 호출해야 한다 +```kotlin +// nullable한 타입에 대한 확장함수 +fun String?.isNullOrEmpty() = this == null || this.isEmpty() // null 체크를 내부에서 명시적으로 +fun main() { + val str: String? = null + println(str.isNullOrEmpty()) // null을 넘겨줘도 예외 발생하지 않는다 + println(str.length) // 컴파일 에러 +} +``` +**null 값에 대해 세이프 콜 없이 `let`을 호출하면?** +- `let`도 nullable한 타입의 값에 대해 호출할 수는 있지만 this가 null인지 판단하지는 않는다 +- 따라서 세이프 콜 없이 `let`을 호출하면 null 여부와 관계없이 호출되기 때문에 컴파일 에러가 발생할 수 있다 +- 항상 nullalbe한 타입에 대해 `let`을 호출할 때에는 세이프 콜을 사용해야 한다 +```kotlin +fun sendEmailTo(email: String) { + println("Sending email to $email") +} +fun main() { + val recipient: String? = null + recipient.let { sendEmailTo(it) } + // 컴파일 에러: 타입이 일치하지 않습니다. + // 필요 항목: String 발견된 항목: String? +} +``` -lateinit : "나중에 수동으로 직접 초기화할게." -lazy : "처음 쓰일 때 자동으로 초기화할게." From 824ef259c971ea8cf3cf8b1b1f4a4a47bb60c8d6 Mon Sep 17 00:00:00 2001 From: jusung-c <32183520@dankook.ac.kr> Date: Sun, 27 Apr 2025 01:53:42 +0900 Subject: [PATCH 3/6] =?UTF-8?q?~=20ch7=20=EC=99=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- Chapter07/millo/README.md | 151 ++++++++++++++++++++++++++++++++++++-- 1 file changed, 144 insertions(+), 7 deletions(-) diff --git a/Chapter07/millo/README.md b/Chapter07/millo/README.md index 3a3758f..26f9cf1 100644 --- a/Chapter07/millo/README.md +++ b/Chapter07/millo/README.md @@ -81,7 +81,7 @@ fun managerName(employee: Employee): String? = employee.manager?.name fun main() { val ceo = Employee("Da Boss", null) val developer = Employee("Bob Smith", ceo) - + println(managerName(developer)) // Da Boss println(managerName(ceo)) // null } @@ -127,7 +127,7 @@ fun main() { printShippingLabel(person) // Elsestr, 47 // 80687, Munich, Germany - + printShippingLabel(Person("John Doe", null)) // IllegalArgumentException: No address } @@ -173,7 +173,7 @@ class SelectableTextList( class CopyRowAction(val list: SelectableTextList) { fun isActionEnabled() = list.selectedIndex != null - + // isActionEnabled()가 true일 때만 호출한다고 가정 fun executeCopyRow() { val index = list.selectedIndex!! // 절대 null이 아님을 단언 @@ -218,13 +218,14 @@ fun main() { var email: String? = "js.lee@naver.com" email?.let { sendEmailTo(it) } // Sending email to js.lee@naver.com - + email = null email?.let { sendEmailTo(it) } } ``` **언제 어떤 스코프 함수를 써야할까** + ![img.png](img.png) 반환값으로 다시 분류해보면 @@ -267,12 +268,12 @@ class MyService { @TestInstance(TestInstance.Lifecycle.PER_CLASS) class MyTest { private lateinit var service: MyService // non-null 타입으로 지연 초기화 - + @BeforeAll fun setUp() { service = MyService() // 이 때 프로퍼티 초기화 } - + @Test fun test() { assertEquals("Action Done!", service.performAction()) @@ -304,7 +305,7 @@ fun main() { **null 값에 대해 세이프 콜 없이 `let`을 호출하면?** - `let`도 nullable한 타입의 값에 대해 호출할 수는 있지만 this가 null인지 판단하지는 않는다 - 따라서 세이프 콜 없이 `let`을 호출하면 null 여부와 관계없이 호출되기 때문에 컴파일 에러가 발생할 수 있다 -- 항상 nullalbe한 타입에 대해 `let`을 호출할 때에는 세이프 콜을 사용해야 한다 +- 항상 nullalbe한 타입에 대해 `let`을 호출할 때에는 세이프 콜을 사용하는 게 좋다 ```kotlin fun sendEmailTo(email: String) { println("Sending email to $email") @@ -318,10 +319,146 @@ fun main() { } ``` +### 타입 파라미터의 널 가능성 +- 타입 파라미터? 함수나 클래스를 다룰 데이터 타입을 나중에 정할 수 있게 해주는 매개변수 +- 코틀린에서 함수나 클래스의 타입 파라미터는 기본적으로 널이 될 수 있다 + - 즉, 모든 타입 파라미터 T는 기본적으로 null을 허용하기에 `T?`와 같은 의미를 가진다 + +```kotlin +fun printHashCode(t: T) { + println(t?.hashCode()) // t가 null일 수 있으므로 세이프 콜 필요 +} + +fun main() { + printHashCode(null) // T는 Any?로 추론되어 null 허용 +} +``` + +**타입 파라미터에 upper bound 제약 걸기** +- 타입 파라미터가 널이 될 수 없게 하려면 널이 될 수 없는 타입을 upper bound로 지정해 널이 될 수 있는 값을 거부한다 +- upper bound로 타입 파라미터로 받을 수 있는 타입의 최상위 타입을 지정하는 것 + +```kotlin +fun printHashCode(t: T) { + println(t.hashCode()) // t는 이제 null이 될 수 없으므로 세이프 콜 필요 없음 +} + +fun main() { + printHashCode("abc") // 정상 작동 + printHashCode(null) + // 컴파일 에러: Null can not be a value of a non-null type parameter T +} +``` + +### 자바와 코틀린의 상호 운용성 +- 자바의 코드를 코틀린에서 쓸 때 모든 값에 대해 null 체크가 필요할까? +- 자바도 어노테이션으로 널 가능성을 명시하는데 코틀린도 그 정보를 활용한다 + - `@Nullable String`은 `String?`으로, `@NotNull String`은 `String`으로 + +**플랫폼 타입** +- 자바에서 코틀린으로 가져온 타입 중 널 관련 정보를 알 수 없는 타입을 플랫폼 타입이라고 한다 +- 코틀린 컴파일러는 플랫폼 타입을 널이 될 수 있는 타입으로도, 널이 될 수 없는 타입으로도 처리할 수 있다 + - 플랫폼 타입 처리에 대한 책임은 개발자에게 있으므로 NPE 발생 가능성 있다 +- 플랫폼 타입은 타입 뒤에 `!`가 붙는데 코틀린 코드 자체에서 선언할 수는 없고 자바에서 가져온 플랫폼 타입에 대한 IDE나 컴파일러 오류 메세지에서 확인할 수 있다 +```java +public class Person { + private final String name; + + public Person(String name) { + this.name = name; + } + + public String getName() { + return name; + } +} +``` +```kotlin +// null 체크 없이 플랫폼 타입 처리 +fun yellAt(person: Person) { + println(person.name.uppercase() + "!!!") +} + +// null-safe 플랫폼 타입 처리 +fun yellAtSafe(person: Person) { + println((person.name ?: "Anyone").uppercase() + "!!!") +} +fun main() { + val nullPerson: Person? = null + yellAt(nullPerson) // 런타임 에러: Null can not be a value of a non-null type parameter T + yellAtSafe(nullPerson) // ANYONE!!! +} +``` + +**자바 라이브러리 사용 시 주의사항** +- 자바 라이브러리를 사용할 때는 문서를 통해 메서드가 null을 반환할 수 있는지 확인해야 한다 +- 확실하지 않다면 널이 될 수 있는 타입으로 생각하고 처리하는 것이 안전하다 + +**왜 안전하게 모든 타입을 nullable하게 처리하지 않고 플랫폼 타입을 도입했을까?** +- 컴파일러가 널 가능성을 판단하지 못하므로 널이 절대 되지 않는 값도 불필요한 널 체크가 필요하다 +- 모든 자바 `ArrayList`을 코틀린에서 `ArrayList?`으로 다루면 매번 원소 접근 시 null 체크와 안전한 캐스팅이 필요하다 + - 널 체크 안전성보다 검사 비용이 커져 실용적이지 않다 + +### 코틀린에서 자바 메서드를 오버라이드할 때 +- 코틀린에서 자바 메서드를 오버라이드할 때 그 메서드의 파라미터와 반환 타입을 nullable or non-null 한지 결정해야 한다 +- 둘 다 가능하므로 +```java +interface StringProcessor { + void process(String value); +} +``` +```kotlin +// 널 가능성 없다고 판단하고 오버라이드 +class StringPrinter : StringProcessor { + override fun process(value: String) { + println(value) + } +} +// 널 가능성 있다고 판단하고 오버라이드 +class NullableStringPrinter : StringProcessor { + override fun process(value: String?) { + if (value != null) println(value) + } +} +``` +**자바 코드를 코틀린에서 사용할 때 Tip** +1. 자바 코드를 직접 호출해서 쓰지 말고 한번 래핑해서 사용하자 + - null 체크 후 null 안전하게 사용할 수 있도록 래핑해 단일 메서드로 제공하자 +2. 단일 진집점을 만들자 + - 자바 코드 접근은 이 단 하나의 래핑된 코틀린 메서드에서만 일어나게 하자 + - 나중에 자바 코드가 바뀌어도 코틀린은 이 단일 메서드만 수정하면 된다 +```java +// Java 코드 (외부 라이브러리, 예: API 응답 DTO) +public class ApiResponse { + public String getMessage() { + return null; // 실패하거나 응답이 잘못되면 null + } +} +``` +```kotlin +// 안전한 단일 wrapping 메서드 +fun ApiResponse.safeMessage(): String { + return this.message ?: "(no message)" +} +fun handleApiResponseSafe(response: ApiResponse?) { + if (response == null) { + println("(empty response)") + return + } + println(response.safeMessage().uppercase()) +} +fun main() { + val validResponse = ApiResponse() + val nullResponse: ApiResponse? = null + + handleApiResponseSafe(validResponse) // (no message) + handleApiResponseSafe(nullResponse) // (empty response) +} +``` From 477621247f8ed75b03ddd557a08af11f2aa0b14c Mon Sep 17 00:00:00 2001 From: jusung-c <32183520@dankook.ac.kr> Date: Sun, 27 Apr 2025 03:25:47 +0900 Subject: [PATCH 4/6] ~ 358 --- Chapter08/millo/README.md | 112 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 112 insertions(+) create mode 100644 Chapter08/millo/README.md diff --git a/Chapter08/millo/README.md b/Chapter08/millo/README.md new file mode 100644 index 0000000..2561360 --- /dev/null +++ b/Chapter08/millo/README.md @@ -0,0 +1,112 @@ + +## 기본 타입, 컬렉션, 배열 + +**다루는 내용** +- 코틀린의 원시 타입 및 기본 타입 +- 코틀린 타입과 자바 타입과의 관계 +- 코틀린에서의 컬렉션과 배열 및 이들의 널 가능성과 상호운용성 + +### 기본 타입 + +#### 자바에서의 원시 타입과 참조 타입 +- 자바에서는 원시 타입(primitive type)과 참조 타입(reference type)을 구분한다 + - 원시 타입(int, boolean, char 등)의 변수에는 그 값이 직접 들어간다 + - 참조 타입(Integer, Boolean, Character 등)의 변수에는 메모리상의 객체 위치가 들어간다 +- 자바에서 원시 타입에 대해 메서드를 호출하거나 컬렉션에 담을 수 없다 + - 참조 타입이 필요한 경우 특별한 래퍼 타입으로 원시 타입을 감싸서 사용한다 + - `Collection`가 아닌 `Collection` + +#### 코틀린에서의 원시 타입과 참조 타입 +- 코틀린은 원시 타입과 래퍼 타입을 구분하지 않고 항상 같은 타입으로 취급한다 + - 그렇다면 모든 타입을 항상 객체로 표현하는 것은 아니고 실행 시점에 가장 효율적인 방식으로 표현된다 + - 대부분의 경우 코틀린의 숫자 타입은 자바의 원시 타입으로 컴파일 되고, 컬렉션과 같은 제네릭 클래스를 사용하는 경우는 래퍼 타입에 해당하는 객체로 컴파일된다 + +#### 부호 없는 숫자 타입 +- 양수를 표현하기 위해 모든 비트 범위를 사용하고 싶을 때 코틀린은 JVM의 일반적인 원시 타입을 확장해 부호 없는 타입을 제공한다 +- 부호 없는 숫자 타입들은 상응하는 부호 있는 타입의 범위를 시프트해서 같은 크기의 메모리를 사용해 더 큰 양수 범위를 표현할 수 있게 한다 +- 코틀린의 부호 없는 수도 다른 원시 타입과 마찬가지로 필요할 때만 래핑된다 + - JVM 자체는 부호 없는 수에 대한 원시 타입을 제공하지 않지만 인라인 클래스를 통해 추상화를 제공한다 + - 즉, 메모리에는 Int나 Long 그대로 있다가 코드에서 타입 구분할 때만 UInt, ULong 처럼 행동해 성능 손해 없이 부호 없는 수를 다룬다 + +| 타입 | 크기 | 값의 범위 | +|:--|:--|:---------------------------------------| +| `UByte` | 8비트 (2⁸) | 0 ~ 255 (2⁸-1) | +| `UShort` | 16비트 (2¹⁶) | 0 ~ 65,535 (2¹⁶-1) | +| `UInt` | 32비트 (2³²) | 0 ~ 4,294,967,295 (2³²-1) | +| `ULong` | 64비트 (2⁶⁴) | 0 ~ 18,446,744,073,709,551,615 (2⁶⁴-1) | + +#### 널이 될 수 있는 원시 타입 +- 코틀린의 nullable한 타입은(`Int?`) 자바의 원시 타입으로 표현할 수 없기 때문에 자바의 래퍼 타입으로(`Integer`) 컴파일된다 +- 코틀린 컴파일러는 두 값이 nullable할 때 무조건 먼저 null 체크한 뒤 일반적인 값으로 다루도록 허용한다 +- 자바 클래스를 가져와서 쓸 때에는 그냥 nullable인지 아닌지만 신경 쓰면 된다. + - 내부적으로 알아서 `int` or `Integer` 언박싱/박싱해서 컴파일해준다 + +**제네릭 클래스의 경우** +- 제네릭 클래스의 경우 JVM은 제네릭 타입이 타입 소거 방식으로 구현되기 때문에 타입 파라미터에 원시 타입을 허용하지 않는다 + - 타입 소거 방식? JVM은 제네릭 타입 정보를 컴파일 이후에 버린다. 즉, 런타임에는 타입 인자가 사라지고 Object처럼 다룬다 + ```kotlin + fun checkType(value: T) { + if (value is String) { } // OK + if (value is T) { } // T가 뭔지 모르므로 불가능 + } + ``` + - 코틀린은 `inline`과 `reified`의 조합으로 이 타입 소거 문제를 피할 수 있다 + - 함수를 인라인하면 타입 파라미터도 복붙으로 삽입될 수 있어서 타입 정보도 런타입까지 살아남게(`reified`) 할 수 있는 것 + ```kotlin + inline fun checkType(value: Any) { + if (value is T) println("Yes, it's a ${T::class}") + else println("No, it's not a ${T::class}") + } + + fun main() { + checkType("Hello") // Yes, it's a class kotlin.String + checkType("Hello") // No, it's not a class kotlin.Int + } + ``` +- 따라서 제네릭에선 항상 박스 타입을 사용해야 한다 + +#### 수 변환 +- 코틀린은 한 타입의 수를 다른 타입의 수로 자동 변환하지 않기 때문에 명시적으로 변환 메서드를 사용해야 한다 +- 코틀린은 Boolean을 제외한 모든 원시 타입에 대해 양방향 변환 함수를 모두 제공한다 + - 표현 범위가 더 좁은 타입으로 변환하면서 값을 벗어나는 경우에는 일부를 잘라내는 함수도 있다 + +**숫자 리터럴** +- 보통은 변환 함수 호출 필요 없이 타입을 표현하는 문자를 붙여주기만 하면 된다 +- 직접 변환하지 않더라도 숫자 리터럴을 타입이 알려진 변수에 대입하거나 인자로 넘기면 컴파일러가 필요한 변환을 자동으로 해준다 +- 산술 연산자는 적당한 타입의 값을 받아들일 수 있도록 이미 오버로드되어 있다 + - ex) Int + Long은 Long 기준 + +| 종류 | 예시 | 설명 | +|:--|:-----------------------------------|:--| +| **10진수(Int, Long 기본)** | `123`, `4567` | 일반 정수 | +| **Long** | `123L`, `4567L` | 끝에 `L` 또는 `l` 추가 | +| **Unsigned(Int, Long)** | `123u`, `4567U`, `123uL`, `4567UL` | `u` 또는 `U` 접미사로 부호 없는 타입 | +| **16진수** | `0x1F`, `0X1F` | 접두사 `0x` 또는 `0X` 사용 | +| **2진수** | `0b1010`, `0B1010` | 접두사 `0b` 또는 `0B` 사용 | +| **8진수** | (Kotlin은 8진수 지원 X) | 사용 불가 (자바는 지원했지만 코틀린은 아님) | +| **부동소수점(Double 기본)** | `123.45`, `1.0e3`, `3.14E-2` | 지수 표기 가능 (`e`, `E` 사용) | +| **Float** | `123.45f`, `1.0e3f`, `3.14E-2F` | 끝에 `f` 또는 `F` 추가 | + +**문자 리터럴** + +| 리터럴 | 의미 | +|:--|:--| +| `'\n'` | 줄바꿈 (New Line) | +| `'\t'` | 탭 (Tab) | +| `'\b'` | 백스페이스 (Backspace) | +| `'\r'` | 캐리지 리턴 (Carriage Return) | +| `'\''` | 작은따옴표 자체 (Single Quote) | +| `'\"'` | 큰따옴표 자체 (Double Quote) | +| `'\\'` | 역슬래시 자체 (Backslash) | + + +**문자열 변환** +- 문자열을 원시 타입으로 변환하는 여러 함수를 제공하는데 변환에 실패하면 `NumberFormatException`이 발생한다 +- `NumberFormatException`를 명시적으로 처리하기 귀찮을 때에는 실패를 `null`로 돌려주는 함수도 있다 +- 문자열이 null이 아니고 정확히 단어 true와 같으면(대소문자 구분 X) Boolean `true`로, 그 외에는 `false`로 변환해주는 `toBoolean`도 있다 +- 정확히 `true`나 `false`와 일치시키고 싶을 땐 `toBooleanStrict`를 쓰면 일치하지 않을 때 예외를 던져준다 + + +### Any와 Any? +- 자바의 최상위 타입 `Object`처럼 코틀린은 `Any`가 모든 널이 될 수 없는 타입의 조상 타입이다 + From 4c9c1377960df2833ecead966a7a6abce64877c6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EC=9D=B4=EC=A3=BC=EC=84=B1?= <32183520@dankook.ac.kr> Date: Sun, 27 Apr 2025 17:21:05 +0900 Subject: [PATCH 5/6] ~ 377 --- Chapter08/millo/README.md | 233 +++++++++++++++++++++++++++++++++++++- Chapter08/millo/img.png | Bin 0 -> 72019 bytes 2 files changed, 232 insertions(+), 1 deletion(-) create mode 100644 Chapter08/millo/img.png diff --git a/Chapter08/millo/README.md b/Chapter08/millo/README.md index 2561360..727ebc9 100644 --- a/Chapter08/millo/README.md +++ b/Chapter08/millo/README.md @@ -108,5 +108,236 @@ ### Any와 Any? -- 자바의 최상위 타입 `Object`처럼 코틀린은 `Any`가 모든 널이 될 수 없는 타입의 조상 타입이다 +- 자바의 최상위 타입 `Object`이지만 참조 타입만 그 계층에 포함되며 원시 타입은 포함되지 않는다 + - 즉, Object 타입의 객체가 필요한 경우 원시 타입은 래퍼 타입으로 감싸야만 한다 +- 코틀린은 모든 타입의 조상 타입으로 `Any` 혹은 `Any?`를 제공하는데 원시 타입과 래퍼 타입 모두 포함된다 + - 자바와 마찬가지로 원시 타입의 값을 `Any`타입의 변수에 대입하면 래퍼 타입으로 자동으로 박싱된다 +- `Any`는 `equals`, `hashCode`, `toString` 메서드를 제공하기 때문에 모든 코틀린 클래스가 이 메서드들을 가지고 있다 + - `Any`는 자바의 `Object`로 컴파일되지만 `Object`의 `wait`나 `notify`같은 메서드들은 `Any`에서 사용할 수 없다 (사용하려면 캐스팅 필요) + +### Unit: 코틀린의 void +- 코틀린은 자바의 `void`와 같은 역할을 하는 `Unit`이라는 타입을 제공한다 + - 실제로 제네릭 함수를 오버라이드하지 않는 한 자바 `void`로 컴파일되기 때문에 성능 손실 없다 + - 코틀린 쓸 땐 타입처럼 쓰고 자바에서 쓸 땐 `void`처럼 동작 + +**`void`와 다른 `Unit`만의 특성** +1. 실제로 존재하는 일반 타입이다 + - 타입 인자(제네릭)으로 사용할 수 있고, 값으로 전달하거나 저장할 수도 있다 +2. 암시적으로 `Unit` 값을 반환한다 + - `Unit` 타입 함수는 명시적으로 `return Unit`을 적지 않아도 자동으로 `Unit` 객체를 반환한다 +3. 제네릭 함수에서 타입 인자로 사용 가능 + - 자바의 경우 값 없음을 `Void`로 표현했지만 `Void`는 진짜 타입이 아니라서 메모리에 어떤 실제 값을 담을 수 없는 껍데기 타입이라 반드시 `return null`을 해줘야 한다 + - 코틀린은 실제 값이 존재하는 정식 타입인 `Unit`을 제네릭 타입 인자로 넣을 수 있다 +```kotlin +interface Processor { + fun process(): T +} + +class NoResultProcessor : Processor { + override fun process() { // Unit을 반환하지만 타입 지정 필요 없다 + // 업무 처리 코드 .. // 명시적으로 return할 필요 없다 + } +} +``` + +### Nothing 타입 +- 코틀린은 반환값 개념 자체가 의미없는 함수가 일부 존재하는데 이런 함수의 반환 타입을 `Nothing`으로 지정한다 + - ex) 테스트 라이브러리 `fail`, 무한 루프 함수 ... +- `Nothing`은 절대 정상적으로 값을 반환하지 않는다는 의도를 코드에 명시적으로 나타낼 때 사용한다 + - 코틀린 타입 시스템 안에서 정상적으로 끝나지 않을 함수라는 것을 컴파일 타임에 보장할 수 있게 한다 +- `Nothing`을 반환하는 함수를 엘비스 연산자(`?:`)와 함께 사용해서 전제 조건을 검사할 수 있다 + - Nothing 이후 코드가 도달 불가로 인식되어서 컴파일러가 경고하거나 최적화할 수 있다 +```kotlin +fun fail(message: String): Nothing { + throw IllegalArgumentException(message) +} + +fun main() { + val name: String = inputName ?: fail("No name") // null이라면 이후 코드 unreachable + // null이 아니라면 안전하게 String 타입 확정 (스마트 캐스트) +} +``` + +--- + +## 컬렉션과 배열 + +### 널 여부에 따른 컬렉션 +- 코틀린은 컬렉션을 다룰 때 컬렉션과 그 안 요소 각각에 대해 널 가능성을 구분한다 + - 컬렉션 자체의 null 여부, 컬렉션 안 요소의 null 여부를 다 구분해서 타입을 정한다 + - ex) `List` `List` `List?` `List?` + +**널이 될 수 있는 값으로 이뤄진 컬렉션** +```kotlin +fun readNumbers(text: String): List { + return text.lineSequence() + .map { it.toIntOrNull() } + .toList() +} +``` + +**널이 될 수 있는 값으로 이뤄진 컬렉션의 널을 걸러내기** +- `filterNotNull`을 사용하면 null이 아닌 값만 필터링할 수 있다 +- 널을 걸러낸 상태이므로 널이 아님을 보장할 수 있어 새로운 컬렉션 `List`로 반환된다 +```kotlin +fun readNumbers(text: String): List { + return text.lineSequence() + .map { it.toIntOrNull() } + .filterNotNull() + .toList() +} +``` + +### 읽기 전용 및 가변 컬렉션 +- 코틀린은 자바와 다르게 컬렉션 안의 데이터에 접근하는 인터페이스와 컬렉션 안의 데이터를 변경하는 인터페이스를 분리했다 + +**`kotlin.collections.Collection`** +- 읽기 전용 컬렉션 (ex: `List`, `Set`, `Map`) +- 컬렉션 안의 원소에 대해 이터레이션, 크기, 검사 등 여러 연산을 수행할 수 있지만 원소를 추가하거나 제거할 수는 없다 + +**`kotlin.collections.MutableCollection`** +- 가변 컬렉션 (ex: `MutableList`, `MutableSet`, `MutableMap`) +- 일반 인터페이스인 `kotlin.collections.Collection`를 확장하면서 원소를 추가, 삭제 등의 데이터를 수정할 수 있는 메서드를 제공한다 + + +**`var` 대신 `val`을 사용하는 것처럼 가능하면 읽기 전용인 `Collection`을 사용하자** +- 컬렉션을 다룰 때도 가능하면 읽기 전용 컬렉션을 사용해 오직 읽기만 가능하도록 하는 것이 좋다 + +**하지만 읽기 전용 컬렉션이 불변하다는 뜻은 아니다** +- 코틀린의 읽기 전용 인터페이스(`List`, `Set`, 등)은 그 타입을 통해서는 변경할 수 없다는 뜻일 뿐이고 실제로 불변이라는 보장은 없다 + - 즉, 내가 읽기 전용으로 보고 있는 `List`는 수정 못하지만 다른 누군가가 같은 인스턴스를 `MutableList`로 변경할 수도 있는 것이다 + - 즉, 뷰만 제공하는 것이지 그 뷰 안 객체를 다른 누군가가 변경할 수 있는 것 +- 실행 도중에 이렇게 컬렉션이 바뀌게 되면 `ConcurrentModificationException`과 같은 오류가 발생할 수도 있다 + - 전달받은 컬렉션이나 객체가 외부에서 변경될 위험이 있을 때 방어적 복사를 통해 안전하게 사용하자 +- 특히 멀티 스레드 환경에서 데이터들이 동시에 수정되면서 예상치 못한 오류를 만날 수 있다 + +**멀티 스레드에서 일관성있게 스레드 안전하게 컬렉션 다루기** +1. `synchronized` 블록 동기화 + - 같은 프로세스 안의 스레드들끼리 동시 접근을 막아준다 + - 즉, JVM 안에서만 유효하기에 같은 서버, 같은 메모리 안에서만 안전하다 +2. `Immutable` 컬렉션 활용 + - 표준 라이브러리에는 없지만 `kotlinx.collections.immutable` 라이브러리에서 불변 컬렉션 인터페이스를 제공한다 + - 아예 컬렉션 내용 자체를 변경할 수 없게 만들어서 데이터 수정 시도 자체를 막는다 +3. `Concurrent` 컬렉션 활용 + - 자바의 `java.util.concurrent` 컬렉션을 활용해 동시 접근을 막는다 + - ex) `ConcurrentHashMap`, `CopyOnWriteArrayList` 등 + +**분산 시스템의 경우 여러 파드가 각각 서로 다른 프로세스를 띄우기에 서로 메모리를 공유하지 않아서 무용지물인 점 유의하자** + - 위 3가지 전부 단일 프로세스에서의 멀티 스레드 환경에서의 안전성을 보장하는 방법일 뿐이다 + - 레디스 분산락같은 도구를 활용해 여러 프로세스에 공유되는 락을 걸어줄 필요가 있다 + + +### 코틀린 컬렉션과 자바 컬렉션의 밀접한 관계 +- 모든 코틀린 컬렉션은 자바 컬렉션 인터페이스의 인스턴스로 대응된다 +- 따라서 코틀린과 자바 사이를 오갈 때 따로 래퍼 클래스를 만들거나 복사하거나 변환할 필요가 없다 + +**코틀린 컬렉션 인터페이스 계층 구조** + +![img.png](img.png) +- 코틀린은 자바 표준 클래스가(`ArrayList`, `HashSet` 등) 각각 코틀린의 인터페이스를(`MutableList`, `MutableSet` 등) 상속한 것처럼 취급한다 +- 이런 방식으로 코틀린은 자바 표준 클래스를 읽기 전용 인터페이스와 변경 가능 인터페이스를 분리한다 + +**컬렉션 생성 함수** + +| 타입 | 읽기 전용 생성 함수 | 가변 생성 함수 | 빌더 기반 생성 함수(가변) | +|:---|:---|:---|:----------------| +| `List`, `MutableList` | `listOf(vararg T)` | `mutableListOf(vararg T)`, `arrayListOf(vararg T)` | `buildList { }` | +| `Set`, `MutableSet` | `setOf(vararg T)` | `mutableSetOf(vararg T)`, `hashSetOf(vararg T)` | `buildSet { }` | +| `Map`, `MutableMap` | `mapOf(vararg Pair)` | `mutableMapOf(vararg Pair)`, `hashMapOf(vararg Pair)` | `buildMap { }` | + +**자바와 코틀린 혼용 시 주의점** +```kotlin +fun provideNames(): List { + return mutableListOf("Alice", "Bob", "Charlie") +} +``` +```java +// Java 코드 +import java.util.List; + +public class JavaSide { + public static void main(String[] args) { + List names = KotlinCodeKt.provideNames(); + names.add("Diana"); + names.set(0, null); // 자유롭게 수정 가능한 문제 + System.out.println(names); // 출력: [null, Bob, Charlie, Diana] + } +} +``` +- 자바는 읽기 전용과 가변 컬렉션을 구분하지 않아서 코틀린에서 읽기 전용으로 해도 자바는 그 컬렉션을 변경할 수 있다 +- 즉, 컬렉션을 변경하는 자바 메서드에게 읽기 전용 컬렉션을 넘겨도 코틀린 컴파일러가 이를 막을 수가 없다 + - 요소가 null이 불가하도록 한 컬렉션을 넘겨줬을 때 자바 메서드가 null을 넣어버릴 수도 있는 것 +- 컬렉션을 자바로 넘기는 코틀린 코드가 있다면 그 타입이 무력화됨을 주의해야 하며 자바 쪽 코드가 이 컬렉션을 수정할 수도 있다는 걸 염두에 두고 타입 선언을 해야한다 + + + + +### 자바에서 선언한 컬렉션은 코틀린에서 플랫폼 타입으로 취급된다 +- 널 관련 정보가 없기에 컴파일러는 어느쪽으로도 사용할 수 있도록 플랫폼타입으로 허용한다 +- 컬렉션 타입이 시그니처에 들어간 자바 메서드 구현을 오버라이드하려는 경우 읽기 전용 컬렉션과 가변 컬렉션의 차이가 문제가 도리 수 있다 + - 이런 경우 오버라이드하려는 메서드의 자바 컬렉션 타입을 어떤 코틀린 컬렉션 타입으로 표현할지 명시적으로 정해줘야 한다 + +**타입 선택 기준** +1. 컬렉션이 null이 될 수 있는가? +2. 컬렉션의 원소가 null이 될 수 있는가? +3. 컬렉션이 읽기 전용인가? 가변인가? + +**자바 인터페이스 구현 예제 1)** +```java +interface FileContentProcessor { + void processContents( + File path, + byte[] binaryContents, + List textContents + ); +} +``` +1. 일부 파일이 이진 파일이고 이진 파일은 텍스트로 표현할 수 없는 경우가 있으니까 리스트는 nullable하게 +2. 파일의 각 줄은 null이 될 수 없으므로 리스트의 원소는 null이 될 수 없도록 +3. 이 리스트는 파일의 내용을 표현하며 그 내용을 바꿀 필요가 없으므로 읽기 전용으로 + +```kotlin +class FileIndexer : FileContentProcessor { + override fun processContents( + path: File, + binaryContents: ByteArray?, + textContents: List? + ) { + // .. + } +} +``` + +**자바 인터페이스 구현 예제 2)** +```java +interface DataParser { + void parseData( + String input, + List output, + List errors + ); +} +``` +1. 호출하는 쪽에서 항상 오류 메세지를 받아야 하므로 errors는 읽기 전용으로 +2. 출력 리스트의 모든 원소마다 오류가 발생하는 것은 아니므로 errors의 원소는 nullable하게 +3. 구현 코드에서 원소를 추가할 수 있어야 해서 가변으로 + +```kotlin +class PersonParser : DataParser { + override fun parseData( + input: String, + output: MutableList, + errors: MutableList + ) { + // .. + } +} +``` + +**자바 인터페이스나 클래스가 어떤 맥락에서 어떻게 활용되는지 정확하게 판단 후에 코틀린으로 구현해야 한다** + + +### 성능 및 상호운용을 위해 객체의 배열이나 원시 타입의 배열 만들기 + + diff --git a/Chapter08/millo/img.png b/Chapter08/millo/img.png new file mode 100644 index 0000000000000000000000000000000000000000..414cf31863751726e70f8fe48ec65bb1e6eaf48c GIT binary patch literal 72019 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 literal 0 HcmV?d00001 From c633613d70f2d61d3ca3801b9471b81bc539295c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EC=9D=B4=EC=A3=BC=EC=84=B1?= <32183520@dankook.ac.kr> Date: Sun, 27 Apr 2025 20:07:15 +0900 Subject: [PATCH 6/6] =?UTF-8?q?~=20ch8=20=EC=99=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- Chapter08/millo/README.md | 110 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 110 insertions(+) diff --git a/Chapter08/millo/README.md b/Chapter08/millo/README.md index 727ebc9..490a302 100644 --- a/Chapter08/millo/README.md +++ b/Chapter08/millo/README.md @@ -338,6 +338,116 @@ class PersonParser : DataParser { ### 성능 및 상호운용을 위해 객체의 배열이나 원시 타입의 배열 만들기 +- 코틀린 `Array`는 타입 파라미터를 가진 제네릭 클래스이다 +- 배열의 원소 타입은 타입 파라미터로 결정되며 배열의 크기는 고정된다 + +**왜 배열이 컬렉션보다 성능이 좋은가?** + +| 구분 | 배열 (Array) | 컬렉션 (List, Set 등) | +|:---|:---|:---| +| 메모리 구조 | 연속된 메모리 블록 | 내부적으로 포장(wrapper)된 객체 구조 | +| 접근 속도 | O(1), 바로 인덱스 접근 | O(1) 대부분 가능하지만 추가 오버헤드 존재 | +| 메모리 사용량 | 낮음 (원소만 저장) | 높음 (객체 + 메타데이터 저장) | +| 생성 비용 | 낮음 (크기만 지정) | 높음 (구조 확장/포장 비용) | +| 추가/삭제 지원 | 불가 (고정 크기) | 가능 (가변 크기 구조) | +| 최적 사용 케이스 | 대량의 데이터를 빠르게 읽을 때 | 데이터 추가/삭제가 필요한 경우 | + +**배열을 만드는 다양한 방법** +1. `arrayOf(vararg T)` + - 인자로 받은 원소들을 포함한 배열 생성 +2. `arrayOfNulls(size: Int)` + - 지정한 크기만큼 null로 초기화된 배열 생성 + - 원소 타입이 nullable한 타입일 경우에만 사용 가능 +3. `Array(size: Int, init: (Int) -> T)` 생성자 + - 배열 크기와 람다를 받아 람다를 호출해서 각 배열 원소를 초기화한다 + - 람다는 배열 원소의 인덱스를 인자로 받아 배열의 해당 위치에 들어갈 원소를 반환한다 + - 원소가 널이 아닌 배열을 만들어야 하는 경우 사용한다 +```kotlin +fun main() { + val letters = Array(26) { i -> ('a' + i).toString() } + println(letters.joinToString("")) + // 출력: abcdefghijklmnopqrstuvwxyz +} +``` + +**컬렉션을 배열로 변환하는 방법** +```kotlin +fun main() { + val strings = listOf("a", "b", "c") + println("%s/%s/%s".format(*strings.toTypedArray())) // 출력: a/b/c +} +``` +- `toTypedArray()`를 사용하면 컬렉션을 배열로 변환할 수 있다 + + +**JVM 상에서는 항상 객체 배열(`Object[]`)로 컴파일된다** +- 즉, `Array`는 사실 내부적으로 `Array` 박싱된 타입으로 동작한다 +- 원시 타입을 배열로 담고 싶어도 박싱된 객체 타입으로 저장되기에 성능이 느려질 수 있다. +- 따라서 원시 타입을 위한 특별한 배열 클래스를 제공한다 + +**원시 타입의 배열을 만드는 방법** +1. 각 배열 타입의 생성자는 size 인자를 ㅂ다아 해당 워닛 타입의 기본값으로 초기화된 size 크기의 배열을 반환한다 +```kotlin +val numbers = IntArray(5) +println(numbers.joinToString()) // 출력: 0, 0, 0, 0, 0 +``` + +2. 팩토리 함수는 여러 값을 가변 인자로 받아 그런 값이 들어간 배열을 반환한다 +```kotlin +val numbers = intArrayOf(1, 2, 3, 4, 5) +println(numbers.joinToString()) // 출력: 1, 2, 3, 4, 5 +``` + +3. 크기와 람다를 인자로 받는 다른 생성자를 사용한다 +```kotlin +val squares = IntArray(5) { i -> (i + 1) * (i + 1) } +println(squares.joinToString()) // 출력: 1, 4, 9, 16, 25 +``` + +4. 이미 박싱된 값이 들어있는 컬렉션이나 배열인 경우 확장함수 활용해 변환한다 +```kotlin +val boxedNumbers: List = listOf(1, 2, 3, 4, 5) +val numbers = boxedNumbers.toIntArray() +``` +**왜 원시 타입 배열이 빠른가?** + +| 구분 | Array (객체 배열) | IntArray (원시 타입 배열) | +|:---|:---|:---| +| 저장 방식 | 박싱된 객체(Integer) 배열 (Object[]) | 원시 타입(int[]) 직접 저장 | +| 메모리 사용량 | 높음 (객체 참조 + 값) | 낮음 (값만 저장) | +| 접근 속도 | 느림 (객체 참조를 따라가야 함) | 빠름 (바로 값 읽기) | +| GC 부담 | 큼 (객체 수 많음) | 적음 (순수 데이터만 존재) | +| 실행 성능 | 약 1.5~2배 느림 | 빠름 | +| 최적 사용 케이스 | 복합 타입 관리 | 대량 숫자 데이터 처리, 고성능 요구 시 | + +**컬렉션에 사용할 수 있는 모든 확장함수를 배열에도 제공한다** +- filter, map 등의 확장 함수를 배열에도 똑같이 사용할 수 있다 + - 원시 타입인 원소로 이루어진 배열도 마찬가지 + +**배열의 요소와 함께 인덱스도 다루고 싶다면? `forEachIndexed`** +- 배열의 모든 원소에 대해 인자로 받은 람다를 호출해준다 +- 이 때 배열의 원소와 그 원소의 인덱스를 람다에게 인자로 전달한다 +```kotlin +fun main(args: Array) { + args.forEachIndexed { index, element -> + println("args[$index] = $element") + } +} +``` +### **왜 코틀린은 배열보다 컬렉션을 권장할까?** +- 코틀린은 편의성과 안전성, 가독성을 더 중요하게 생각하기 때문에 + 특별히 성능이 중요한 경우가 아니라면, 기본적으로 배열보다 컬렉션(List, Set, Map)을 사용하는 것을 권장한다. + +| 비교 항목 | 배열 (Array) | 컬렉션 (List, Set 등) | +|:--|:--|:--| +| 구조 | 고정 크기, 연속된 메모리 | 가변 크기, 동적 구조 | +| 접근 속도 | 매우 빠름 (O(1)) | 빠름 (O(1)), 추가적인 오버헤드 | +| 크기 조정 | 불가 (고정) | 가능 (동적으로 확장/축소) | +| 제공 기능 | 최소한의 기능 (get, set) | 다양한 고차 함수(map, filter, etc) 제공 | +| 읽기/쓰기 구분 | 불가능 (항상 가변) | 읽기 전용(List)과 가변(MutableList) 구분 가능 | +| 코드 가독성 | 낮음 | 높음 (표현력 풍부) | +| 메모리 효율 | 높음 (원시 타입 배열 기준) | 상대적으로 높지 않음 | +| 추천 상황 | 성능 최적화가 필요한 경우, 자바 상호운용성 필요 시 | 일반적인 개발 상황(앱/서버 등) |