Collection
1. 컬렉션이란?
컬렉션은 자바에서 제공하는 자료구조를 코틀린에서도 편리하게 사용하기 위해 제공되는 라이브러리를 의미한다. 크게 List, Map, Set이 Collection이며 2가지 타입을 제공한다.
- 가변으로 읽기, 쓰기가 모두 가능한 Mutable 타입
- 불변으로 읽기만 가능한 Immutable 타입
컬렉션도 타입을 추론해주기 때문에 빈 컬렉션을 생성하는 경우가 아닌경우 타입을 명시적으로 알려주지 않아도 괜찮다.
2. List
- 데이터를 저장하거나 삭제할 때 순서를 지키는 컬렉션이며 인덱스를 이용해 직접 접근이 가능하다.
- 중복된 값을 가질 수 있고 추가, 삭제, 교체가 쉽다.
// 읽기만 가능하다.
val list = listOf(1,2,3)
// 읽고 쓰기가 가능하다.
val mutableList = mutableListOf(1,2,3)
list[0] = 100 // 쓰기가 불가능해 오류 발생
mutableList[0] = 100 // 쓰기가 가능하다
listOf()란?
public fun <T> listOf(vararg elements: T): List<T> =
if (elements.size > 0) elements.asList() else emptyList()
listOf()는 가변인자 파라미터를 받기 때문에 인자의 개수를 원하는 만큼 넘겨줄 수가 있다. 또, emptyList()를 반환하기 때문에 listOf<T>()로 빈 읽기 전용 리스트 생성이 가능하다. 내부적으로 add나 set을 가지고 있지 않기 때문에 초기화할때를 제외하고는 값을 추가, 삭제, 수정이 불가능하다.
가변인자(varargs)란 ?
- 개수가 별할 수 있는 인자로 인자의 개수에 따라 코드 수정이 불필요하다.
- varargs로 선언된 인자는 가변 인자로 취급되며 배열처럼 인식되기 때문에 forEach 구문으로 순차 탐색이 가능하다.
mutableListOf()란?
@SinceKotlin("1.1")
@kotlin.internal.InlineOnly
public inline fun <T> mutableListOf(): MutableList<T> = ArrayList()
@SinceKotlin("1.1")
@kotlin.internal.InlineOnly
public fun <T> mutableListOf(vararg elements: T): MutableList<T> =
if (elements.size == 0) ArrayList() else ArrayList(ArrayAsCollection(elements, isVarargs = true))
mutableList는 내부적으로 ArrayList를 반환한다. ArrayList로 만들어 지기 때문에 추가, 삭제, 수정이 가능하다.
3. Set
- 중복되지 않은 요소들로 구성된 컬렉션이다. null 객체를 가질 수 있지만 중복된 객체는 추가 될 수 없다.
- forEach로 모든 객체 탐색이 가능하다.
- 인덱스를 가지고 있지 않다.
- 인덱스가 없지만 특정 값만 꺼내고 싶은 경우 elementAt() 함수를 사용해 저장된 순서로 꺼내오는 것이 가능하다.
setOf()란?
@kotlin.internal.InlineOnly
public inline fun <T> setOf(): Set<T> = emptySet()
중복된 요소를 허용하지 않는다. 초기화 된 이후엔 읽기만 가능하며, 추가, 삭제, 수정이 불가능하다.
fun main(){
val setList = setOf(1, 3, 3, 5)
println(setList : $setList) // setList : [1, 3, 5]
}
set은 중복을 허용하지 않기 때문에 중복된 값을 넣어도 하나만 저장한다.
mutableSetOf()란?
public fun <T> mutableSetOf(vararg elements: T): MutableSet<T> =
elements.toCollection(LinkedHashSet(mapCapacity(elements.size)))
추가, 삭제, 수정가 모두 가능하다. 인덱스가 없기 때문에 removeAt()을 지원하지 않는다. remove()를 이용해 원하는 값을 명시해 지워야한다.
val list = mutablueSetOf(1, 3, 5, 7)
list.removeAt(0) // 오류 발생 !! 인덱스가 없기 때문에 특정 인덱스로 요소를 지우는건 불가능헤 제공 안해줌 !!
list.remove(1) // 정상 동작 !! 특정 값을 찾아 지우기는건 가능 !!
4. Map
- key, value가 하나의 쌍으로 이루어져있다.
- 중복된 value를 가질 수 있지만 key는 고유한 값이다.
- Pair를 요소로 받는다.
mapOf()란?
public actual fun <K, V> mapOf(pair: Pair<K, V>): Map<K, V> = java.util.Collections.singletonMap(pair.first, pair.second)
읽기만 가능하다. key는 중복을 허용하지 않지만 value는 중복을 허용한다. Pair를 인자로 받는다.
val mapList1 = mapOf(Pair("첫번째", 1), Pair("두번째", 2))
val mapList2 = mapOf("첫번째" to 1, "두번째" to 1)
// to는 확장함수이다.
public infix fun <A, B> A.to(that: B): Pair<A, B> = Pair(this, that)
mutableMapOf()
public fun <T> mutableListOf(vararg elements: T): MutableList<T> =
if (elements.size == 0) ArrayList() else ArrayList(ArrayAsCollection(elements, isVarargs = true))
읽기, 추가, 삭제, 수정 모두 가능하다.
5. List, Set, Map 타입
컬렉션 | 불변 | 가변 |
List | listOf() | mutableListOf(), arrayList() |
Set | setOf() | mutableSetOf(), hashSetOf(), linkedSetOf(), sortedSetOf() |
Map | mapOf() | mutableMapOf(), hashMapOf(), linkedMapOf(), sortedMapOf() |
함수
1. 이름 붙인 인자
함수를 호출할때 n개의 매개변수를 필요로 하는 경우 그 수가 많아지면 코드의 가독성이 떨어지고 혼돈이 생길 수가 있다.
fun mian(){
test(1, 2, "3", 4)
}
fun test(a: Int, b: Int, strC: String, d: Int){
/**
**
**/
}
이런 문제를 해결하기 위해 이름을 붙인 인자로 함수 호출이 가능하다. 단, 인자에 이름을 명시하고 난 뒤엔 뒤에 오는 모든 인자에 이름을 명시해 줘야한다.
fun mian(){
test(1, 2, strC = "3", d = 4)
test(d = 4, a = 1, b = 2, strC = "3") // 이름을 명시할 경우 순서를 변경해도 된다.
}
fun test(a: Int, b: Int, strC: String, d: Int){
/**
**
**/
}
2. 디폴트 파라미터 값
생성자를 여러곳에서 오버로딩하는 경우 필요로하는 상황에 따라 불필요한 파라미터가 생길 수도, 인자가 생략된 오버로드 함수를 호출할 때 어떤 함수를 호출할지 모호한 경우가 생긴다. 이런 상황은 파라미터의 디폴트 값을 지정해 피할 수 있다.
fun mian(){
test(1, 2, "3") // d에 defult 값을 주어 생략 가능하다.
}
fun test(a: Int, b: Int, strC: String, d: Int = 0){
/**
**
**/
}
3. 최상위 함수와 프로퍼티
코틀린은 함수를 만들때 꼭 어떤 클래스 안에 선언해 주지 않아도 된다.
package com.example.kotlin_study
// main.kt 파일
fun testFun(...): String {...}
위와 같이 선언해 주어도 testFun() 함수를 사용할수가 있다. JVM은 클래스 안에 들어있는 코드만 실행이 가능하기 때문에 컴파일러는 소스가 들어있는 코틀린 파일 명과 동일한 새로운 클래스를 정의해준다.
package com.example.kotlin_study
var opCount = 0
fun main() {...}
프로퍼티도 동일하게 클래스 밖에 선언이 가능하다.
확장 함수 & 확장 프로퍼티
1. 확장 함수란?
코틀린은 기본 클래스가 final이기 때문에 open 키워드를 사용하지 않으면 상속이 불가능하고 외부 라이브러리를 직접 함수를 추가하거나 변경하기 어렵거나 불가능한 경우가 있다. 이런 이유로 코틀린은 확장 개념을 지원한다.
클래스에서 상속하거나 디자인 패턴을 사용하지 않고 클래스나 인터페이스를 새로운 기능으로 확장할 수 있다. 확장 함수를 사용해 필요에 맞춰 기능 변경이 가능하다.
fun MutableList<Int>.swap(index1: Int, index2: Int) {
val tmp = this[index1] //this는 MutableList<Int>를 의미한다.
this[index1] = this[index2]
this[index2] = tmp
}
fun main(){
val list = mutableListOf(1, 2, 3)
list.swap(0, 2)
}
2. 확장 함수 특징
- 확장 함수는 오버라이드할 수 없다.
- 확장 함수는 클래스 밖에 선언되기 때문에 클래스의 일부분이 아니라 오버라이드가 불가능하다.
- 컴파일시(정적) 어떤 타입일지 결정된다.
- 확장 함수는 오버로드가 가능하다.
- 확장 함수의 이름이 같더라도 매개 변수 타입이 다르다면 오버로드가 가능하다.
- 동일한 클래스 멤버 함수가 있는 경우 멤버 함수가가 호출 된다.
- 확장 함수 보단 멤버 함수의 우선순위가 더 높다.
- 확장 함수 내부에서 수신객체를 생략하거나 this로 표기가 가능하다.
3. 확장 프로퍼티란?
일반 프로퍼티와 동일하지만 수신 객체 클래스가 추가 된다.
val String.lastChar: Char
get() = get(length - 1)
4. 확장 프로퍼티 특징
- 게터 구현이 제공 되지 않으니 필수로 게터를 정의 해야한다.
- 초기화 코드를 담을 공간이 없기 때문에 초기화 코드로 사용이 불가능하다.
로컬 함수와 확장
1. 로컬 함수와 확장
하나의 메소드가 길어지면 부분부분 나눠서 각 부분으로 분리를 하고 재사용을 할 수 있게 된다. 하지만 이런 방법으로 재사용을 하게 되면 각 메소드간의 관계 파악이 어려워져 코드 가독성이 떨어질 수도 있다. 코틀린은 함수에서 추출한 함수를 원 함수 내부에 중첩이 가능하다. 이 방법을 사용하면 문법적인 부가 비용이 들지 않고 깔끔하게 코드를 조작할 수 있게된다.
'Kotlin > Kotlin In Action' 카테고리의 다른 글
Enum / when (0) | 2023.07.22 |
---|---|
클래스와 프로퍼티 (0) | 2023.07.20 |
코틀린 기초 (0) | 2023.07.13 |
코틀린이란? (0) | 2023.07.02 |