기본 지식 - 변수와 자료
기본 연산자
// **1. 산술 연산자 (Arithmetic Operators)**
val sum = 10 + 5 // 덧셈
val diff = 10 - 5 // 뺄셈
val product = 10 * 5 // 곱셈
val quotient = 10 / 5 // 나눗셈
val remainder = 10 % 3 // 나머지
// **2. 복합 대입 연산자 (Assignment Operators)**
var num = 10
num += 5 // num = num + 5 (덧셈 후 대입)
num -= 3 // num = num - 3 (뺄셈 후 대입)
num *= 2 // num = num * 2 (곱셈 후 대입)
num /= 2 // num = num / 2 (나눗셈 후 대입)
num %= 3 // num = num % 3 (나머지 후 대입)
// **3. 증가 및 감소 연산자 (Increment and Decrement Operators)**
var count = 0
count++ // 후위 증가: count 값을 사용한 후 1 증가
++count // 전위 증가: count 값을 증가시킨 후 사용
count-- // 후위 감소: count 값을 사용한 후 1 감소
--count // 전위 감소: count 값을 감소시킨 후 사용
// **4. 비교 연산자 (Comparison Operators)**
val a = 10
val b = 20
val isEqual = (a == b) // 값이 같은지 비교
val isNotEqual = (a != b) // 값이 다른지 비교
val isGreater = (a > b) // a가 b보다 큰지 비교
val isLess = (a < b) // a가 b보다 작은지 비교
val isGreaterOrEqual = (a >= b) // a가 b보다 크거나 같은지 비교
val isLessOrEqual = (a <= b) // a가 b보다 작거나 같은지 비교
// **5. 논리 연산자 (Logical Operators)**
val condition1 = true
val condition2 = false
val andResult = condition1 && condition2 // AND 연산: 둘 다 true일 때만 true
val orResult = condition1 || condition2 // OR 연산: 둘 중 하나라도 true면 true
val notResult = !condition1 // NOT 연산: true → false, false → true
// **6. 비트 연산자 (Bitwise Operators)**
val x = 5 // 0b0101
val y = 3 // 0b0011
val bitAnd = x and y // AND 연산: 0b0001 (비트 단위)
val bitOr = x or y // OR 연산: 0b0111
val bitXor = x xor y // XOR 연산: 0b0110
val bitInv = x.inv() // 비트 반전: -6 (0b...11111010)
val leftShift = x shl 2 // 왼쪽 시프트: 0b10100 (5 * 2^2)
val rightShift = x shr 1 // 오른쪽 시프트: 0b0010 (5 / 2)
// **7. 범위 연산자 (Range Operators)**
val range = 1..5 // 1에서 5까지 포함된 범위
val untilRange = 1 until 5 // 1에서 5 미만 (1, 2, 3, 4)
val downToRange = 5 downTo 1 // 5에서 1까지 감소
val stepRange = 1..10 step 2 // 1, 3, 5, ..., 9 (2씩 증가)
// **8. 인 연산자 (in Operator)**
val isInRange = 3 in range // true (3이 range 안에 포함)
val isNotInRange = 6 !in range // true (6이 range에 포함되지 않음)
// **9. 타입 연산자 (Type Operators)**
val str: Any = "Hello"
val isString = str is String // true (str이 String 타입인지 확인)
val isNotString = str !is String // false (str이 String 타입이 아닌지 확인)
// **10. 엘비스 연산자 (Elvis Operator)**
val nullable: String? = null
val result = nullable ?: "Default" // nullable이 null이면 "Default" 반환
// **11. 안전 호출 연산자 (Safe Call Operator)**
val nullableStr: String? = "Hello"
val length = nullableStr?.length // nullableStr이 null이 아니면 length 반환, null이면 null 반환
// **12. 강제 호출 연산자 (Not-null Assertion Operator)**
val nonNullLength = nullableStr!!.length // nullableStr이 null이 아니라고 확신하는 경우 사용 (null이면 예외 발생)
// **13. 기타 연산자**
val aStr = "Kotlin"
val bStr = "kotlin"
val ignoreCaseEquals = (aStr.equals(bStr, ignoreCase = true)) // 대소문자 무시하고 비교
요약
- Kotlin 연산자는 Java의 연산자와 유사하지만, 더 간결하고 안전성을 높인 연산자를 제공합니다.
- 특히 Elvis 연산자 (?:), 안전 호출 연산자 (?.), 강제 호출 연산자 (!!) 등은 Kotlin의 강력한 특징 중 하나로 Null 안전성을 보장합니다.
- 기본적으로 자바보다도 더 가독성이 좋은 형태로 구성이 되어 있어, 코딩시에 유리합니다.
출력과 입력
📌 흔히 다른 장치로 데이터를 전송하는 행위를 Output(출력), 반대로 데이터를 불러오는 행위를 Input(입력)이라고 합니다.
- 프로그램에서 스피커로 사운드 출력
- 마이크에서 녹음한 목소리를 불러와서 프로그램에서 확인
1. 출력 (Output)
// **기본 출력**
println("Hello, Kotlin!") // 줄 바꿈과 함께 메시지 출력
print("Hello, ") // 줄 바꿈 없이 메시지 출력
print("World!") // 이어서 출력
// **문자열 보간(String Interpolation)**
val name = "Alice"
val age = 25
println("My name is $name, and I am $age years old.") // 변수 값을 문자열에 삽입
println("Next year, I will be ${age + 1} years old.") // 중괄호를 사용해 식도 삽입 가능
// **서식 지정 출력**
val pi = 3.14159
println("The value of pi is %.2f".format(pi)) // 소수점 둘째 자리까지 출력
println("Hello, %s! You are %d years old.".format(name, age)) // %s: 문자열, %d: 정수
2. 입력 (Input)
// **readLine() 사용 (기본 입력)**
print("Enter your name: ")
val userName = readLine() // 사용자 입력을 문자열로 읽음
println("Hello, $userName!")
// **숫자 입력 처리**
print("Enter your age: ")
val userAge = readLine()?.toIntOrNull() // 문자열을 정수로 변환 (null 안전)
if (userAge != null) {
println("You are $userAge years old!")
} else {
println("Invalid input. Please enter a valid number.")
}
// **여러 값 입력**
print("Enter two numbers separated by a space: ")
val (num1, num2) = readLine()!!.split(" ").map { it.toInt() } // 공백으로 구분된 두 숫자를 읽어 정수로 변환
println("Sum of $num1 and $num2 is ${num1 + num2}")
// **예외 처리와 함께 입력 처리**
try {
print("Enter a decimal number: ")
val decimal = readLine()!!.toDouble() // 문자열을 실수(Double)로 변환
println("The value you entered is $decimal")
} catch (e: NumberFormatException) {
println("Invalid input. Please enter a valid decimal number.")
}
3. 입력 및 출력 예제
fun main() {
// 사용자 이름과 나이 입력받기
print("What's your name? ")
val name = readLine() ?: "Unknown" // Null 안전성을 위해 기본값 설정
print("How old are you? ")
val age = readLine()?.toIntOrNull() ?: 0 // 숫자 변환 실패 시 기본값 0
// 입력 결과 출력
println("Nice to meet you, $name!")
println("You are $age years old.")
// 두 숫자를 입력받아 합 계산
print("Enter two numbers (e.g., 5 10): ")
val (a, b) = readLine()!!.split(" ").map { it.toInt() }
println("The sum of $a and $b is ${a + b}")
}
요약
작업 | 방법 | 설명 |
출력 | print, println, format 사용 | 문자열 출력, 서식 지정 출력, 문자열 보간 등 다양한 방식 제공. |
입력 | readLine 사용 | 사용자로부터 문자열 입력받음. 숫자 변환 시 toIntOrNull, toDouble 활용. |
에러 처리 | try-catch 또는 null 체크 | 잘못된 입력에 대한 에러 처리 및 기본값 설정 가능. |
조건식

1. if-else 조건문
// **기본 if-else**
val a = 10
val b = 20
if (a > b) {
println("a is greater than b") // 조건이 참일 때 실행
} else {
println("a is not greater than b") // 조건이 거짓일 때 실행
}
// **if-else if-else (여러 조건 처리)**
val number = 15
if (number > 0) {
println("$number is positive") // 양수일 때 실행
} else if (number < 0) {
println("$number is negative") // 음수일 때 실행
} else {
println("$number is zero") // 0일 때 실행
}
// **if-else 표현식 (Expression)**
val max = if (a > b) a else b // 조건 결과를 변수에 저장
println("The maximum value is $max")
2. when 조건문
// **기본 when 사용**
val x = 2
when (x) {
1 -> println("x is 1") // x가 1일 때
2 -> println("x is 2") // x가 2일 때
else -> println("x is neither 1 nor 2") // 어떤 경우에도 해당되지 않을 때
}
// **범위와 여러 값 검사**
val y = 10
when (y) {
in 1..10 -> println("y is in the range 1 to 10") // 범위 검사
in 11..20 -> println("y is in the range 11 to 20")
else -> println("y is out of range")
}
// **is 연산자 사용**
val obj: Any = "Hello"
when (obj) {
is String -> println("obj is a String") // obj가 String 타입일 때
is Int -> println("obj is an Integer") // obj가 Int 타입일 때
else -> println("obj is of an unknown type") // 다른 타입일 때
}
// **결과를 반환하는 when**
val z = 5
val result = when (z) {
1 -> "One" // z가 1일 때 반환값
2, 3 -> "Two or Three" // z가 2 또는 3일 때 반환값
else -> "Other" // 어떤 경우에도 해당되지 않을 때
}
println("Result: $result")
3. 조건식 간소화
// **삼항 연산자 대체 (Kotlin은 삼항 연산자를 지원하지 않음)**
val isEven = if (x % 2 == 0) "Even" else "Odd" // 삼항 연산자 대신 if-else 표현식 사용
println("x is $isEven")
4. 조건식 활용 예제
fun main() {
// 숫자 크기 비교
val a = 10
val b = 15
println("Larger number: ${if (a > b) a else b}")
// 범위와 타입 검사
val input: Any = 42
when (input) {
in 1..50 -> println("Input is in range 1 to 50")
is String -> println("Input is a string")
else -> println("Input is something else")
}
// 다양한 조건 처리
val score = 85
val grade = when {
score >= 90 -> "A"
score >= 80 -> "B"
score >= 70 -> "C"
else -> "F"
}
println("Your grade is $grade")
}
요약
조건식 | 유형 설명 | 사용 예제 |
if-else | 조건이 참인지 거짓인지 확인 후 실행. | if (a > b) println("a > b") else println("a <= b") |
if-else 표현식 | 조건 결과를 값으로 반환. | val max = if (a > b) a else b |
when | 여러 조건을 확인하거나 값을 반환. | when (x) { 1 -> ...; else -> ... } |
범위/타입 검사 | in으로 범위 확인, is로 타입 확인 가능. | when (x) { in 1..10 -> ...; is String -> ... } |
삼항 연산자 대체 | Kotlin은 삼항 연산자를 지원하지 않아 if-else 표현식을 사용. | val result = if (a > b) a else b |
반복문
1. for 반복문
// **1. 기본 for문**
for (i in 1..5) { // 1부터 5까지 포함된 범위
println("i = $i")
}
// **2. with step (간격 설정)**
for (i in 1..10 step 2) { // 1부터 10까지 2씩 증가
println("i = $i")
}
// **3. downTo (역방향 반복)**
for (i in 5 downTo 1) { // 5부터 1까지 감소
println("i = $i")
}
// **4. until (미포함 범위)**
for (i in 1 until 5) { // 1부터 4까지 (5는 포함하지 않음)
println("i = $i")
}
// **5. 배열 및 리스트 반복**
val fruits = listOf("Apple", "Banana", "Cherry")
for (fruit in fruits) {
println("Fruit: $fruit")
}
// **6. 인덱스와 함께 반복**
for ((index, value) in fruits.withIndex()) {
println("Index $index: $value")
}
2. while 반복문
// **1. 기본 while문**
var count = 5
while (count > 0) {
println("Count: $count")
count--
}
// **2. 조건이 참일 때 실행**
val numbers = listOf(1, 2, 3, 4, 5)
var index = 0
while (index < numbers.size) {
println("Number: ${numbers[index]}")
index++
}
3. do-while 반복문
// **1. 기본 do-while문**
var num = 3
do {
println("Number: $num")
num--
} while (num > 0)
// **2. 무조건 한 번 실행**
do {
println("This will run at least once!")
} while (false)
4. 반복문 제어 키워드
// **1. break (반복문 종료)**
for (i in 1..5) {
if (i == 3) break // i가 3이면 반복문 종료
println("i = $i")
}
// **2. continue (다음 반복으로 넘어감)**
for (i in 1..5) {
if (i == 3) continue // i가 3이면 아래 코드 건너뜀
println("i = $i")
}
// **3. 라벨(label)을 사용한 제어**
outer@ for (i in 1..3) {
for (j in 1..3) {
if (i == 2 && j == 2) break@outer // outer 반복문 종료
println("i = $i, j = $j")
}
}
5. 반복문 활용 예제
fun main() {
// 1부터 10까지의 숫자 중 짝수만 출력
for (i in 1..10) {
if (i % 2 == 0) println("Even number: $i")
}
// 리스트 요소 출력
val items = listOf("Kotlin", "Java", "Python")
for (item in items) {
println("Language: $item")
}
// 2중 반복문으로 구구단 출력
for (i in 2..9) {
for (j in 1..9) {
print("${i * j}\t")
}
println()
}
// while문으로 합 계산
var sum = 0
var n = 1
while (n <= 10) {
sum += n
n++
}
println("Sum of 1 to 10: $sum")
}
요약
반복문 | 설명 | 예제 |
for문 | 특정 범위 또는 컬렉션을 순회하며 반복. | for (i in 1..5) println(i) |
while문 | 조건이 참인 동안 반복 실행. | while (x > 0) { x-- } |
do-while문 | 조건과 관계없이 블록을 최소 한 번 실행한 후 조건을 확인. | do { x-- } while (x > 0) |
break | 반복문을 즉시 종료. | if (x == 3) break |
continue | 현재 반복을 건너뛰고 다음 반복 실행. | if (x == 3) continue |
라벨 사용 | 다중 반복문에서 특정 반복문을 제어. | break@outer |
형변환
📌 어떤 타입의 값을 다른 타입으로 변환하는 것을 말합니다. 흔히 타입 캐스팅이라고도 불립니다.
1. Kotlin에서의 형변환
- Kotlin은 타입 안정성을 중시하여 명시적 형변환을 요구.
- 형변환을 위해 toInt(), toDouble(), toString() 등의 변환 함수를 제공.
- 안전한 형변환과 비안전한 형변환 연산자도 사용 가능.
2. 기본 형변환 (Primitive Type Casting)
// **정수형 변환**
val number = 42
val doubleValue = number.toDouble() // Int → Double
val stringValue = number.toString() // Int → String
println("Double: $doubleValue, String: $stringValue")
// **실수형 변환**
val decimal = 3.14
val intValue = decimal.toInt() // Double → Int (소수점 버림)
val longValue = decimal.toLong() // Double → Long
println("Int: $intValue, Long: $longValue")
// **문자열 변환**
val str = "123"
val parsedInt = str.toInt() // String → Int
val parsedDouble = str.toDouble() // String → Double
println("Parsed Int: $parsedInt, Parsed Double: $parsedDouble")
// **예외 처리**
val invalidStr = "abc"
val safeConversion = invalidStr.toIntOrNull() // 실패 시 null 반환
println("Safe Conversion: $safeConversion")
3. 스마트 캐스팅 (Smart Casting)
// **스마트 캐스팅이란?**
// Kotlin 컴파일러가 타입을 자동으로 추론하여 형변환을 수행.
fun printLength(obj: Any) {
if (obj is String) { // is 연산자로 타입 검사
println("String length: ${obj.length}") // obj는 자동으로 String으로 스마트 캐스팅
} else {
println("Not a String")
}
}
printLength("Hello") // 출력: String length: 5
printLength(123) // 출력: Not a String
4. 안전한 형변환 (Safe Casting)
// **as? 연산자 사용**
val obj: Any = "Kotlin"
val str: String? = obj as? String // 안전한 형변환 (실패 시 null 반환)
println("Safe Cast Result: $str") // 출력: Kotlin
val notString: Any = 123
val failedCast: String? = notString as? String // 실패 시 null 반환
println("Failed Cast Result: $failedCast") // 출력: null
5. 비안전한 형변환 (Unsafe Casting)
// **as 연산자 사용**
val obj: Any = "Kotlin"
val str: String = obj as String // 명시적 형변환
println("Unsafe Cast Result: $str") // 출력: Kotlin
val notString: Any = 123
// val failedCast: String = notString as String // ClassCastException 발생
6. Any, Unit, Nothing 타입
// **Any 타입**
val anyValue: Any = "This can be any type"
println("Any Value: $anyValue")
// **Unit 타입 (void와 유사)**
fun printMessage(): Unit { // 반환값이 없는 함수
println("This function returns Unit")
}
// **Nothing 타입 (절대 반환되지 않음)**
fun throwError(): Nothing {
throw IllegalArgumentException("This function always throws an exception")
}
7. 형변환 활용 예제
fun main() {
// 1. 입력 값을 숫자로 변환 후 계산
print("Enter a number: ")
val input = readLine()?.toIntOrNull() ?: 0 // Null-safe 변환
println("Double of your input: ${input * 2}")
// 2. 다양한 타입을 처리하는 스마트 캐스팅
val values: List<Any> = listOf(42, "Kotlin", 3.14, true)
for (value in values) {
when (value) {
is Int -> println("$value is an Integer")
is String -> println("$value is a String")
is Double -> println("$value is a Double")
else -> println("$value is of an unknown type")
}
}
// 3. 안전한 형변환
val unknown: Any = "Kotlin"
val safeString: String? = unknown as? String
println("Safe Cast: $safeString")
}
요약
형변환 종류 | 설명 | 예제 |
기본 형변환 | toInt(), toDouble(), toString() 등 기본 제공 함수로 타입 변환. | val x = "123".toInt() |
스마트 캐스팅 | is 연산자로 타입 확인 후 자동 형변환. | if (obj is String) obj.length |
안전한 형변환 | as? 연산자로 실패 시 null 반환. | val str = obj as? String |
비안전한 형변환 | as 연산자로 명시적 형변환, 실패 시 예외 발생. | val str = obj as String |
Any, Unit, Nothing | Any: 모든 타입의 최상위 클래스.Unit: 반환값 없는 함수.Nothing: 반환 불가 타입. | val x: Any = "Hello" |
함수 사용
📌 특정 로직을 가지는 소스코드에 별명을 붙인 것으로, 특정 기능을 수행하는 로직을 함수라고 부릅니다.
1. 함수 기본 구조
// **기본 함수**
fun greet() {
println("Hello, Kotlin!") // 출력만 수행
}
greet() // 함수 호출
2. 매개변수와 반환값이 있는 함수
// **매개변수와 반환값이 있는 함수**
fun add(a: Int, b: Int): Int { // 두 개의 정수 매개변수와 반환값(Int)을 선언
return a + b // 두 값을 더해 반환
}
val sum = add(3, 5) // 함수 호출 후 결과를 변수에 저장
println("Sum: $sum") // 출력: Sum: 8
3. 단일 표현식 함수
// **단일 표현식 함수**
fun multiply(a: Int, b: Int): Int = a * b // 단일 식을 사용하는 함수
val product = multiply(4, 5) // 결과: 20
println("Product: $product")
4. 기본값을 가진 매개변수
// **기본값 매개변수**
fun greetUser(name: String = "Guest") {
println("Hello, $name!")
}
greetUser("Alice") // 출력: Hello, Alice!
greetUser() // 출력: Hello, Guest!
5. 가변인자 (Varargs)
// **가변인자**
fun printAll(vararg numbers: Int) {
for (num in numbers) {
println(num)
}
}
printAll(1, 2, 3, 4, 5) // 가변 개수의 매개변수를 전달
6. 함수 호출 시 이름 붙인 매개변수
// **이름 붙인 매개변수**
fun formatMessage(greeting: String, name: String): String {
return "$greeting, $name!"
}
val message = formatMessage(greeting = "Hi", name = "Bob") // 매개변수 이름을 명시적으로 지정
println(message) // 출력: Hi, Bob!
7. 고차 함수 (함수를 매개변수로 받는 함수)
// **고차 함수**
fun calculate(a: Int, b: Int, operation: (Int, Int) -> Int): Int {
return operation(a, b) // 전달받은 함수로 계산 수행
}
val sumResult = calculate(3, 7) { x, y -> x + y } // 람다 표현식으로 함수 전달
println("Sum Result: $sumResult") // 출력: Sum Result: 10
8. 확장 함수
// **확장 함수**
fun String.addExclamation(): String {
return this + "!" // 문자열에 느낌표 추가
}
val excited = "Kotlin".addExclamation() // 기존 String 클래스에 함수 추가
println(excited) // 출력: Kotlin!
9. 인라인 함수
// **인라인 함수**
inline fun executeInline(action: () -> Unit) {
action() // 전달받은 함수 실행
}
executeInline { println("Inline function executed!") } // 출력: Inline function executed!
10. 지역 함수
// **지역 함수**
fun outerFunction(a: Int, b: Int): Int {
fun innerFunction(x: Int): Int { // 외부 함수 내부에서 정의된 함수
return x * x
}
return innerFunction(a) + innerFunction(b)
}
val result = outerFunction(2, 3) // 결과: 13
println("Result: $result")
11. 재귀 함수
// **재귀 함수**
fun factorial(n: Int): Int {
return if (n == 1) 1 else n * factorial(n - 1) // 자기 자신 호출
}
val fact = factorial(5) // 결과: 120
println("Factorial: $fact")
요약
유형 | 설명 | 예제 |
기본 함수 | 입력값 없이 실행되는 함수. | fun greet() { ... } |
매개변수와 반환값 | 매개변수와 반환 타입을 지정. | fun add(a: Int, b: Int): Int |
단일 표현식 함수 | 간단한 식으로 반환값을 처리. | fun multiply(a: Int, b: Int): Int = a * b |
기본값 매개변수 | 매개변수에 기본값을 설정. | fun greet(name: String = "Guest") |
가변인자 | 여러 개의 값을 배열처럼 받을 수 있음. | fun printAll(vararg nums: Int) |
고차 함수 | 함수를 매개변수로 전달하거나 반환. | fun calculate(a: Int, b: Int, op: (Int, Int) -> Int) |
확장 함수 | 기존 클래스에 새로운 함수 추가. | fun String.addExclamation(): String |
인라인 함수 | 컴파일 시 코드가 삽입되어 성능 최적화 가능. | inline fun executeInline(action: () -> Unit) |
지역 함수 | 함수 내에 정의된 함수로 외부 함수 내부에서만 사용 가능. | fun outer() { fun inner() { ... } } |
재귀 함수 | 자기 자신을 호출하여 작업을 반복. | fun factorial(n: Int): Int |
Collection과 반복
📌 Collection은 데이터를 수집하거나 그룹화하는 자료구조입니다. Kotlin에서는 주로 List와 Map을 사용합니다.
- List: 순서가 있는 데이터의 목록으로, 중복된 값이 포함될 수 있습니다.
- Map: 키와 값의 쌍으로 이루어진 데이터 구조로, 각 키는 유일해야 합니다.
- Set: 중복되지 않는 요소의 집합
1. List
// **불변 리스트**
val immutableList = listOf("Apple", "Banana", "Cherry") // 불변 리스트 (수정 불가)
println(immutableList[1]) // 인덱스로 접근 (출력: Banana)
// **가변 리스트**
val mutableList = mutableListOf("Apple", "Banana")
mutableList.add("Cherry") // 요소 추가
mutableList[0] = "Orange" // 요소 수정
println(mutableList) // 출력: [Orange, Banana, Cherry]
// **리스트 반복**
for (item in immutableList) { // for-each 반복
println(item)
}
// **인덱스와 함께 반복**
for ((index, value) in immutableList.withIndex()) {
println("Index $index: $value") // 출력: Index 0: Apple, Index 1: Banana, ...
}
2. Set
// **불변 집합**
val immutableSet = setOf("Apple", "Banana", "Cherry", "Apple") // 중복 허용하지 않음
println(immutableSet) // 출력: [Apple, Banana, Cherry]
// **가변 집합**
val mutableSet = mutableSetOf("Apple", "Banana")
mutableSet.add("Cherry") // 요소 추가
mutableSet.remove("Banana") // 요소 제거
println(mutableSet) // 출력: [Apple, Cherry]
// **Set 반복**
for (item in mutableSet) {
println(item)
}
3. Map
// **불변 맵**
val immutableMap = mapOf("A" to 1, "B" to 2, "C" to 3) // 키-값 쌍 생성
println(immutableMap["B"]) // 키로 값 접근 (출력: 2)
// **가변 맵**
val mutableMap = mutableMapOf("A" to 1, "B" to 2)
mutableMap["C"] = 3 // 키-값 추가
mutableMap["A"] = 10 // 기존 키의 값 수정
mutableMap.remove("B") // 키-값 제거
println(mutableMap) // 출력: {A=10, C=3}
// **Map 반복**
for ((key, value) in immutableMap) {
println("$key -> $value") // 출력: A -> 1, B -> 2, ...
}
4. 반복문을 사용한 컬렉션 처리
// **forEach 사용**
val fruits = listOf("Apple", "Banana", "Cherry")
fruits.forEach { fruit -> println(fruit) } // 각 요소에 대해 작업 수행
// **map을 사용한 변환**
val lengths = fruits.map { it.length } // 각 문자열의 길이를 계산
println(lengths) // 출력: [5, 6, 6]
// **filter를 사용한 조건 필터링**
val filtered = fruits.filter { it.startsWith("A") } // "A"로 시작하는 요소만 필터링
println(filtered) // 출력: [Apple]
// **flatMap으로 중첩된 리스트 처리**
val nestedList = listOf(listOf(1, 2, 3), listOf(4, 5))
val flatList = nestedList.flatMap { it } // 중첩 리스트를 단일 리스트로 변환
println(flatList) // 출력: [1, 2, 3, 4, 5]
5. 기타 컬렉션 함수
// **find: 조건에 맞는 첫 번째 요소 반환**
val firstMatch = fruits.find { it.contains("e") } // "e"를 포함하는 첫 번째 요소
println(firstMatch) // 출력: Apple
// **any: 조건에 맞는 요소가 하나라도 있으면 true**
val hasLongName = fruits.any { it.length > 5 }
println(hasLongName) // 출력: true
// **all: 모든 요소가 조건을 만족하면 true**
val allShortNames = fruits.all { it.length < 7 }
println(allShortNames) // 출력: true
// **count: 조건에 맞는 요소의 개수**
val countStartsWithB = fruits.count { it.startsWith("B") }
println(countStartsWithB) // 출력: 1
// **sort: 정렬**
val sortedFruits = fruits.sorted() // 오름차순 정렬
println(sortedFruits) // 출력: [Apple, Banana, Cherry]
// **groupBy: 조건에 따라 그룹화**
val grouped = fruits.groupBy { it.first() } // 첫 글자로 그룹화
println(grouped) // 출력: {A=[Apple], B=[Banana], C=[Cherry]}
요약
컬렉션 타입 | 불변 | 가변 |
List | listOf("A", "B") | mutableListOf("A", "B") |
Set | setOf("A", "B") | mutableSetOf("A", "B") |
Map | mapOf("A" to 1, "B" to 2) | mutableMapOf("A" to 1, "B" to 2) |
반복문 사용 | 설명 | 예제 |
for | 각 요소에 대해 순회. | for (item in list) { ... } |
forEach | 고차 함수를 사용해 각 요소에 대해 작업 수행. | list.forEach { println(it) } |
map | 각 요소를 변환하여 새로운 컬렉션 생성. | list.map { it.length } |
filter | 조건에 맞는 요소만 포함한 새로운 컬렉션 생성. | list.filter { it.startsWith("A") } |
flatMap | 중첩된 리스트를 단일 리스트로 변환. | nestedList.flatMap { it } |
groupBy | 조건에 따라 요소를 그룹화. | list.groupBy { it.first() } |
고차함수
📌 고차 함수는 함수를 매개변수로 받거나, 함수를 반환하는 함수입니다.
- Kotlin은 함수형 프로그래밍을 지원하며, 고차 함수는 코드의 간결성과 재사용성을 높입니다.
1. 고차 함수의 정의
// **함수를 매개변수로 받는 고차 함수**
fun calculate(a: Int, b: Int, operation: (Int, Int) -> Int): Int {
return operation(a, b) // 전달받은 함수를 호출
}
// 고차 함수 호출
val sum = calculate(5, 10) { x, y -> x + y } // 람다식으로 함수 전달
println("Sum: $sum") // 출력: Sum: 15
// **함수를 반환하는 고차 함수**
fun getOperation(type: String): (Int, Int) -> Int {
return when (type) {
"add" -> { x, y -> x + y } // 덧셈 함수 반환
"multiply" -> { x, y -> x * y } // 곱셈 함수 반환
else -> { _, _ -> 0 } // 기본값 반환
}
}
// 반환된 함수 호출
val operation = getOperation("add")
println("Result: ${operation(3, 7)}") // 출력: Result: 10
2. 람다 표현식 (Lambda Expressions)
// **람다 기본 문법**
val add: (Int, Int) -> Int = { x, y -> x + y }
println("Add: ${add(3, 4)}") // 출력: Add: 7
// **단일 매개변수 사용**
val square: (Int) -> Int = { it * it } // `it` 키워드로 단일 매개변수 사용
println("Square: ${square(5)}") // 출력: Square: 25
// **다양한 람다 사용법**
val printMessage: (String) -> Unit = { message -> println("Message: $message") }
printMessage("Hello, Kotlin!") // 출력: Message: Hello, Kotlin!
3. 익명 함수 (Anonymous Functions)
// **익명 함수**
val subtract = fun(a: Int, b: Int): Int {
return a - b
}
println("Subtract: ${subtract(10, 4)}") // 출력: Subtract: 6
4. 주요 고차 함수 예제
1. forEach
- 컬렉션의 각 요소에 대해 작업 수행.
val numbers = listOf(1, 2, 3, 4, 5)
numbers.forEach { println("Number: $it") } // 각 요소 출력
2. map
- 컬렉션의 각 요소를 변환하여 새 컬렉션 생성.
val squaredNumbers = numbers.map { it * it } // 각 요소를 제곱
println("Squared: $squaredNumbers") // 출력: Squared: [1, 4, 9, 16, 25]
3. filter
- 조건에 맞는 요소만 포함한 새 컬렉션 생성.
val evenNumbers = numbers.filter { it % 2 == 0 } // 짝수만 필터링
println("Even Numbers: $evenNumbers") // 출력: Even Numbers: [2, 4]
4. reduce
- 컬렉션의 모든 요소를 하나의 값으로 누적.
val sum = numbers.reduce { acc, num -> acc + num } // 요소들의 합 계산
println("Sum: $sum") // 출력: Sum: 15
5. fold
- 초기값과 함께 컬렉션을 누적.
val sumWithInitial = numbers.fold(10) { acc, num -> acc + num } // 초기값 10 포함
println("Sum with Initial: $sumWithInitial") // 출력: Sum with Initial: 25
6. flatMap
- 중첩된 리스트를 단일 리스트로 변환.
val nestedList = listOf(listOf(1, 2), listOf(3, 4, 5))
val flatList = nestedList.flatMap { it }
println("Flat List: $flatList") // 출력: Flat List: [1, 2, 3, 4, 5]
7. take / drop
- 일부 요소를 선택하거나 제외.
val firstTwo = numbers.take(2) // 처음 두 요소 선택
println("First Two: $firstTwo") // 출력: First Two: [1, 2]
val dropTwo = numbers.drop(2) // 처음 두 요소 제외
println("After Drop: $dropTwo") // 출력: After Drop: [3, 4, 5]
5. 고차 함수의 장점
장점 | 설명 |
코드 간결화 | 반복적이고 장황한 코드 대신 고차 함수를 사용해 간결하게 작성 가능. |
재사용성 | 고차 함수는 추상화 수준을 높여 다양한 상황에서 재사용 가능. |
유연성 | 매개변수로 함수를 전달하여 동작을 동적으로 변경 가능. |
함수형 프로그래밍 지원 | Kotlin의 함수형 프로그래밍 특성을 활용하여 더 높은 수준의 코드를 작성 가능. |
6. 예제: 고차 함수 활용
fun performOperation(numbers: List<Int>, operation: (Int) -> Int): List<Int> {
return numbers.map { operation(it) }
}
// 고차 함수 사용
val doubledNumbers = performOperation(listOf(1, 2, 3, 4)) { it * 2 } // 각 숫자를 2배로
println("Doubled: $doubledNumbers") // 출력: Doubled: [2, 4, 6, 8]
val squaredNumbers = performOperation(listOf(1, 2, 3, 4)) { it * it } // 각 숫자를 제곱
println("Squared: $squaredNumbers") // 출력: Squared: [1, 4, 9, 16]
요약
항목 | 설명 | 예제 |
고차 함수 | 함수를 매개변수로 받거나 반환하는 함수. | fun calculate(a: Int, b: Int, op: (Int, Int) -> Int) |
람다 표현식 | 익명 함수의 간결한 표현으로 함수형 프로그래밍 지원. | { x, y -> x + y } |
주요 고차 함수 | map, filter, reduce, fold, flatMap, forEach. | list.map { it * 2 } |
장점 | 코드 간결화, 유연성, 재사용성 증가. |
Nullable type
📌 null이 될 수 있는 변수를 의미한다.
- null은 값이 없음, 값이 존재하지 않음을 의미한다. 변수를 만들었지만 그 안에 값을 넣지 않은 경우를 흔히 말한다.
- Kotlin에서 모든 타입은 기본적으로 NULL을 허용하지 않는다. 다만 타입에 ? 을 명시하면 사용이 가능하다.
1. Nullable 타입 선언
// **Nullable 타입 선언**
var name: String? = null // `?`를 사용해 Null을 허용
name = "Kotlin" // Null이 아닌 값 할당 가능
println(name) // 출력: Kotlin
// **Non-null 타입**
var nonNullableName: String = "Hello"
// nonNullableName = null // 컴파일 에러: Null 불허
2. Null 안전성 연산자
1) Safe Call Operator (?.)
- Null 값을 허용하는 변수에서 안전하게 프로퍼티나 메서드를 호출.
val nullableName: String? = null
println(nullableName?.length) // 출력: null (예외 발생 없이 안전)
val nonNullName: String? = "Kotlin"
println(nonNullName?.length) // 출력: 6
2) Elvis Operator (?:)
- Null 값일 경우 기본값을 반환.
val nullableText: String? = null
val result = nullableText ?: "Default Value" // Null이면 "Default Value" 반환
println(result) // 출력: Default Value
3) Non-null Assertion Operator (!!)
- 변수 값이 Null이 아니라고 확신할 때 사용.
- Null일 경우 NullPointerException 발생.
val nullableString: String? = "Kotlin"
println(nullableString!!.length) // 출력: 6
val nullValue: String? = null
// println(nullValue!!.length) // NullPointerException 발생
3. Nullable 타입 활용
1) if 문을 사용한 Null 체크
- Null 여부를 확인하여 처리.
val str: String? = null
if (str != null) {
println("Length: ${str.length}")
} else {
println("String is null")
}
2) Smart Casting (is)
- Kotlin은 Null 체크 후 해당 변수의 타입을 자동으로 변환(스마트 캐스팅).
val nullable: String? = "Kotlin"
if (nullable is String) {
println("Length: ${nullable.length}") // 스마트 캐스팅으로 안전하게 호출
}
4. Safe Call with Let
- **let**은 Null 값을 제외하고 실행할 코드를 작성 가능.
val nullableValue: String? = "Hello"
nullableValue?.let { println("Length: ${it.length}") } // 출력: Length: 5
val nullValue: String? = null
nullValue?.let { println(it) } // 아무 작업도 수행하지 않음
5. Nullable 타입과 컬렉션
1) 컬렉션에 Nullable 타입 포함
- 컬렉션에 Null 값을 허용.
val list: List<String?> = listOf("A", null, "C")
println(list) // 출력: [A, null, C]
2) Null 값 필터링
- 컬렉션에서 Null 값을 제거.
val nullableList: List<String?> = listOf("A", null, "C")
val nonNullList = nullableList.filterNotNull() // Null 제거
println(nonNullList) // 출력: [A, C]
6. Nullable 타입 예제
fun printLength(text: String?) {
val length = text?.length ?: 0 // Null이면 0 반환
println("Length: $length")
}
fun main() {
printLength("Kotlin") // 출력: Length: 6
printLength(null) // 출력: Length: 0
// Safe call과 let 활용
val message: String? = "Hello Kotlin"
message?.let { println("Message length: ${it.length}") } // 출력: Message length: 12
}
요약
연산자/기법 | 설명 | 예제 |
Safe Call (?.) | Null일 경우 호출하지 않고 Null 반환. | nullable?.length |
Elvis Operator (?:) | Null일 경우 기본값 반환. | val result = nullable ?: "Default" |
Non-null Assertion (!!) | Null이 아니라고 확신할 때 사용, Null이면 예외 발생. | nullable!!.length |
if 문을 사용한 체크 | Null 여부를 확인하여 안전하게 처리. | if (str != null) { ... } |
Smart Casting (is) | Null 체크 후 자동으로 Non-null 타입으로 변환. | if (nullable is String) { ... } |
let 블록 사용 | Null이 아닌 경우에만 코드 블록 실행. | nullable?.let { println(it.length) } |
Null 필터링 | 컬렉션에서 Null 값을 제거. | list.filterNotNull() |
예외 처리
📌 예외(Exception)는 프로그램 실행 중 발생하는 오류 상황을 말하며 이런 상황에 대한 대응을 예외 처리라 한다.
- Kotlin은 try-catch-finally 블록을 사용하여 예외를 처리.
- 모든 예외는 Throwable 클래스를 상속받음.
1. try-catch 블록
// **기본 구조**
try {
// 예외 발생 가능성이 있는 코드
val number = "abc".toInt() // 문자열을 정수로 변환하려다 예외 발생
println("Number: $number")
} catch (e: NumberFormatException) {
// 예외 처리
println("Exception occurred: ${e.message}")
}
// **결과**
// 출력: Exception occurred: For input string: "abc"
2. 여러 catch 블록
try {
val result = 10 / 0 // ArithmeticException 발생
println("Result: $result")
} catch (e: ArithmeticException) {
println("ArithmeticException: Division by zero is not allowed.")
} catch (e: Exception) {
println("Generic Exception: ${e.message}")
}
// **결과**
// 출력: ArithmeticException: Division by zero is not allowed.
3. finally 블록
// **try-catch-finally 구조**
try {
val value = "123".toInt() // 정상적으로 실행
println("Value: $value")
} catch (e: Exception) {
println("Exception: ${e.message}")
} finally {
// 예외 발생 여부와 관계없이 항상 실행
println("Finally block executed.")
}
// **결과**
// 출력:
// Value: 123
// Finally block executed.
4. 예외 발생시키기 (throw)
// **throw 키워드로 예외 발생**
fun divide(a: Int, b: Int): Int {
if (b == 0) throw IllegalArgumentException("Division by zero is not allowed.") // 예외 발생
return a / b
}
try {
println(divide(10, 0)) // 예외 발생
} catch (e: IllegalArgumentException) {
println("Caught Exception: ${e.message}")
}
// **결과**
// 출력: Caught Exception: Division by zero is not allowed.
5. 사용자 정의 예외
// **사용자 정의 예외 클래스**
class CustomException(message: String) : Exception(message)
fun checkValue(value: Int) {
if (value < 0) throw CustomException("Negative values are not allowed.") // 사용자 정의 예외 발생
}
try {
checkValue(-10) // 예외 발생
} catch (e: CustomException) {
println("Custom Exception: ${e.message}")
}
// **결과**
// 출력: Custom Exception: Negative values are not allowed.
6. 예외 처리와 Null 안전성
// **toIntOrNull 사용으로 Null 안전 처리**
val number = "abc".toIntOrNull() ?: 0 // 변환 실패 시 Null 반환
println("Number: $number") // 출력: Number: 0
7. 예외 처리 활용 예제
fun readNumber(): Int {
print("Enter a number: ")
val input = readLine()
return try {
input?.toInt() ?: 0 // 입력값을 정수로 변환, 실패 시 0 반환
} catch (e: NumberFormatException) {
println("Invalid number format: ${e.message}")
-1 // 예외 발생 시 -1 반환
}
}
val result = readNumber()
println("Result: $result")
8. 예외 처리의 주요 키워드
키워드 | 설명 | 예제 |
try | 예외 발생 가능성이 있는 코드를 실행. | try { ... } |
catch | 발생한 예외를 처리. | catch (e: Exception) { ... } |
finally | 예외 발생 여부와 관계없이 항상 실행되는 코드. | finally { ... } |
throw | 예외를 명시적으로 발생. | throw IllegalArgumentException("Error") |
toIntOrNull | 예외 없이 문자열을 안전하게 정수로 변환, 실패 시 Null 반환. | "123".toIntOrNull() |
9. 예외 처리의 장점
장점 | 설명 |
프로그램 안정성 유지 | 예외를 처리하여 프로그램이 중단되지 않고 계속 실행 가능. |
문제 디버깅 용이 | 예외 메시지를 통해 문제를 정확히 파악 가능. |
NullPointerException 방지 | Kotlin의 toIntOrNull 등 Null 안전 기능과 결합하여 예외를 줄임. |
'Back-End (Web) > Kotlin' 카테고리의 다른 글
[Kotlin] Kotlin의 테스트코드 (0) | 2025.02.16 |
---|---|
[Kotlin] Kotlin의 객체지향 (0) | 2025.02.12 |
[Kotlin] Kotlin이란? (0) | 2025.02.10 |