Chapter3. 함수 정의와 호출

김신영·2022년 9월 28일
0

kotlin-in-action

목록 보기
3/11
post-thumbnail

Kotlin에서 Collection 객체 만들기

  • setOf(vararg elements: T): Set<T>

  • hashSetOf(vararg elements: T): HashSet<T>

  • linkedSetOf(vararg elements: T): LinkedHashSet<T>

  • listOf(vararg elements: T): List<T>

  • arrayListOf(vararg elements: T): ArrayList<T>

  • mapOf(vararg pairs: Pair<K, V>): Map<K, V>

  • hashMapOf(vararg pairs: Pair<K, V>): HashMap<K, V>

  • linkedMapOf(vararg pairs: Pair<K, V>): LinkedHashMap<K, V>

val list = listOf(1,2,3)  
val arrayList = arrayListOf(1,2,3)  

println("${list}, ${list.javaClass}")  
println("${arrayList}, ${arrayList.javaClass}")

val set = setOf(3,1,2)  
val hashSet = hashSetOf(1,2,3)  
val linkedSet = linkedSetOf(3,2,1)  

println("${set}, ${set.javaClass}")  
println("${hashSet}, ${hashSet.javaClass}")  
println("${linkedSet}, ${linkedSet.javaClass}")

val map = mapOf(3 to 4, 1 to 2, 2 to 3,)  
val hashMap =  hashMapOf(1 to 2, 2 to 3, 3 to 4)  
val linkedHashMap = linkedMapOf(3 to  4, 1 to 2, 2 to 3)  

println("${map}, ${map.javaClass}")  
println("${hashMap}, ${hashMap.javaClass}")  
println("${linkedHashMap}, ${linkedHashMap.javaClass}")
[1, 2, 3], class java.util.Arrays$ArrayList
[1, 2, 3], class java.util.ArrayList

[3, 1, 2], class java.util.LinkedHashSet
[1, 2, 3], class java.util.HashSet
[3, 2, 1], class java.util.LinkedHashSet

{3=4, 1=2, 2=3}, class java.util.LinkedHashMap
{1=2, 2=3, 3=4}, class java.util.HashMap
{3=4, 1=2, 2=3}, class java.util.LinkedHashMap

Named Argument (Named Parameter)

fun <T> joinToString(  
    collection: Collection<T>, separator: String = " ", prefix: String = "[", postFix: String = "]"  
): String {  
    val result = StringBuilder(prefix)  
  
    for ((index, element) in collection.withIndex()) {  
        if (index > 0) {  
            result.append(separator)  
        }  
  
        result.append(element)  
    }  
  
    result.append(postFix)  
    return result.toString()  
}

fun main() {  
    val list = listOf(1, 2, 3)  
    println(joinToString(list, separator = ", ", prefix = "{", postFix = "}"))  
}

// {1, 2, 3}

Default Parameter

  • Java에서 Kotlin 함수 호출시, Default Parameter 기능이 적용되지 않는다.
  • Java에서 Default Parameter가 선언된 Kotlin 함수를 편하게 호출하기 위해서, @JvmOverloads 를 활용한다.

NOTE:
Kotlin 함수를 Java에서 호출하는 경우에는 디폴트 파라미터 값을 선언했더라도, 모든 인자를 명시하고 호출해야한다.

@JvmOverloads

  • Before use @JvmOverloads
class CustomView : View { 
	constructor(context: Context) : super(context) 
	constructor(context: Context, attrs: AttributeSet) : super(context, attrs)
	constructor(context: Context, attrs: AttributeSet, defStyleAttr: Int) : super(context, attrs, defStyleAttr)

	// ...
}
  • Example - @JvmOverloads
class CustomView @JvmOverloads 
constructor(context: Context, attrs: AttributeSet? = null, defStyleAttr: Int = 0)
: View(context, attrs, defStyleAttr) {
	// ...
}

Python *args , **kwargs

  • *args receives arguments as an array
  • **kwargs receives arguments as a dictionary
def myFun(*args, **kwargs):
	print(f"args: {args}")
	print(f"kwargs: {kwargs}")


# Now we can use both *args ,**kwargs`
# to pass arguments to this function :`

myFun('geeks', 'for', 'geeks', first='Geeks',  mid='for', last='Geeks')

# OUTPUT
# args: ('geeks', 'for', 'geeks')
# kwargs: {'first': 'Geeks', 'mid': 'for', 'last': 'Geeks'}

최상위 함수

  • Kotlin 컴파일러가 생성하는 클래스의 이름은 최상위 함수가 들어있던 Kotlin 소스 파일의 이름과 대응한다.
  • 코틀린 최상위 함수가 포함되는 클래스의 이름을 바꾸고 싶다면, 파일 맨 윗줄 package 선언문 보다 위에 @JvmName 어노테이션을 추가하면 된다.
  • @JvmName 어노테이션은 패키지 이름 선언 이전에 위치해야 한다.
@file:JvmName("StringFunctions")  
package string  
  
@JvmOverloads  
fun <T> joinToString(  
    collection: Collection<T>, separator: String = " ", prefix: String = "[", postFix: String = "]"  
): String {  
    val result = StringBuilder(prefix)  
  
    for ((index, element) in collection.withIndex()) {  
        if (index > 0) {  
            result.append(separator)  
        }  
  
        result.append(element)  
    }  
  
    result.append(postFix)  
    return result.toString()  
}
public static void main(String[] args) {  
    System.out.println(StringFunctions.joinToString(List.of(1,2,3)));  
	System.out.println(StringFunctions.joinToString(List.of(1,2,3), ","));  
	System.out.println(StringFunctions.joinToString(List.of(1,2,3), ",", "["));  
	System.out.println(StringFunctions.joinToString(List.of(1,2,3), ",", "[", "]")); 
}

// [1 2 3]
// [1,2,3]
// [1,2,3]
// [1,2,3]

최상위 프로퍼티

  • Kotlin
// File 명: String.kt
val UNIX_LINE_SEPERATOR = "\n"
  • Java
public String getUNIX_LINE_SEPERATOR() {
	return "\n";
}
  • Java Execution Example
public static void main(String[] args) {  
    System.out.println(StringKt.getUNIX_LINE_SEPERATOR());  
}

const 를 사용할 경우, Java에서 static final 정적 변수로 전환된다.

  • Kotlin
const val UNIX_LINE_SEPERATOR = "\n"
  • Java
public static final String UNIX_LINE_SEPERATOR = "\n";
  • Java Execution Example
public static void main(String[] args) {  
    System.out.println(StringKt.UNIX_LINE_SEPERATOR);  
}

확장 함수

  1. 확장 함수 내부에서는 수신 객체의 메소드나 프로퍼티를 사용할 수 있다.
  2. 하지만 확장 함수 내부에서는 private, protected 멤버를 사용할 수 없다.
    (Encapsulation 유지를 위함.)
  3. 어떤 클래스의 확장 함수를 사용하기 위해서는, 반드시 임포트 해야한다.
  4. import xxx.xxx as yyy import... as 를 통해서 다른 이름으로 부를 수 있다.
  5. 확장함수와 클래스의 멤버 함수의 이름과 시그니처가 같다면, 멤버 함수가 호출된다.
    (멤버 함수가 우선순위가 높다.)
@file:JvmName("StringFunctions")
package string

fun <T> Collection<T>.join(  
    separator: String = " ", prefix: String = "[", postFix: String = "]"  
): String {  
    val result = StringBuilder(prefix)  
  
    for ((index, element) in withIndex()) {  
        if (index > 0) {  
            result.append(separator)  
        }  
  
        result.append(element)  
    }  
  
    result.append(postFix)  
    return result.toString()  
}
import string.join as merge  // 클래스의 확장 함수를 사용하기 위해서는 반드시 임포트 해야 한다.

fun main() {  
    println(listOf(1, 2, 3).merge(",", "[", "]"))  
}

// [1,2,3]
  • Java에서는 수신 객체를 첫 번째 인자로 받는 정적 메소드이다.
public static void main(String[] args) {  
    System.out.println(StringFunctions.join(List.of(1,2,3), ",", "[", "]"));  
}
  1. 확장 함수는 오버라이드할 수 없다.
    • Instance Method 의 경우, 동적(Runtime 시점)으로 호출될 메서드를 결정한다. (Dynamic Dispatch)
    • 확장 함수의 경우, 정적(Complie 시점)으로 호출된 메서드를 결정한다. (Static Dispatch)
open class View {  
    open fun click() = println("View clicked")  
}  
  
class Button: View() {  
    override fun click() = println("Button clicked")  
}

fun main() {  
    val view: View = Button()  
    view.click()  // instance method 호출
}

// Button clicked
fun View.showOff() = println("View showOff")  
fun Button.showOff() = println("Button showOff")  
  
fun main() {  
    val view: View = Button()  
    view.showOff()  // 확장 함수 호출
}

// View showOff

확장 프로퍼티

  • 확장 프로퍼티의 경우, backing field를 추가적으로 클래스에 선언할 수 없으므로

  • Getter를 반드시 정의해야한다.

  • 변경 가능한 확장 프로퍼티

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

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

가변 인자 함수 vararg

vararg

fun main(args: Array<String>) {  
    println(customList(listOf(2, 3, 4)))  // 10
    println(customVarg(1, 2, 3))          // 7  
}  
  
  
fun customList(list: List<Int>) = customVarg(*list.toIntArray())  
  
fun customVarg(vararg args: Int) = customVararg2(1, *args)  
  
fun customVararg2(vararg args: Int) = args.sum()

Spread Operator *

  • 배열에 들어있는 값들과 다른 여러 값을 함께 인자로 전달해서 함수를 호출할 수 있다.
  • Array<T>, IntArray, LongArray, DoubleArray. ByteArray, CharArray, ShortArray FloatArray, StringArray, BooleanArray
  • 위와 같이 Array 에만 사용 가능
  • List 의 경우 toArray 메서드 적용 후, 사용 가능
fun main(args: Array<String>) {
	val list = listOf("args", *args)
	println(list)
}

중위 호출 (infix call) to

val map = mapOf(1 to "one", 7 to "seven", 53 to "fifty-three")
1.to("one")  // to 메서드를 일반적인 방식으로 호출
1 to "one"   // to 메서드를 중위 호출 방식으로 호출
infix fun Any.to(other: Any) = Pair(this, other)  // 예시 코드

infix fun <A, B> A.to(that: B): Pair<A, B> = Pair(this, that)  //  실제 코드

구조 분해 선언 (Destructuring Declaration)

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

val (a, b, c) = listOf(1,2,3)

![[Pasted image 20221010191658.png]]

문자열과 정규식

  • `fun String.substringBeforeLast()
fun parsePath(path: String) : List<String>{  
    val directory = path.substringBeforeLast("/")  
    val fullName = path.substringAfterLast("/")  
  
    val fileName = fullName.substringBeforeLast(".")  
    val extension = fullName.substringAfterLast(".")  
  
    return listOf(directory, fileName, extension)  
}  
  
fun main(args: Array<String>) {  
    val (directory, fileName, extension) = parsePath("/Users/yole/kotlin-book/chapter.adoc")  
    println("Dir: $directory, name: $fileName, ext: $extension")
    // Dir: /Users/yole/kotlin-book, name: chapter, ext: adoc
}
  • """ 삼중 따움표로 묶은 문자열 내부에는 어떤 문자도 이스케이프할 필요가 없다.
  • """ 삼중 따움표 문자열에는 줄 바꿈을 표현하는
fun parsePath(path: String) : List<String> {  
    val regex = """(.+)/(.+)\.(.+)""".toRegex()  
    val matchResult = regex.matchEntire(path)  
    if (matchResult != null) {  
        return matchResult.destructured.toList()  
    }  
  
    return emptyList()  
}  
  
fun main(args: Array<String>) {  
    val (directory, filename, extension) = parsePath("/Users/yole/kotlin-book/chapter.adoc")  
    println("Dir: $directory, name: $filename, ext: $extension")
    // Dir: /Users/yole/kotlin-book, name: chapter, ext: adoc
}
  • trim()
  • trimMargin(marginPrefix: String)
  • trimIndent()
val kotlinLogo = """  
                   .| //                   
                   .|//                   
                   .|/ \                   
                   """  
fun main(args: Array<String>) {  
    println(kotlinLogo.trimMargin("."))  
    println()  
    println(kotlinLogo.trimIndent())  
    println("========")  
}
  • 출력 결과
| //
|//
|/ \

.| //
.|//
.|/ \
========

Local 함수

class User(val id: Int, val name: String, val address: String)  
  
fun saveUser(user: User) {  
    if (user.name.isEmpty()) {  
        throw IllegalArgumentException(  
            "Can't save user ${user.id}: empty Name")  
    }  
  
    if (user.address.isEmpty()) {  
        throw IllegalArgumentException(  
            "Can't save user ${user.id}: empty Address")  
    }  
  
    // Save user to the database  
}
  • Local함수는 자신이 속한 바깥 함수의 모든 파라미터와 변수를 사용할 수 있다.
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(  
                "Can't save user ${user.id}: " +  
                    "empty $fieldName")  
        }  
    }  
  
    validate(user.name, "Name")  
    validate(user.address, "Address")  
  
    // Save user to the database  
}
  • 확장함수는 공개 프로퍼티나 메소드를 사용할 수 있다
  • 확장함수 내부 Local 함수 또한 공개 프로퍼티나 메소드를 사용할 수 있다.
class User(val id: Int, val name: String, val address: String)  
  
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()  
  
    // Save user to the database  
}
profile
Hello velog!

0개의 댓글