[Kotlin] 함수 정의와 호출

sonnie
lucky-sonnie
Published in
7 min readJan 1, 2021

3.4 컬렉션 처리: 가변 길이 인자, 중위 함수 호출, 라이브러리 지원

컬렉션 처리에 관한 코틀린 언어 특성

  • vararg 키워드를 사용하면 호출 시 인자 개수가 달라질 수 있는 함수를 정의할 수 있다.
  • 중위(infix) 함수 호출 구문을 사용하면 인자가 하나뿐인 메소드를 간편하게 호출할 수 있다.
  • 구조 분해 선언(destructuring declaration)을 사용하면 복합적인 값을 분해해서 여러 변수에 나눠 담을 수 있다.

3.4.1 자바 컬렉션 API 확장

IDE 코드 자동 완성 기능을 통해 그런 메소드나 함수를 살펴보자.

3.4.2 가변 인자 함수: 인자의 개수가 달라질 수 있는 함수 정의

코틀린에서는 배열을 명시적으로 풀어서 배열의 각 원소가 인자로 전달되게 해야한다. 기술적으로는 스프레드spread 연산자가 그런 작업을 해준다. (실제로 전달하려는 배열 앞에 *를 붙이기만 하면 된다.)

fun main(args: Array<String>){
val list = listOf(“args:”, *args)
println(list)
}
// 결과값
[args: , one, two, three]

이 예제는 스프레드 연산자를 통하면 배열에 들어있는 값과 다른 여러 값을 함께 써서 함수를 호출할 수 있음을 보여준다.

3.4.3 값의 쌍 다루기: 중위 호출과 구조 분해 선언

val map = mapOf(1 to "one", 7 to "seven", 10 to "ten")

위의 코드는 중위 호출이라는 특별한 방식으로 to라는 일반 메소드를 호출한 것이다.

1.to(“one”) // "to" 메소드를 일반적인 방식으로 호출함
1 to "one" // "to" 메소드를 중위 호출 방식으로 호출함

인자가 하나뿐인 일반 메소드나 인자가 하나뿐인 확장 함수에 중위 호출을 사용할 수 있다. 함수를 중위 호출에 사용하게 허용하고 싶으면 infix 변경자를 함수 선언 앞에 추가해야 한다.

infix fun Any.to(other: String) = Pair(this, other)
infix fun
Any.ro(value: String) = Pair(this, value) // 내가 만든 infix

Pair는 코틀린 표준 라이브러리 클래스로, 그 이름대로 두 원소로 이뤄진 순서쌍을 표현한다. Pair의 내용으로 두 변수를 즉시 초기화할 수 있다.

val (number, name) = 1 to "one"

이런 기능을 구조 분해 선언이라고 부른다.

Pair 인스턴스 외 다른 객체에도 구조 분해를 적용할 수 있다. 예를 들어 key와 value라는 두 변수를 맵의 원소를 사용해 초기화할 수 있다.

val (key, value) = "two" to 2

루프에서도 구조 분해 선언을 활용할 수 있다. withIndex를 구조 분해 선언과 조합하면 컬렉션 원소의 인덱스와 값을 따로 변수에 담을 수 있다.

for((index, element) in collection.withIndex()){
println("$index : $element")
}

3.5 문자열과 정규식 다루기

3.5.1 문자열 나누기

코틀린에서는 자바의 split 대신에 여러가지 다른 조합의 파라미터를 받는 split 확장함수를 제공함으로써 혼동을 야기하는 메소드를 감춘다. 정규식을 파라미터로 받는 함수는 String이 아닌 Regex 타입의 값을 받는다. 따라서 코틀린에서는 split 함수에 전달하는 값의 타입에 따라 정규식이나 일반 텍스트 중 어느 것으로 문자열을 분리하는지 쉽게 알 수 있다.

>>> println("12.345-6.A".split("\\.|-".toRegex()))
[12, 345, 6, A]
>>> println("12.345-6.A".split(".", "-")) // 여러 구분 문자열을 지정한다.
[12, 345, 6, A]

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

fun parsePath2(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")
}
}

3중 따옴표 문자열에서는 역슬래시(\)를 포함한 어떤 문자도 이스케이프할 필요가 없다. 예를 들어 일반 문자열을 사용해 정규식을 작성하는 경우 마침표 기호를 이스케이프하려면 \\. 라고 써야하지만, 3중 따옴표 문자열에서는 \. 라고 쓰면 된다. 위의 예제에서 쓴 정규식은 슬래시와 마침표를 기준으로 경로를 세 그룹으로 분리한다. 패턴 .은 임의의 문자와 매치될 수 있다. 따라서 첫번째 그룹인 (.+)는 마지막 슬래시까지 모든 문자와 매치된다. 이 부분 문자열에는 마지막 슬래시를 제외한 모든 슬래시도 들어간다. 비슷한 이유로 두번째 그룹에도 마지막 마침표 전까지 모든 문자가 들어간다. 세번째 그룹에는 나머지 모든 문자가 들어간다.

그룹별로 분해한 매치 결과를 의미하는 destructured 프로퍼티를 각 변수에 대입

3.5.3 여러 줄 3중 따옴표 문자열

3.6 코드 다듬기: 로컬 함수와 확장

코틀린에서는 함수에서 추출한 함수를 원 함수 내부에 중첩시킬 수 있다. 그렇게 하면 문법적인 부가 비용을 들이지 않고도 깔끔하게 코드를 조작할 수 있다. 아래 코드는 로컬 함수를 사용해 코드 중복 줄인 예시.

fun saveUser(user: User){
fun validate(user: User, value: String, fieldName: String){
if(value.isEmpty()){
throw IllegalArgumentException("Can't save user ${user.id}: empty $fieldName")
}
}
validate(user, user.name, "Name")
validate(user, user.address, "Address")
}

아래 코드는 로컬 함수에서 바깥 함수의 파라미터 접근하기

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")
}

아래 코드는 검증 로직을 확장 함수로 추출하기

fun User.validateBeforeSave(){
fun validate(value: String, fieldName: String){
if(value.isEmpty()){
throw IllegalArgumentException("Can't save user $id: empty $fieldName")
}
}
validate(name, "Name")
validate(address, "Address")
}

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

출처: 코틀린 인 액션

--

--