Skip to content

Commit 664d943

Browse files
committed
Update contents
1 parent 9215925 commit 664d943

File tree

5 files changed

+320
-1
lines changed

5 files changed

+320
-1
lines changed

README.md

Lines changed: 320 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4586,4 +4586,323 @@ fun showProgress(progress: Int) {
45864586
We're 100% done!
45874587
```
45884588

4589-
4589+
실행 시점에 숫자 타입은 가능한 한 가장 효율적인 방식으로 표현된다.
4590+
대부분의 경우 코틀린의 Int 타입은 자바 int 타입으로 컴파일된다.
4591+
이런 컴파일이 불가능한 경우는 컬렉션과 제네릭 클래스를 사용하는 경우뿐이다.
4592+
예를 들어 Int 타입을 컬렉션의 타입 파라미터로 넘기면 그 컬렉션에는 Int의 래퍼 타입에 해당하는 java.lang.Integer 객체가 들어간다.
4593+
4594+
자바 원시 타입에 해당하는 타입은 다음과 같다.
4595+
4596+
- 정수 타입: Byte, Short, Int, Long
4597+
- 부동소수점 수 타입: Float, Double
4598+
- 문자 타입: Char
4599+
- 불리언 타입: Boolean
4600+
4601+
Int와 같은 코틀린 타입에는 널 참조가 들어갈 수 없기 때문에 쉽게 그에 상응하는 자바 원시 타입으로 컴파일할 수 있다.
4602+
반대로 자바 원시 타입의 값은 결코 널이 될 수 없으므로 자바 원시 타입을 코틀린에서 사용할 때도 (플랫폼 타입이 아니라) 널이 될 수 없는 타입으로 취급할 수 있다.
4603+
4604+
### - 널이 될 수 있는 원시 타입: Int?, Boolean? 등
4605+
4606+
null 참조를 자바의 참조 타입의 변수에만 대입할 수 있기 때문에 널이 될 수 있는 코틀린 타입은 자바 원시 타입으로 표현할 수 없다.
4607+
따라서 코틀린에서 널이 될 수 있는 원시 타입을 사용하면 그 타입은 자바의 래퍼 타입으로 컴파일된다.
4608+
4609+
```kotlin
4610+
data class Person(val name: String,
4611+
val age: Int? = null) {
4612+
fun isOlderThan(other: Person): Boolean? {
4613+
if (age == null || other.age == null)
4614+
return null
4615+
return age > other.age
4616+
}
4617+
}
4618+
4619+
>>> println(Person("Sam", 35).isOlderThan(Person("Amy", 42)))
4620+
false
4621+
>>> println(Person("Sam", 35).isOlderThan(Person("Jane")))
4622+
null
4623+
```
4624+
4625+
널이 될 가능성이 있으므로 Int? 타입의 두 값을 직접 비교할 수는 없다.
4626+
4627+
Person 클래스에 선언된 age 프로퍼티의 값은 java.lang.Integer로 저장된다.
4628+
4629+
제네릭 클래스의 경우 래퍼 타입을 사용한다.
4630+
어떤 클래스의 타입 인자로 원시 타입을 넘기면 코틀린은 그 타입에 대한 박스 타입을 사용한다.
4631+
예를 들어 다음 문장에서는 null 값이나 널이 될 수 있는 타입을 전혀 사용하지 않았지만 만들어지는 리스트는 래퍼인 Integer 타입으로 이뤄진 리스트다.
4632+
4633+
```kotlin
4634+
val listOfInts = listOf(1, 2, 3)
4635+
```
4636+
4637+
자바 가상머신에서는 타입 인자로 원시 타입을 허용하지 않는다.
4638+
따라서 자바나 코틀린 모두에서 제네릭 클래스는 항상 박스 타입을 사용해야 한다.
4639+
4640+
### - 숫자 변환
4641+
4642+
코틀린은 한 타입의 숫자를 다른 타입의 숫자로 자동 변환하지 않는다.
4643+
4644+
```kotlin
4645+
val i = 1
4646+
val l: Long = i // "Error: type mismatch" 컴파일 오류 발생
4647+
```
4648+
4649+
대신 직접 메소드를 호출해야 한다.
4650+
4651+
```kotlin
4652+
val i = 1
4653+
val l: Long = i.toLong()
4654+
```
4655+
4656+
코틀린은 Boolean을 제외한 모든 원시 타입에 대한 변환 함수를 제공한다.
4657+
양방향 변환 함수가 모두 제공된다.
4658+
표현 범위가 더 넓은 타입으로 변환하는 함수도 있고(`Int.toLong()`),
4659+
표현 범위가 더 좁은 타입으로 변환하면서 값을 벗어나는 경우에는 일부를 잘라내는 함수도 있다(`Long.toInt()`).
4660+
4661+
코틀린은 개발자의 혼란을 피하기 위해 타입 변환을 명시하기로 결정했다.
4662+
타입을 비교하는 경우 두 박스 타입 간의 equals 메소드는 그 안에 들어있는 값이 아니라 박스 타입 객체를 비교한다.
4663+
따라서 자바에서 new Integer(42).equals(new Long(42))는 false다.
4664+
4665+
```kotlin
4666+
val x = 1 // Int 타입인 변수
4667+
val list = listOf(1L, 2L, 3L) // Long 값으로 이뤄진 리스트
4668+
x in list // 묵시적 타입 변환으로 인해 false임
4669+
```
4670+
4671+
묵시적 타입 변환이 되는건가..?
4672+
4673+
코틀린에서는 타입을 명시적으로 변환해서 같은 타입의 값으로 만든 후 비교해야 한다.
4674+
4675+
```kotlin
4676+
>>> val x = 1
4677+
>>> println(x.toLong() in listOf(1L, 2L, 3L))
4678+
true
4679+
```
4680+
4681+
> #### 원시 타입 리터럴
4682+
> 코틀린은 소스코드에서 단순한 10진수(정수) 외에 다음과 같은 숫자 리터럴을 허용한다.
4683+
> - L 접미사가 붙은 Long 타입 리터럴: 123L
4684+
> - 표준 부동소수점 표기법을 사용한 Double 타입 리터럴: 0.12, 2.0, 1.2e10, 1.2e-10
4685+
> - f나 F 접미사가 붙은 Float 타입 리터럴: 123.4f, .456F, 1e3f
4686+
> - 0x나 0X 접두사가 붙은 16진수 리터럴: 0xCAFEBABE, 0xbcdL
4687+
> - 0b나 0B 접두사가 붙은 2진수 리터럴: 0b000000101
4688+
>
4689+
> 코틀린 1.1부터는 숫자 리터럴 중간이 밑줄(_)을 넣울 수 있다(1_234, 1_0000_0000_0000L, 1_000.123_456, 0b0100_0001 등)
4690+
4691+
숫자 리터럴을 사용할 때는 보통 변환 함수를 호출할 필요가 없다.
4692+
추가로 산술 연산자는 적당한 타입의 값을 받아들일 수 있게 이미 오버로드돼 있다.
4693+
4694+
```kotlin
4695+
fun foo(l: Long) = println(l)
4696+
4697+
>>> val b: Byte = 1 // 상수 값으 적절한 타입으로 해석된다.
4698+
>>> val l: = b + 1L // + 는 Byte와 Long을 인자로 받을 수 있다.
4699+
>>> foo(42) // 컴파일러는 42를 Long 값으로 해석한다.
4700+
42
4701+
```
4702+
4703+
코틀린은 오버플로우를 검사하느라 추가 비용을 들이지 않는다.
4704+
4705+
> #### 문자열을 숫자로 변환하기
4706+
> 코틀린 표준 라이브러리는 문자열을 원시 타입으로 변환하는 여러 함수를 제공한다(toInt, toByte, toBoolean 등).
4707+
>
4708+
> ```kotlin
4709+
> >>> println("42".toInt())
4710+
> 42
4711+
> ```
4712+
>
4713+
> 이런 함수는 문자열의 내용을 각 원시 타입을 표기하는 문자열로 파싱한다.
4714+
파싱에 실패하면 NumberFormatException이 발생한다.
4715+
4716+
### - Any, Any?: 최상위 타입
4717+
4718+
자바에서 클래스 계층의 최상위 타입은 Object
4719+
코틀린에서 모든 널이 될 수 없는 타입의 조상 타입은 Any 다.
4720+
하지만 자바에서는 원시 타입은 Object 계층에 들어있지 않다.
4721+
코틀린에서는 Any가 원시 타입을 포함한 모든 타입의 조상 타입이다.
4722+
4723+
```kotlin
4724+
val answer: Any = 42 // Any가 참조 타입이기 때문에 42가 박싱된다.
4725+
```
4726+
4727+
Any는 널이 될 수 없는 타입이므로 Any 타입의 변수에는 null이 들어갈 수 없다.
4728+
널을 포함하려면 Any? 타입을 사용해야 한다.
4729+
4730+
내부에서 Any 타입은 java.lang.Object에 대응한다.
4731+
더 정확히 말하면 널이 될 수 있는지 여부를 알 수 없으므로 플랫폼 타입인 Any!로 취급한다.
4732+
코틀린 함수가 Any를 사용하면 자바 바이트코드의 Object로 컴파일 된다.
4733+
4734+
모든 코틀린 클래스에는 toString, equals, hachCode라는 세 메소드가 들어있다.
4735+
이 세 메소드는 Any에 정의된 메소드를 상속한 것이다.
4736+
하지만 java.lang.Object에 있는 다른 메소드(wait나 notify 등)는 Any에서 사용할 수 없다.
4737+
그런 메소드를 호출하고 싶다면 Object 타입으로 값을 캐스트 해야한다.
4738+
4739+
### - Unit 타입: 코틀린의 void
4740+
4741+
```kotlin
4742+
fun f(): Unit { ... }
4743+
```
4744+
4745+
이는 반환 타입 선언 없이 정의한 블록이 본문인 함수와 같다.
4746+
4747+
```kotlin
4748+
fun f() { ... } // 반환 타입을 명시하지 않았다.
4749+
```
4750+
4751+
Unit은 모든 기능을 갖는 일반적인 타입이며, void와 달리 Unit을 타입 인자로 쓸 수 있다.
4752+
Unit 타입에 속한 값은 단 하나뿐이며, 그 이름도 Unit이다.
4753+
Unit 타입의 함수는 Unit 값을 묵시적으로 반환한다.
4754+
4755+
```kotlin
4756+
interface Processor<T> {
4757+
fun process(): T
4758+
}
4759+
4760+
class NoResultProcessor : Processor<Unit> {
4761+
override fun process() { // Unit을 반환하지만 타입을 지정할 필요는 없다.
4762+
// 업무 처리 코드
4763+
} // 여기서 return을 명시할 필요가 없다.
4764+
}
4765+
```
4766+
4767+
NoResultProcessor에서 명시적으로 Unit을 반환할 필요는 없다.
4768+
컴파일러가 묵시적으로 return Unit을 넣어준다.
4769+
4770+
### - Nothing 타입: 이 함수는 결코 정상적으로 끝나지 않는다
4771+
4772+
'반환 값'이라는 개념 자체가 의미 없는 함수가 일부 존재한다.
4773+
예를 들어 무한 루프를 도는 함수도 결코 값을 반환하며, 정상적으로 끝나지 않는다.
4774+
이런 경우를 표현하기 위해 코틀린에는 Nothing이라는 특별한 반환 타입이 있다.
4775+
4776+
```kotlin
4777+
fun fail(mesage: String): Nothing {
4778+
throw IllegalStateException(message)
4779+
}
4780+
4781+
>>> fail("Error occurred")
4782+
java.lang.IllegalStateException: Error occurred
4783+
```
4784+
4785+
Nothing 타입은 아무 값도 포함하지 않는다.
4786+
4787+
Nothing을 반환하는 함수를 엘비스 연산자의 우항에 사용해서 전제 조건을 검사할 수 있다.
4788+
4789+
```kotlin
4790+
val address = company.address ?: fail("No address")
4791+
println(address.city)
4792+
```
4793+
4794+
컴파일러는 Nothing이 반환 타입인 함수가 결코 정상 종료되지 않음을 알고 그 함수를 호출하는 코드를 분석할 때 사용한다.
4795+
위의 예제에서 컴파일러는 company.address가 널인 경우 엘비스 연산자의 우항에서 예외가 발생한다는 사실을 파악하고 address의 값이 널이 아님을 추론할 수 있다.
4796+
4797+
## 컬렉션과 배열
4798+
4799+
### - 널 가능성과 컬렉션
4800+
4801+
```kotlin
4802+
fun readNumbers(reader: BufferedReader): List<Int?> {
4803+
val result = ArrayList<Int?>() // 널이 될 수 있는 Int 값으로 이뤄진 리스트를 만든다.
4804+
for (line in reader.lineSequence()) {
4805+
try {
4806+
val number = line.toInt()
4807+
result.add(number) // 정수(널이 아닌 값)를 리스트에 추가한다.
4808+
}
4809+
catch(e: NumberFormatException) {
4810+
result.add(null) // 현재 줄을 파싱할 수 없으므로 리스트에 널을 추가한다.
4811+
}
4812+
}
4813+
return result
4814+
}
4815+
```
4816+
4817+
List<Int?>는 Int? 타입의 값을 저장할 수 있다.
4818+
코틀린 1.1부터는 파싱에 실패하면 null을 반환하는 String.toIntOrNull을 사용해 이 예제를 더 줄일 수 있다.
4819+
4820+
![](./images/06fig10.jpg)
4821+
4822+
경우에 따라 널이 될 수 있는 값으로 이뤄진 널이 될 수 있는 리스트를 정의해야 한다면 물음표를 2개 사용해 List<Int?>?로 표현한다.
4823+
이런 리스트를 처리할 때는 변수에 대해 널 검사를 수행한 다음에 그 리스트에 속한 모든 원소에 대해 다시 널 검사를 수행해야 한다.
4824+
4825+
```kotlin
4826+
fun addValidNumbers(numbers: List<Int?>) {
4827+
var sumOfValidNumbers = 0
4828+
var invalidNumbers = 0
4829+
for (number in numbers) {
4830+
if (number != null) {
4831+
sumOfValidNumbers += number
4832+
} else {
4833+
invalidNumbers++
4834+
}
4835+
}
4836+
println("Sum of valid numbers: $sumOfValidNumbers")
4837+
println("Invalid numbers: $invalidNumbers")
4838+
}
4839+
4840+
>>> val reader = BufferedReader(StringReader("1\nabc\n42"))
4841+
>>> val numbers = readNumbers(reader)
4842+
>>> addValidNumbers(numbers)
4843+
Sum of valid numbers: 43
4844+
Invalid numbers: 1
4845+
```
4846+
4847+
코틀린 표준 라이브러리 함수 filterNotNull 사용
4848+
4849+
```kotlin
4850+
fun addValidNumbers(numbers: List<Int?>) {
4851+
val validNumbers = numbers.filterNotNull()
4852+
println("Sum of valid numbers: ${validNumbers.sum()}")
4853+
println("Invalid numbers: ${numbers.size - validNumbers.size}")
4854+
}
4855+
```
4856+
4857+
filterNotNull이 컬렉션 안에 널이 들어있지 않음을 보장해주므로 validNumbers는 List<Int> 타입이다.
4858+
4859+
### - 읽기 전용과 변경 가능한 컬렉션
4860+
4861+
코틀린 컬렉션과 자바 컬렉션을 나누는 가장 중요한 특성 하나는 코틀린에서는 컬렉션 안의 데이터에 접근하는 인터페이스와 컬렉션 안의 데이터를 변경하는 인터페이스를 분리했다는 점이다.
4862+
4863+
![](./images/06fig11.jpg)
4864+
4865+
MutableCollection은 Collection을 확장하면서 컬렉션 내용을 변경하는 메소드를 더 제공한다.
4866+
4867+
어떤 컴포넌트의 내부 상태에 컬렉션이 포함된다면 그 컬렉션을 MutableCollection을 인자로 받는 함수에 전달할 때는 어쩌면 원본의 변경을 막기 위해 컬렉션을 복사해야할 수도 있다(이런 패턴을 방어적 복사(defensive copy)라고 부른다).
4868+
4869+
```kotlin
4870+
fun <T> copyElement(source: Collection<T>,
4871+
target: MutableCollection<T>) {
4872+
for (item in source) { // source 컬렉션의 모든 원소에 대해 루프를 돈다.
4873+
target.add(item) // 변경 가능한 target 컬렉션에 원소를 추가한다.
4874+
}
4875+
}
4876+
4877+
>>> val source: Collection<Int> = arrayListOf(3, 5, 7)
4878+
>>> val target: MutableCollection<Int> = arrayListOf(1)
4879+
>>> copyElement(source, target)
4880+
>>> println(target)
4881+
[1, 3, 5, 7]
4882+
```
4883+
4884+
target에 해당하는 인자로 읽기 전용 컬렉션을 넘길 수 없다.
4885+
4886+
```kotlin
4887+
>>> val source: Collection<Int> = arrayListOf(3, 5, 7)
4888+
>>> val target: Collection<Int> = arrayListOf(1)
4889+
>>> copyElements(source, target) // "target" 인자에서 컴파일 오류 발생
4890+
Error: Type mismatch: inferred type is Collection<Int> but MutableCollection<Int> was expected
4891+
```
4892+
4893+
컬렉션 인터페이스를 사용할 때 항상 염두에 둬야 할 핵심은 읽기 전용 컬렉션이라고 해서 꼭 변경 불가능한 컬렉션일 필요는 없다.
4894+
읽기 전용 인터페이스 타입인 변수를 사용할 때 그 인터페이스는 실제로는 어떤 컬렉션 인스턴스를 가리키는 수많은 참조 중 하나일 수 있다.
4895+
4896+
![](./images/06fig12.jpg)
4897+
4898+
따라서 읽기 전용 컬렉션이 항상 스레드 안전(thread safe)하지 않다는 점을 명심해야 한다.
4899+
다중 스레드 환경에서 데이터를 다루는 경우 그 데이터를 적절히 동기화하거나 동시 접근을 허용하는 데이터 구조를 활용해야 한다.
4900+
4901+
### - 코틀린 컬렉션과 자바
4902+
4903+
모든 코틀린 컬렉션은 그에 상응하는 자바 컬렉션 인터페이스의 인스턴스이다.
4904+
4905+
![](./images/06fig13_alt.jpg)
4906+
4907+
변경 가능한 인터페이스는 java.util 패키지에 있는 인터페이스와 직접적으로 연관되지만 읽기 전용 인터페이스에는 컬렉션을 변경할 수 있는 모든 요소가 빠져있다.
4908+

images/06fig10.jpg

47.1 KB
Loading

images/06fig11.jpg

35.7 KB
Loading

images/06fig12.jpg

29.6 KB
Loading

images/06fig13_alt.jpg

37.5 KB
Loading

0 commit comments

Comments
 (0)