본문 바로가기

코틀린

[Kotlin In Action] 3장. 함수 정의와 호출

Kotlin in Action 책에서 각 챕터의 Summary를 보며 부족한 키워드만 후라닥 정리하는 글.
안드정이 코틀린 인 액션 책 스터디하면서 정리한 자료도 참고할 예정 땡큐땡큐 

 

컬렉션 정의

  • 코틀린에서 컬렉션은 두가지로 분리됨
  • Collection : 불변
  • MutableCollection : 가변
  • 자체 콜렉션이 아닌 자바 콜렉션 사용: 자바 코드와 상호작용 용이

함수를 호출하기 쉽게 만들기

이름있는 인자

fun <T> joinToString(
    collection: Collection<T>,
    separator: String = ", ",
    prefix: String = "",
    postfix: String = ""
): String
joinToString(collection, separator = " ", prefix = " ", postfix = ".")
  • 인자 중 하나라도 이름을 명시하면, 그 뒤 에 오는 모든 인자는 이름을 명시해야 함
  • 이름있는 인자 사용하면 오버라이드 할 필요성이 사라지고, 인자값 이름으로 파라미터를 쉽게 구별할 수 있다.

디폴트 파라미터 값

  • 이름 없는 인자: 일부를 생략하면 뒷부분의 인자들 생략
  • 이름 붙인 인자: 지정하고 싶은 인자를 이름을 붙여 지정
joinToString(collection, ", ", "", "")
joinToString(collection)
joinToString(collection, "; ")
joinToString(collection,
postfix = ";", prefix = "# ")

최상위 함수

  • 자바의 정적 메서드로 변환됨
package strings
fun joinToString(...): String { ... }

최상위 프로퍼티

  • const는 원시 타입과 String에만 가능
  • 정적 변수로 변환됨
// 함수 호출될 때마다 증가하는 변수 용도
var opCount = 0
fun performOperation() {
	opCount++
	// ...
}
fun reportOperationCount() {
	println("Operation performed $opCount times")
}

확장 함수

  • 어떤 클래스의 멤버 함수인 것처럼 호출할 수 있지만, 그 클래스의 밖에 선언된 함수
package strings
fun String.lastChar(): Char = this[this.length -1]

"mazzi".lastChar()
  • 여기서 클래스의 이름인 String은 수신 객체 타입
  • 확장 함수가 호출되는 대상이 되는 값(객체)인 this를 수신 객체라 함
    • this는 생략 가능
  • 확장함수는 오버라이딩할 수 없음
  • 확장 함수 안에서 수신 객체의 메서드나 프로퍼티 사용 가능 (private 멤버나 protected 멤버만 접근 불가)
  • 확장 함수 이름과 클래스 멤버 함수 이름이 같을 경우 멤버 함수가 우선순위가 더 높음
  • 확장 함수를 임포트할 때 as로 다른 이름으로 임포트 가능(이름이 충돌할 경우 주로 사용)
import strings.lastChar as last
val c = "mazzi".last()

확장 프로퍼티

  • 기존 클래스 객체에 대한 프로퍼티 형식 구문으로 사용할 수 있는 API 추가
  • 상태를 저장할 방법이 없지만, 프로퍼티 문법으로 더 짧게 코드를 작성할 수 있음
  • 초기화가 불가능하고, get()을 필수 구현해야함
val String.first: Char
	get() = get(0)
var StringBuilder.lastChar: Char
	get() = get(length ‐ 1)
	set(ch: Char) {
		this.setCharAt(length ‐ 1, ch)
	}

➕ 더 보면 좋을 자료

컬렉션 처리

가변 길이 인자

  • 파라미터 앞에 vararg를 붙음
  • * 연산자로 배열 내용을 펼칠 수도 있음
fun listOf<T>(vararg values: T): List<T> { ... }

fun main(args: Array<String>) {
	val list = listOf(1, 2, 3, 4)
	// 배열은 스프레드 연산자(*)로 전달
	val arglist = listOf("args: ",*args)
	// listOf(args) ‐‐> List<Array<String>>이 됨
}

중위 호출 (infix 수식어)

  • 일반 메서드를 중위 호출이 가능하게 만들기 위해 infix 변경자를 메서드 앞에 선언해야 함
  • 중위 호출의 인수는 한개만 가능함
  • `객체 메서드이름 유일인자` 순으로 작성
infix fun Any.to(other: Any) = Pair(this, other)
1.to("one")
1 to "one"
val (number, name) = 1 to "one"

코틀린 인 액션

  • Pair의 실제 선언은 제네릭 타입이지만, 이를 생략중

구조 분해 선언

  • 객체가 가지고 있는 여러 값을 분해해서 여러 변수에 한꺼번에 초기화할 수 있음
  • 구조 분해 선언 내부에서는 각 변수를 초기화하기 위해 componentN이라는 함수 호출
val (number, name) = Pair(1, "one")
val (_, name) = Pair(1, "one") // 사용하지 않는 변수

for ((index, element) in list.withIndex()) { // IndexedValue<Int>
}

map.mapValues { (key, value) ‐> "$value!" } // 람다에서의 분해 선언
map.mapValues { (_, value) ‐> "$value!" }
  • 데이터 클래스 주 생성자에 있는 프로퍼티에 대해서는 컴파일러가 자동으로 componentN 함수를 만들어줌
  • 일반 클래스에서는 다음과 같이 구현
class Crew(val name: String, val nickname: String) {
   operator fun component1() = name
   operator fun component2() = nickname
}

코드 다듬기

로컬 함수

  • 함수 내부에 함수를 중첩해서 만듦
  • 로컬 함수는 바깥 함수의 파라미터 변수에 접근 가능
  • 로컬 함수에 파라미터 전달하여 사용 가능
fun saveUser(user: User) {
    // 로컬 함수
    fun validate(value: String, fieldName: String) {
        if (value.isEmpty()) {
    		// user.id : 자신이 속한 바깥 함수의 파라미터와 변수 사용할 수 있음
            throw IllegalArgumentException("Can't save user ${user.id}: " +
                "empty $fieldName")
        }
    }
    validate(user.name, "Name")
    validate(user.address, "Address")
}

중복 코드를 줄일 수 있으며, 작게 쪼개진 함수들이 많은 경우 로컬 함수를 사용해 공통처리

➕ 더 보면 좋을 강추 자료