[kotlin] 3장 함수의 정의와 호출 - 2

김우경·2022년 1월 27일
0

코액션

목록 보기
2/3

3.4 컬렉션 처리

코틀린 언어 특성

  • vararg : 호출 시 인자 개수가 달라질 수 있는 함수를 정의할 수 있음
  • 중위 함수 호출 구문: 인자가 하나인 메소드를 편하게
  • 구조 분해 선언: 복합적인 값을 분해하고 여러 변수에 나눠 담을 수 있음

자바 컬렉션 API의 확장

last()

val strings: List<String> = listOf("first", "second", "last")
println(strings.last())

>>> last

실제 구현은 다음과 같이 List의 확장함수로 구현되어 있다.

max()

val numbers: Collection<Int> =setOf(1, 14, 2)
println(numbers.max())

→ 코틀린 1.4 버전 이후로 deprecated되었고, maxOfNull()을 사용한다.

가변 인자 함수

위에서 사용한 listOf()는 다음과 같이 정의되어 있다.

public fun <T> listOf(vararg elements: T): List<T> = if (elements.size > 0) elements.asList() else emptyList()
  • 자바의 가변 길이 인자 : ...
  • 코틀린의 가변 길이 인자: 파라미터 앞에 vararg

이미 배열인 원소를 가변 길이 인자로 넘기기

fun main(args: Array<String>) {
	val list = listOf("args: ", *args)
	println(list)
}
  • 스프레드 연산자 * 사용 → 자바에서는 사용할 수 없는 기능

중위 호출과 구조 분해 선언

val map = mapOf(1 to  "one", 7 to "seven", 53 to "fifty-three")

와 같이 map을 만들때, to는 중위호출로 to라는 일반 메소드를 호출한다.

중위 호출

  • 인자가 하나뿐인 일반 메소드/확장 함수에 사용 가능
  • 사용 방법
    • infix 변경자를 함수 선언 앞에 추가
  • to를 일반 방법으로 호출 : 1.to("one")
  • to를 중위 호출로 호출 : 1 to("one")

구조 분해 선언

infix fun Any.to(other: Any) = Pair(this, other)

위와 같이 선언된 to()는 Pair의 구조 분해 선언으로 두 변수를 즉시 초기화 한다.

val (number, name) = 1 to "one"
  • 활용: Pair 뿐만 아니라 다른 객체에도 적용 가능함
    for ((index, element) in collection.withIndex()) {
    	println("$index: $element")
    }

문자열과 정규식 다루기

문자열 나누기

  • 자바의 split : separator가 정규식임 → .는 모든 문자를 나타내는 정규식으로 해석됨
  • 코틀린에서의 문자열 나누기
    • 파라미터로 Regex 타입을 받는다.

      println("12.345-6.A".split("\\.|-".toRegex()))
      
      >>> [12, 345, 6, A]

      → 코틀린 정규식 문법은 자바와 같음

    • 자바 split 오버로딩

      println("12.345-6.A".split(".", "-"))
      
      >>> [12, 345, 6, A]

      → 여러 separator 사용 가능

정규식과 3중 따옴표로 묶은 문자열

파일 path를 디렉토리, 파일명, 확장자로 구분하기

  • 방법 1) 코틀린 라이브러리 사용
fun parsePath(path: String) {
    val directory = path.substringBeforeLast("/")
    val fullName = path.substringAfterLast("/")
    val fileName = fullName.substringBeforeLast(".")
    val extension = fullName.substringAfterLast("/")

    println("Dir: $directory, name: $fileName, ext: $extension")
}

fun main() {
    parsePath("/Users/yole/kotlin-book/chapter.doc")
}
  • 방법 2) 정규식 사용
fun parsePath(path: String) {
    val regex = """(.+)/(.+)\.(.+)""".toRegex()
    val matchResult = regex.matchEntire(path)

    if (matchResult != null) {
        val (directory, filename, extension) = matchResult.destructured
        println("Dir: $directory, name: $filename, ext: $extension")
    }
}

fun main() {
    parsePath("/Users/yole/kotlin-book/chapter.doc")
}

→ """"" : 삼중 따옴표 문자열에서는 어떤 문자도 이스케이프 할 필요가 x

여러 줄 3중 따옴표 문자열

fun main() {
    val kotlinLogo = """| //
        .| //
        .|/\"""
    println(kotlinLogo.trimMargin("."))
}

>>> | //
>>> | //
>>> |/\

→ 와 같이 문자열이 텍스트가 포함해도 쉽게 문자열로 변환할 수 있음

로컬 함수 확장

  • 추출한 함수를 원래 함수 내부에 중첩시키기 : 일반적으로 한단계만 중첩시키라고 권장
import java.lang.IllegalArgumentException

class User(val id: Int, val name: String, val address: String)

fun saveUser(user: User) {
    if (user.name.isEmpty()) {
        throw IllegalArgumentException("name null")
    }

    if (user.address.isEmpty()) {
        throw IllegalArgumentException("address null")
    }
}

fun main() {
    saveUser(User(1, "", ""))
}

→ 함수가 복잡하지는 않지만, 검증하는 코드가 반복된다

import java.lang.IllegalArgumentException

class User(val id: Int, val name: String, val address: String)

fun saveUser(user: User) {
    fun validate(user: User, value: String, fieldName: String) {
        if (value.isEmpty()) {
            throw IllegalArgumentException("$[user.id]: null $fieldName")
        }
    }
    validate(user, user.name, "Name")
    validate(user, user.address, "Address")
}

fun main() {
    saveUser(User(1, "", ""))
}

→ 중복되는 검증 부분을 메소드로 추출하고, 원래 함수 내부에 중첩

→ user 객체를 로컬 함수에 하나하나 전달해야 된다

import java.lang.IllegalArgumentException

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("$[user.id]: null $fieldName")
        }
    }
    validate(user.name, "Name")
    validate(user.address, "Address")
}

fun main() {
    saveUser(User(1, "", ""))
}

→ 로컬 함수가 자신이 속한 바깥 함수의 모든 파라미터와 변수를 사용할 수 있음을 이용

import java.lang.IllegalArgumentException

class User(val id: Int, val name: String, val address: String)

fun User.validateBeforeSave(user: User) {
    fun validate(value: String, fieldName: String) {
        if (value.isEmpty()) {
            throw IllegalArgumentException("$[user.id]: null $fieldName")
        }
    }
    validate(name, "Name")
    validate(address, "Address")
}

fun saveUser(user: User) {
    user.validateBeforeSave(user)
}

fun main() {
    saveUser(User(1, "", ""))
}

→ 확장 함수로 추출

: 이 검증 로직이 User 외에 다른 데에서는 쓰이지 않아서 포함시키고 싶지 않은 경우

profile
Hongik CE

0개의 댓글