[커니의 Kotlin] Chap3
해당 글은 커니의 코틀린 책을 구매하여 공부하고 작성한 글입니다.
조건 확인 함수
특정 값의 일치 여부 확인
- check() : 인자로 받은 표현식이 참인지 확인하며 참이 아닌 경우 IllegalStateException 예외를 발생시킨다.
- require() : 인자로 받은 표현식이 참인지 확인하며 참이 아닌 경우 IllegalArgumentException 예외를 발생시킨다.
check(), require() 함수 모두 값을 확인하는 형태뿐만 아니라 조건이 일치하지 않았을 경우 수행할 작업을 함께 지정할 수 있는 형태의 함수를 지원한다.
1 | fun showMessage(isPrepared: Boolean, message: String){ |
checkNotNull(), requireNotNull()
함수를 사용해 특정 값의 널 여부를 확인하고 널이 아닌 값을 반환받을 수 있다. 이도 위의 함수처럼 단순히 값을 확인만 하는 형태와 함께 실행할 함수를 지정하는 형태를 지원한다.
1 | fun showMessage(message: String){ |
명시적으로 실행 중단하기
프로그램이 실행될 때, 호출될 가능성이 없는 영역이 있다. 하지만, 예기치 못한 이유로 이 영역이 실행되면 프로그램에 부작용이 발생하게 된다. 따라서 이 같은 영역에 진입하게 되는 경우 임의로 예외를 발생시킬 수 있다. error()
함수를 이용하면 된다.
1 | fun showMessage(isPrepared: Boolean, message: String){ |
다른 부분의 작업이 완료되어야 구현이 가능할 때, 보통 주석을 달아서 추가 작업이 필요하다고 표시하는 경우가 대부분이다. 하지만, 간혹 이런 주석을 확인하지 못하고 그냥 두면 버그가 발생하기도 한다. 이 문제를 해결하기 위해 코틀린에서는 TODO()
함수를 제공한다. 이를 통해서 NotImplementedError를 발생시켜 아직 이 부분이 완성되지 않았음을 알려준다.
1 | class Car{ |
컬렉션 생성 함수
1. 배열
특정 원소를 담고 있는 배열을 생성하기 위해서 arrayOf()
함수를 사용한다.
빈 배열을 생성하고 싶은 경우 emptyArray()
함수를 사용한다.
널 값을 포함할 수 있는 배열을 생성하고 싶은 경우, arrayOfNulls()
함수를 사용한다.
1 | // 문자열을 포함하는 배열을 생성한다. 인자를 통해 타입 추론이 가능하다. |
자바의 원시 타입을 포함하는 배열은 코틀린의 배열과 다른 타입으로 취급되므로, 위의 함수가 아닌 각 타입에 맞는 함수를 사용해야 한다. 아래에서 소개하는 것은 자바의 원시 타입을 포함하는 배열을 생성하는 함수들이다.
- booleanArrayOf() : BooleanArray 를 반환하며 자바의 boolean[] 배열과 호환된다.
- byteArrayOf()
- charArrayOf()
- doubleArrayOf()
- floatArrayOf()
- intArrayOf()
- longArrayOf()
- shortArrayOf()
결국 동일하기 때문에 나머지는 설명을 생략했다.
2. 리스트
listOf()
: 포함하는 요소를 읽을 수만 있고 수정할 수 없는 읽기 전용 리스트를 생성할 수 있다. 다른 말로 immutable 하다고 한다.listOfNotNull()
: 널 값은 무시하고 널이 아닌 값으로만 리스트를 구성할 수 있다. 인자로 전달된 모든 값이 널이라면 빈 리스트를 반환한다.
1 | // 인자가 null 값이므로 빈 리스트를 반환한다. |
리스트에 포함된 요소를 수정할 수 있는 리스트는 mutableListOf()
함수를 사용하여 생성한다. 함수의 정의는 아래와 같다.
- fun
mutableListOf(vararg elements: T): MutableList - 인자로 받은 elements를 요소로 가지며 수정 가능한 리스트를 반환한다.
- fun
mutableListOf(): MutableList - 비어있는 수정 가능한 리스트를 반환한다.
또한, 자주 사용되는 ArrayList도 arrayListOf()
함수를 사용해 쉽게 생성할 수 있다.
이외에도 Map, Set(집합) 자료구조도 List와 비슷한 함수를 가지고 있다.
읽기만 가능한 함수와 수정도 가능한 함수까지 이름만 다르고 동일한 형태를 취하기 때문에 설명은 생략하도록 하겠다.
- Map : Key, value 형태를 갖는다.
- Set : 중복을 허용하지 않는 집합이며 순서가 없다.
스트림 함수
자바8 에서는 리스트나 맵과 같은 컬렉션에 포함된 자료를 쉽게 다룰 수 있도록 스트림 기능을 제공한다. 코틀린에서는 스트림 대신 유사한 역할을 하는 함수들을 표준 라이브러리에서 제공하며, 확장 함수 형태로 제공된다. 자바8의 스트림 기능을 사용하지 않지만, 편의상 스트림 함수라 칭하도록 하겠다.
변환
- map() : 함수는 컬렉션 내 인자를 다른 값이나 타입으로 변환할 때 사용한다.
- mapIndexed() : 함수는 컬렉션 내 포함된 인자의 인덱스 값을 변환 함수 내에서 사용할 수 있다.
- mapNotNull() : 함수는 컬렉션 내 인자를 변환함과 동시에, 변환한 값이 널 값인 경우 이를 무시한다.
- flatMap() : 함수는 map() 함수와 달리 반환형이 Iterable이다. 따라서 하나의 인자에서 여러 개의 인자로 매핑이 필요한 경우에 사용한다.
- groupBy() : 함수는 컬렉션 내 인자들을 지정한 기준에 따라 분류하며, 각 인자들의 리스트를 포함하는 맵 형태로 결과를 반환한다.
1 | fun main(args: Array<String>) { |
내 생각에 자주 사용할 함수들은 map(), flatMap() 이라고 생각한다. 까먹더라도 이 두 함수는 꼭 기억하자!
필터
- filter() : 컬렉션 내 인자들 중 주어진 조건과 일치하는 인자만 걸러주는 역할
- take() : 함수의 인자로 받은 개수만큼만을 인자로 갖는 리스트를 반환한다.
- takeLast() : take() 함수와 반대로 뒤에서부터 이 함수의 인자로 받은 개수만큼만을 인자로 갖는 리스트를 반환한다.
- takeWhile() : 첫 번째 인자부터 시작하여 주어진 조건을 만족하는 인자까지를 포함하는 리스트를 반환한다.
1 | package Chap04 |
drop() 함수라는 것이 존재하는데, 이는 take() 함수의 반대 역할을 한다. 조건을 만족하는 항목을 컬렉션에서 제외한 결과를 반환한다. take() 함수와 유사하게 dropLast(), dropWhile(), dropLastWhile() 함수를 지원한다. 사용 예제는 책을 참고하길 바란다.
다른 종류의 함수
- first() : 컬렉션 내 첫 번째 인자를 반환한다. 뿐만 아니라 조건을 만족하는 첫번째 인자를 반환할 수도 있다. 조건을 만족하는 인자가 없는 경우 NoSuchElementException 예외를 발생시킨다.
- firstOrNull() : 예외 대신 널 값을 반환하도록 할 수 있다.
- last() : 함수는 first() 함수와 반대 역할을 한다.
- lastOfNull() : 함수도 지원한다.
1 | fun main(args: Array<String>) { |
distinct()
: 함수는 컬렉션 내 포함된 항목 중 중복된 항목을 걸러낸 결과를 반환한다. 이때 항목의 중복 여부는 equals()로 판단하며, distinctBy() 함수를 사용하면 비교에 사용할 키 값을 직접 설정할 수 있다.
1 | fun main(args: Array<String>) { |
조합 및 합계
zip()
: 두 컬렉션 내의 자료들을 조합하여 새로운 자료를 만들 때 사용한다. 두 컬렉션 간 자료의 개수가 달라도 사용할 수 있으며, 이 경우에 반환되는 컬렉션의 자료 수는 조합에 사용하는 컬렉션 중 자료의 수가 더 적은 쪽을 따라간다.
기본값으로는 조합된 결과를 Pair로 만들어주며, 원하는 경우 조합 규칙을 사용자가 정의하여 사용할 수도 있다.
1 | fun main(args: Array<String>) { |
- joinToString() : 함수는 컬렉션 내 자료를 문자열 형태로 변환함과 동시에, 이를 조합하여 하나의 문자열로 생성한다. 이는 컬렉션 내 자료를 직렬화할 때 매우 유용하다.
인자 없이 함수를 호출하는 경우 기본 설정을 바탕으로 컬렉션 내 자료를 문자열로 변환하며, 몇 가지 인자를 함께 전달하면 자신이 원하는 형태로 출력 문자열을 구성할 수도 있다.
- count() : 함수는 컬렉션 내 포함된 자료의 개수를 반환하며, 별도의 조건식을 추가하면 해당 조건을 만족하는 자료의 개수를 반환하도록 할 수 있다.
1 | fun main(args: Array<String>) { |
reduce()
: 함수는 컬렉션 내 자료들을 모두 합쳐 하나의 값으로 만들어주는 역할을 한다. joinToString() 함수는 reduce() 함수의 일종이라고 볼 수 있다. 첫 번째 자료부터 조합을 시작하며, reduceRight() 함수는 동일한 작업을 컬렉션 내 마지막 자료부터 시작한다.
1 | fun main(args: Array<String>) { |
- fold() : 함수는 reduce() 함수와 거의 동일한 역할을 하나, 초기값을 지정할 수 있다. fold()도 컬렉션 내 마지막 인자부터 작업을 수행하는 foldRight() 함수를 지원한다.
1 | /** |
기타 함수
- any() : 함수는 컬렉션 내 단 하나의 자료라도 존재한다면 true를, 그렇지 않으면 false를 반환한다. 조건식을 전달할 경우, 해당 조건식을 만족하는 자료의 유무 여부를 반환한다.
- none() : 함수는 any() 함수와 반대 작업을 수행하며, 컬렉션이 비어있는지 여부를 반환한다. 마찬가지로 조건식을 전달할 경우, 해당 조건식을 만족하는 자료가 하나도 존재하지 않는지 여부를 반환한다.
1 | fun main(args: Array<String>) { |
- max(), min() : 함수는 숫자 타입의 자료를 갖는 컬렉션 내에서 각각 최대값 및 최소값을 찾아 반환한다.
- average() : 함수는 숫자 타입의 자료를 갖는 컬렉션 내 자료들의 평균을 반환한다.
1 | fun main(args: Array<String>) { |
범위 지정 함수
개발을 하다 보면 특정 객체에 있는 함수를 연속해서 사용하거나 다른 함수의 인자로 전달하기 위해 변수를 선언하고 이를 다른 곳에서는 사용하지 않는 경우가 있다. 코틀린에서는 이런 경우 유용하게 사용할 수 있는 함수를 표준 라이브러리를 통해 제공한다.
let() 함수
let() 함수는 이 함수를 호출한 객체를 이어지는 함수 블록의 인자로 전달한다.
- 정의 => fun <T, R> T.let(block: (T) -> R): R
- 이 함수를 호출하는 객체를 이어지는 함수형 인자 block의 인자로 전달하며, block 함수의 결과를 반환한다.
- 불필요한 변수 선언을 방지할 수 있다.
- 널 값이 아닌 경우를 체크한 후 특정 작업을 수행할 때 사용할 수 있다.
- Nullable 객체를 다른 Nullable 객체로 변환하는 경우
- 단일 지역 변수의 범위를 제한하는 경우
1 | // 이를 통해 불필요한 변수 선언을 방지할 수 있다. |
apply() 함수
이 함수를 호출하는 객체를 이어지는 함수 블록의 리시버(receiver)로 전달한다.
- 정의 => fun
T.apply(block: T.() -> Unit): T - 이 함수를 호출하는 객체를 이어지는 함수형 인자 block의 리시버로 전달하며, 함수를 호출한 객체를 반환한다.
- 함수를 호출한 객체를 함수형 인자 block의 리시버로 전달하므로, 이 블록 내에서는 해당 객체 내의 프로퍼티나 함수를 직접 호출할 수 있다.
- 따라서 객체 이름을 명시하지 않아도 되므로 코드를 간략하게 만들 수 있다.
1 | val perter = Person().apply{ |
with() 함수
인자로 받은 객체를 이어지는 함수 블록의 리시버로 전달한다.
- 정의 => fun <T,R> with(receiver: T, block: T.() -> R): R
- 인자로 받은 객체 receiver를 이어지는 함수형 인자 block의 리시버로 전달하며, block 결과를 반환한다.
- 함수에서 사용할 객체를 매개변수를 통해서 받는다.
- Non-nullable(Null이 될 수 없는) 객체에 이 함수를 사용한다.
- 그리고 결과가 필요하지 않은 경우에 사용한다.
1 | val person: Person = getPerson() |
run() 함수
인자가 없는 익명 함수처럼 사용하는 형태와 객체에서 호출하는 형태를 제공한다.
- 정의 => fun
run(block: () -> R): R - 함수형 인자 block을 호출하고 그 결과를 반환한다.
- 정의 => fun<T,R> run(block: T(). -> R): R
- 이 함수를 호출한 객체를 함수형 인자 block의 리시버로 전달하고 그 결과를 반환한다.
run() 함수를 인자가 없는 익명 함수처럼 사용하는 경우, 복잡한 계산을 위해 여러 임시 변수가 필요할 때 유용하게 사용할 수 있다. run() 함수 내부에서 선언되는 변수들은 블록 외부에 노출되지 않으므로 변수 선언 영역을 확실히 분리할 수 있다.
1 | val padding = run{ |
객체에서 run() 함수를 호출하는 경우 with() 함수와 유사한 목적으로 사용할 수 있다. 단, run() 함수는 안전한 호출을 사용할 수 있으므로 널 값일 수 있는 객체의 속성이나 함수에 연속적으로 접근해야 할 때 유용하다.
1 | fun printAge(person: Person?){ |