본문 바로가기
프로그래밍/코틀린

K010. 코틀린 함수(Functions) 깊게 살펴보기 - Functions 파트1

by K-인사이터 2024. 2. 5.
반응형

안녕하세요

K-IN 입니다.

 

코틀린 함수(Functions)에 대해서 알아보겠습니다. 

전체 강의 목록은 아래의 링크를 클릭해주세요.

 

K000. 코틀린 시리즈 (연재물)

안녕하세요 K-IN 입니다. 요즘 코틀린을 이용한 개발 프로젝트가 늘어나고 있습니다. 이에, 코틀린에 대해서 상세하게 정리하는 간행물을 제작하고자 합니다. 여기에 있는 링크들은 모두 코틀린

k-in.tistory.com

 

 

코틀린 함수(Functions) 

코들린 함수란 특정 작업을 수행하는 코드 블록입니다. 

함수는 입력을 정의할 수 있고 입력에 따른 출력을 정의할 수 있습니다. 

`fun` 키워드를 통해서 선언(declare)합니다. 

 

코틀린에서 함수에 대해서 이해하려면 아래의 개념들을 숙지해야합니다. "☆" 표기는 중요도를 나타냅니다.

분량이 많아 이글에서는 4번 항목까지만 다룹니다. 

 

  1. 기본적인 함수 구조 ☆
  2. 함수 사용 방법 ☆
  3. 기본 인자(Default Arguments) ☆☆
  4. 이름지정 인자(Named Arguments) ☆☆
  5. 단일 표현식 함수(Single-expression Functions) ☆☆☆
  6. 명시적 리턴 타입(Explicit Return Types)
  7. 가변변수 (varargs) ☆☆☆
  8. 중위 표기법 (Infix Notation) ☆☆☆
  9. 함수의 범위 (Function Scope) ☆☆☆
  10. 제너릭 함수 (Generic Functions) ☆☆☆

 

다음은 코틀린 함수의 구조입니다. 함수를 정의할 때 "함수이름", "파라미터이름", "타입", "리턴타입"을 정의하고 

함수 내부의 로직을 작성하게됩니다. 

fun 함수이름(파라미터이름: 타입, ...): 리턴타입 {
    // 함수 본문
    return 리턴값
}

 

반응형

코틀린 함수 사용 방법 

코틀린 함수를 호출하는 일반적인 방법은 다음과 같습니다. 

class Stream()  {
	fun read() {
    	println("read 호출")
    }
}
fun greet(name: String) {
    println("Hello, $name!")
}

fun main() {
	// 일반적인 함수 호출 예시
	greet("Kotlin")
    // 클래스의 함수 호출 예시 
	Stream().read()
}

 

코틀린 함수 파라미터(parameters) 

코틀린 함수는 입력값을 전달받아 처리할 수 있으며 이를 함수 파라미터(Function Parameters)라고 합니다. 

여기서 파라미터는 파스칼 표기법(Pascal Notation)을 따릅니다. 

면접 질문 예상 ➡️ 파스칼 케이스(Pascal Case)와 파스칼 표기법(Pascal Notatio)의 차이점은? 

파스칼 케이스는 네이밍 규칙을 말합니다. 예를 들어 "start coding"을 파스칼 케이스로 표현하면 StartCoding 이됩니다. 
유사하게 카멜 표기법이 있으며 이 경우 "startCoding" 과 같은 형태로 변수명을 지정합니다. 

파스칼 표기법(Notation)은 엄연히 파스칼 케이스와 다릅니다. 
"파라미터이름: 타입"과 같이 변수이름에 이어 콜론(:)을 쓰고 타입을 지정하는 방식을 의미합니다. 

만약, 면접에서 파스칼 표기법이라고 한다면 Notation 인지 Case 인지를 면접관에게 우선 질문해야합니다.
번역에 따라서 모두 표기법이라고 칭할때가 있습니다. 

 

반응형

 

파스칼 표기법을 통해 변수의 타입을 명시적으로 선언하므로 함수 호출 시 데이터 규격을 맞추어주기 용이합니다.

함수의 파라미터를 선언할 때 연달아서 여러 파라미터를 선언할 경우 콤마(,) 를 사용합니다. 

 

코틀린은 유연하게 맨 마지막 파라미터에 대해서도 콤마(,)를 허용합니다.

이 문법적 특징은 추후에 함수를 변경할때 매우 편리한 특징입니다. (일일이 콤마를 제거하거나 붙이거나 하지 않아도 됩니다.) 

 

다음의 코드를 참고합니다. 

// 코틀린은 파스칼 표기법(Pascal Notation)의 함수 정의 방식을 따름 
fun powerOf(number: Int, exponent: Int): Int { /*...*/ }

fun powerOf(
    number: Int,
    exponent: Int, // 콤마를 붙여도 됨. 
) { /*...*/ }

 

코틀린 함수의 기본 인자(Default Arguments)

코틀린에서 제공하는 기본 인자(Default Arguments) 개념은 함수를 더욱 유연하게 사용할 수 있도록 합니다. 

기본 인자란 함수를 정의할 때 파라미터에 기본값(Default Value)를 할당하는 기능입니다. 

 

즉, 자주 쓰이는 값이 있다면 호출할 때마다 동일한 값을 입력하기보다는

특수한 케이스에 다른 값을 입력하는 형태가 더욱 효율적입니다. 

 

다음 코드를 참고합니다. 

fun greet(
	name: String = "World" // 파라미터에 기본값("World")를 할당 
){
    println("Hello, $name!")
}

fun main() {
	greet() // Hello, World! 가 출력됩니다.
    // .... 생략 
    greet() // Hello, World! 가 출력됩니다.
    // .... 생략
    // 이번에는 특별히 케이인이라고 출력하고 싶은데...
    greet("케이인") // Hello, 케이인! 가 출력됩니다.
}

 

면접 질문 예상 ➡️ 기본인자를 할당한 메서드를 오버라이드하는 경우 

여기서 한번더 응용을 해보겠습니다. 

A 클래스를 상속하는 B 클래스가 있다고 합니다.

A 클래스의 foo 를 override 하였을 때 기본인자의 값은 어떻게 될까요? 

 

아래의 코드를 보면서 정답을 맞추어 봅니다. 

open class A {
    open fun foo(i: Int = 10) { println(i) }
}

class B : A() {
    override fun foo(i: Int) { println(i) }  
}

fun main() {
	A().foo()
    B().foo()
}

 

정답을 생각해보고 다음의 더보기를 펼쳐주세요.

더보기

정답은 A 와 B 객체 모두 10을 출력합니다. 

여기서 또하나의 특징은 오버라이드(override)할때, 기본값을 재할당 할 수 없다는 특징입니다. 

만약 오버라이드하면서 기본값을 재할당하는 코드를 작성할 경우, 아래와 같이 에러를 만나게됩니다. 

An overriding function is not allowed to specify default values for its parameters

역으로 위의 에러가 발생하였다고 한다면 override 키워드를 검색하여 기본값을 실수로 재정의하였는지를 찾아야합니다. 

open class A {
    open fun foo(i: Int = 10) { println(i) }
}

class B : A() {
    // 오버라이드하면서 기본인자의 값을 재할당하는 것을 코틀린에서 허용하지 않습니다. 
    override fun foo(i: Int = 20) { println(i) } 
    // 에러 발생: An overriding function is not allowed to specify 
    // default values for its parameters
}

코틀린 이름 지정 인자(Named Arguments)

코틀린에서 제공하는 이름지정 인자(Named Arguments) 개념은 함수를 더욱 유연하게 사용할 수 있도록 합니다. 

이름 지정 인자(Named Arguments)란 함수 호출 시 파라미터의 이름을 명시적으로 지정하여 인자를 전달하도록 합니다. 

이를 통해 "1) 파라미터의 순서를 변경"하거나"2) 특정 파라미터만 선택적으로 전달"할 수 있습니다.

 

예를 들어, 다양한 문자열 출력 옵션을 제공하는 Reformat 함수가 있다고 가정하겠습니다. 

때때로, 모든 문자를 대문자로 치환하거나 단어들의 구분자를 공백문자 대신 탭문자로 지정할 필요가 있다고 가정하겠습니다. 

fun reformat(
    str: String,
    normalizeCase: Boolean = true,
    upperCaseFirstLetter: Boolean = true,
    divideByCamelHumps: Boolean = false,
    wordSeparator: Char = ' ',
) {
	println("str: $str")
    println("normalizeCase: $normalizeCase")
    println("upperCaseFirstLetter: $upperCaseFirstLetter")
    println("divideByCamelHumps: $divideByCamelHumps")
    println("wordSeparator: '$wordSeparator'")
}

fun main() {
	// 필요한 파라미터만 명시적으로 인자를 전달할 수 있습니다. 
	reformat(
        "String!",
        false,
        upperCaseFirstLetter = false,
        divideByCamelHumps = true,
        '_'
	)
    // 기본인자가 제공되어 있으므로 필수인자만 전달하는 호출도 가능합니다. 
    reformat("This is a long String!")
}

 

면접 질문 예상 ➡️ Java와 코틀린의 차이점
JVM 상에서 Java 함수 호출 시 이름지정 인자(named argument) 문법을 사용할 수 없습니다. 
Java 바이트 코드(Byte Code)는 함수 매개변수의 이름을 항상 유지하지 않기 때문입니다. 

 

이번 시간에 배운 개념들을 종합한 코드입니다.

수정하면서 배운 개념들을 연습해봅니다. 

 

 

이상입니다.

K-IN 올림. 

 

반응형