Kotlin에서는 자바와 동일한 List
, Set
, Map
컬렉션 클래스를 활용합니다. 자바 컬렉션을 그대로 활용하기 때문에 변환 없이 상호작용이 가능하며, 컬렉션 생성을 위한 다양한 함수들을 제공하여 코드를 간결하게 작성할 수 있습니다.
val set = hashSetOf(1, 7, 53)
val list = arrayListOf(1, 7, 53)
val map = hashMapOf(1 to "one", 7 to "seven", 53 to "fifty-three")
Kotlin은 자바 컬렉션과 동일한 클래스 타입을 사용하지만, last()
나 max()
등의 편리한 확장 함수도 제공합니다.
Kotlin은 함수 호출 시 파라미터 이름을 명시하여 가독성을 높이고 혼동을 줄일 수 있습니다. 각 인자의 이름을 붙여서 호출할 수 있으며, 이는 불리언 플래그나 기본값이 있는 파라미터에 특히 유용합니다.
joinToString(collection, separator = ", ", prefix = "(", postfix = ")")
Kotlin에서는 파라미터의 기본값을 지정할 수 있어 자바에서 흔한 오버로딩 문제를 줄일 수 있습니다. 이를 통해 호출 시 특정 인자를 생략할 수 있습니다.
fun <T> joinToString(
collection: Collection<T>,
separator: String = ", ",
prefix: String = "",
postfix: String = ""
): String
자바에서는 정적 유틸리티 클래스에 많은 메서드를 모아놓지만, Kotlin에서는 최상위 함수로 직접 정의하여 클래스를 생략하고 패키지 수준에서 접근할 수 있습니다.
package strings
fun joinToString(...) { ... }
@JvmName: 최상위 함수가 포함될 클래스의 이름을 바꾸고 싶다면 @JvmName 애노테이션을 추가할 수 있다.
Kotlin은 기존 클래스에 메소드를 추가하는 확장 함수를 지원합니다. 이를 통해 자바의 기존 클래스에도 Kotlin 스타일의 함수를 적용할 수 있습니다.
fun String.lastChar(): Char = this.get(this.length - 1)
println("Kotlin".lastChar()) // 'n'
확장 함수는 실제로 수신 객체를 첫 번째 인자로 받는 정적 메소드입니다. 또한 수신 객체의 메소드 및 프로퍼티를 사용할 수 있지만 클래스의 비공개 멤버에는 접근할 수 없습니다.
확장 함수와 마찬가지로 기존 클래스에 프로퍼티 형태의 기능을 추가할 수 있습니다. 확장 프로퍼티는 상태를 가질 수 없지만 기존 클래스에 읽기/쓰기 용도로 사용 가능합니다.
val String.lastChar: Char
get() = get(length - 1)
var StringBuilder.lastChar: Char
get() = get(length - 1)
set(value: Char) {
this.setCharAt(length - 1, value)
}
Kotlin의 vararg
키워드를 사용하면 가변 길이 인자를 받는 함수를 정의할 수 있습니다. 여러 개의 값을 가변 인자로 받아 배열로 변환할 수 있습니다.
fun listOf<T>(vararg values: T): List<T> { ... }
val list = listOf(2, 3, 5, 7, 11)
스프레드 연산자를 사용하여 배열을 가변 인자로 전달할 수 있습니다.
val args = arrayOf("args")
val list = listOf("first", *args)
중위 호출은 단일 인자를 가진 메서드 호출을 보다 읽기 쉽게 해줍니다. 구조 분해 선언은 복합적인 값을 분해하여 여러 변수에 나눠 담을 수 있습니다.
val map = mapOf(1 to "one", 7 to "seven", 53 to "fifty-three")
val (number, name) = 1 to "one"
to
함수는 실제로 Pair
의 인스턴스를 반환하며, 중위 호출을 사용하여 Pair
객체를 생성할 수 있습니다.
Kotlin에서는 split
확장 함수로 문자열을 쉽게 분할할 수 있으며, 정규식을 파라미터로 사용하면 더욱 유연하게 문자열을 다룰 수 있습니다.
println("12.345-6.A".split("\\\\.|-".toRegex())) // [12, 345, 6, A]
println("12.345-6.A".split(".", "-")) // [12, 345, 6, A]
3중 따옴표 문자열("""
)을 사용하면 문자열을 여러 줄로 정의할 수 있습니다. 이를 통해 줄 바꿈과 들여쓰기를 자유롭게 조정할 수 있습니다.
val kotlinLogo = """
.| //
.|//
.|/ \\
""".trimMargin(".")
3중 따옴표 문자열은 텍스트에서 줄 바꿈을 포함할 때 유용하며, 테스트에서 여러 줄 출력 예상 값을 사용할 때도 편리합니다.
Kotlin에서는 함수 내부에 로컬 함수를 선언하여 중복을 제거하고 코드의 가독성을 높일 수 있습니다. 로컬 함수는 바깥 함수의 변수에 접근할 수 있습니다.
class User(val id: Int, val name: String, val address: String)
fun saveUser(user: User) {
fun validate(value: String, fieldName: String) {
if (value.isEmpty()) {
throw IllegalArgumentException("Can't save user ${user.id}: empty $fieldName")
}
}
validate(user.name, "Name")
validate(user.address, "Address")
}
로컬 함수를 활용하면 검증 로직과 같은 반복 코드를 줄이고 코드의 가독성을 개선할 수 있습니다.
이 뒤에는 제가 스터디에서 발표로 준비했던 자료인데 추가적으로 알아두면 좋을 것 들을 준비했습니다!
last()
, filter { }
와 같은 편리한 확장 함수를 제공하여 코드를 간결하게 작성할 수 있습니다.List
)와 가변 리스트 (MutableList
)로 구분됩니다.val immutableList = listOf(1, 2, 3) // 불변 리스트
val mutableList = mutableListOf(1, 2, 3) // 가변 리스트
mutableList.add(4) // 요소 추가 가능
Set
)과 가변 집합 (MutableSet
)으로 구분됩니다.var immutableSet = setOf(1, 2, 3) // 불변 집합
val mutableSet = mutableSetOf(1, 2, 3) // 가변 집합
mutableSet.add(4) // 요소 추가 가능
Map
)과 가변 맵 (MutableMap
)으로 구분됩니다.val immutableMap = mapOf(1 to "one", 2 to "two") // 불변 맵
val mutableMap = mutableMapOf(1 to "one", 2 to "two")
mutableMap[3] = "three" // 요소 추가 가능
listOf
, setOf
, mapOf
로 생성하며 수정할 수 없는 컬렉션입니다. 안전성과 예측 가능성을 높이기 위해 기본 컬렉션으로 사용됩니다.mutableListOf
, mutableSetOf
, mutableMapOf
로 생성하며, 데이터를 추가, 삭제, 변경할 수 있습니다.last()
: 리스트의 마지막 요소를 반환max()
:filter { }
: 조건에 맞는 요소만 추출map { }
: 모든 요소에 함수를 적용해 변환val list = listOf(1, 2, 3, 4, 5)
val evenNumbers = list.filter { it % 2 == 0 } // 짝수만 추출
val doubled = list.map { it * 2 } // 모든 요소를 2배로 변환
println(evenNumbers) // [2, 4]
println(doubled) // [2, 4, 6, 8, 10]
toList()
, toSet()
, toMap()
등으로 타입 변환이 용이합니다.val list = setOf(1, 2, 3).toList()
val set = listOf(1, 2, 2, 3).toSet() // 중복 제거
// groupBy: 요소들을 그룹화
val grouped = listOf(1, 2, 3, 4, 5).groupBy { it % 2 == 0 }
// flatten: 중첩된 컬렉션을 단일 수준으로 평탄화
val nested = listOf(listOf(1, 2), listOf(3, 4))
val flat = nested.flatten() // [1, 2, 3, 4]
val numbers = listOf(1, 2, 3) //불변 리스트
numbers.add(4) // 컴파일 에러 발생
// 참조 교체는 가능
var numbers2 = listOf(1, 2, 3)
numbers2 = listOf(1,2,3,4) // 새로운 리스트로 교체 가능
val arrayList = ArrayList<Int>() // 랜덤 접근에 유리
val linkedList = LinkedList<Int>() // 삽입/삭제에 유리
// zip: 두 컬렉션 결합
val numbers = listOf(1, 2, 3)
val letters = listOf("A", "B", "C")
val zipped = numbers.zip(letters) // [(1, "A"), (2, "B"), (3, "C")]
// associate: 키-값 쌍으로 맵 생성
val mapped = listOf("one", "two").associate { it to it.length } // {"one": 3, "two": 3}
// windowed: 윈도우 크기로 분할
val nums = listOf(1, 2, 3, 4)
val windows = nums.windowed(size = 2) // [[1, 2], [2, 3], [3, 4]]
data class User(val name: String, val age: Int)
val users = listOf(
User("Alice", 25),
User("Bob", 30),
User("Charlie", 35)
)
val result = users
.filter { it.age > 25 }
.map { it.name }
.sorted()
.joinToString(", ")
println(result) // "Bob, Charlie"