Programming/Kotlin

코틀린(Kotlin) - 함수 정의와 호출

JunsuKim 2021. 9. 25.
728x90

컬렉션(Collection) 생성

Kotlin에서는 자체 Collection이 아닌 Java Collection을 사용하여 Java 코드와 상호 작용이 용이하다.

val array = arrayOf("ONE", "TWO", "THREE")
val set = hashSetOf(1, 2, 3)
val list = arrayListOf(1, 2, 3)
val map = hashMapOf(1 to "one", 2 to "two")

이와 같이 배열을 생성하기 위해서는 arrayOf() 함수를, 집합을 생성하기 위해서는 hashSetOf() 함수, 배열 리스트를 생성하기 위해서는 ArrayListOf() 함수, 맵을 생성하기 위해서는 hashMapOf() 함수를 사용한다.

이름 붙인 인자

이름붙인 인자를 사용하면 함수의 인자가 많을 때 함수 호출의 가독성을 향상할 수 있다.

fun <T> joinToString(
    collection: collection<T>.
    seperator: String,
    prefix: String,
    postfix: String
)

                                                                    ↓

// 이름 없는 인자
fun joinToString(collection, " ", " ", ".")

// 이름 붙인 인자
fun joinToString(collection, seperator = " ", prefix = " ", postfix = " .")

* 인자 중 하나라도 이름을 명시했다면, 그 뒤에 오는 인자는 이름을 명시해야 한다.

디폴트 파라미터 값

fun add(a: Int, b: Int=1) = a + b
fun minus(a: Int = 10, b: Int) = a- b

fun main() {
    println(add(3))
    println(add(1, 2))
    println(minus(7))
    println(minus(b = 7))
    println(minus(2, 1))
}

/*
4
3
Complie Error
3
1
*/

함수의 매개변수에는 기본값을 설정할 수 있다. 기본값이 설정되어 있다면 함수를 호출할 시 함수를 선언할 때와 같은 순서로 인자를 지정해야 하기 때문에 (b = 7)과 같이 매개변수의 이름을 붙여 값을 전달해주면 된다. 기본값이 설정되어 있다 하더라도 함수를 호출할 때 다른 값을 전달함으로써 변경시킬 수 있다.

최상위 함수

코틀린에서는 다른 언어들과는 달리 최상위 함수라는 것이 존재한다.

클래스 내의 함수를 사용하기 위해서는 객체를 만든 후 해당 클래스의 함수를 호출해야 하지만, 클래스 없이 함수만 정의한 경우 객체 생성를 생성할 필요 없이 어디서든 호출이 가능하다. 이와 같은 함수를

최상위 함수라고 한다.

fun main() {
    Hello()
}

fun Hello() {
    println("Hello")
    Hi()
}

fun Hi() {
    println("Hi")
}

/*
Hello
Hi
*/

이와 같이 클래스 없이 함수만 존재하며 어디서든 접근이 가능한 Hello()와 Hi()는 최상위 함수이다.

 

* 코틀린 코드의 최상단에 @file:JvmName("클래스명")을 통해 자바에서 클래스명.최상위함수명()으로 호출이 가능하다.

최상위 프로퍼티

함수와 같이 프로퍼티도 최상위 수준에 사용할 수 있다. 이는 정적 필드에 저장이 된다.

var cnt = 0

fun count() {
    count++
}

fun print() {
    println("cnt: ${cnt}")
}

다른 언어들에서의 정적 변수와 유사하다고 보면 된다.

확장 함수

코틀린에서는 클래스의 밖에 있지만 클래스의 멤버 메서드인 것처럼 호출이 가능한 함수를 확장 함수라 한다.

fun String.lastChar(): Char = this.get(this.length - 1)
// this는 생략이 가능하다.

위와 같이 String 클래스에는 lastChar()라는 메소드가 없지만 확장 함수를 생성하여 마치 String 클래스의 메소드인 것처럼 사용이 가능하다. 

String.lastChar(): Char와 this의 역할은 다음과 같다.

  • String -> 수신 객체 타입(receiver type)
  • lastChar() -> 확장 함수의 이름
  • Char -> 반환 타입(return type)
  • this -> 수신 객체(receiver type),  생략이 가능하다.

확장함수의 특성

  • 함수 이름 앞에 확장할 클래스명을 붙여서 정의한다.
  • 확장 함수 안에 수신 객체의 메서드나 프로퍼티 사용이 가능하나, private, protected 멤버는 접근이 불가능하다.
  • 확장 함수보다 멤버 함수가 우선이다.(둘의 이름이 같을 경우 멤버 함수가 우선이 된다.)
  • 확장 함수를 사용하려면 임포트 해야 한다.
    • import strings.lastChar
      
      val c = "Kotlin".lastChar()
  • as로 확장 함수를 다른 이름으로 임포트 가능하다.(이름이 충돌할 경우 주로 사용)
    • import strings.lastChar as last
      
      val c = "Kotlin".last()
  • 확장 함수는 정적(static) 메소드이기 때문에 오버라이드(override)할 수 없다. -> 클래스의 일부가 아닌 밖에서 선언되기 때문이다.

확장 함수를 사용한 예시를 보자.

fun main(){
    Language().kind('a')
    Language().kind(1)
    Language().kind2('c')
}

class Language {
    fun kind(Char: Char){
        println("Kotlin")
    }
    fun kind2(Char: Char){
        println("Java")
    }
}

fun Language.kind(Int: Int){
    println("Python")
}

fun Language.kind2(Char: Char){
    println("C++")
}

/*
Kotlin
Python
Java
*/

확장 프로퍼티

함수와 같이 프로퍼티도 확장이 가능하다. 이는 기존 클래스 객체에 대한 프로퍼티 형식으로 사용할 수 있는 API를 추가할 수 있게 해 준다.

확장 프로퍼티는 상태를 저장할 수 없어 초기화할 수 없고, get()을 구현해야 한다.

val String.lastChar: Char
    get() = get(length - 1)

위와 같이 일반 프로퍼티에서 수신 객체 클래스만이 추가된 것이 확장 프로퍼티이다.

 

List, StringBuilder와 같이 상태를 저장할 수 있는 클래스일 경우 가변적이므로 setter도 만들 수 있으며, 프로퍼티를 var로 선언해야 한다.

var StringBuilder.lastChar: Char
    get() = get(length - 1)
    set(sb: Char) {
        this.setCharAt(length - 1, value)
    }

확장 프로퍼티의 사용법은 일반 프로퍼티 사용법과 같다.

 

확장 프로퍼티를 사용한 예제를 보자.

fun String.lastChar(): Char = this.get(this.length - 1)

var StringBuilder.lastChar: Char
    get() = get(length - 1)
    set(value: Char) {
        this.setCharAt(length - 1, value)
    }

fun main() {
    println("Kotlin".lastChar())
    val sb = StringBuilder("Kotlif")
    sb.lastChar = 'n'
    println(sb)
}

/*
f
Kotlin
*/

가변인자

자바와 같이 코틀린에서도 가변인자를 지원한다.

가변 인자를 사용하면 함수를 호출할 때 인자 개수를 유동적으로 지정할 수 있다.

가변인자를 사용하려면 인자 앞에 vararg 키워드를 붙이면 된다.

fun add(vararg num: Int) = num.sum()

fun main() {
    val n1 = sum(1)
    val n2 = sum(1, 2, 3)
    println(n1)
    println(n2)
}

/*
1
6
*/

배열을 가변인자로 넘기는 방법

배열을 함수의 가변인자로 넘겨야 할 때, 배열 이름 앞에 펼침연산자(스프레드 연산자, *)를 붙여주면 된다.

fun print(vararg lan: String) {
    println(lan.joinToString())
}

fun main() {
    val language = arrayOf("Kotlin", "Java", "C")
    print(*language)
}

// Kotlin, Java, C

중위 호출 

1.to("One")
1 to "One"

이와 같이 to를 중간에 사용하는 것을 중위 호출이라 한다. to는 일반 메소드이다.

 

일반 메소드를 중위 호출이 가능하게 하려면 infix 변경자를 메소드 앞에 추가해야 한다.

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

infix fun Any.to(other: Any, other2:Any) = Pair(this, other) //컴파일 에러

infix 변경자의 인수는 한 개만 가능하다.

구조 분해 선언

val (number, name) = Pair(1, "one")
val(_, name = Pair(1, "one")

for((index, element) in collection.withIndex()) {}

map.mapValues { (key, value) -> "${value}" }
map.mapValues { (_, value) -> "${value}" }

구조 분해 선언의 내부에서는 각 변수를 초기화하기 위해 componentN() 함수를 호출한다.

모든 값이 필요한 것이 아니라면 사용하지 않을 값은 밑줄(_)로 표시하면 된다.

또한 구조 분해 선언은 for문에서 인덱스와 값을 변수에 저장할 수 있다.

 

출처 : Kotlin In Action

728x90

댓글