본문 바로가기
프로그래밍 놀이터/Kotlin, Coroutine

[Kotlin Tutorial] 함수 정의하고 호출하기 #1 - Chap 3. Defining and calling functions

by 돼지왕 왕돼지 2017. 8. 3.
반응형

 [Kotlin Tutorial] 함수 정의하고 호출하기 #1 - Chap 3. Defining and calling functions


참조 : Kotlin in Action

@file:JvmName, @file:JvmName(“StringFunctions”), @Jvm-Overloads annotation, annotation, arraylistof, Calling extension functions from Java, collection, compile time 적용, const, const keyword, const 키워드, Creating collections in Kotlin, Default parameter values, Defining and calling functions, duplicate function name, encapsulation, extension function, extension functions, Extension functions and properties, Extension properties, extension property, full qualified function call, getter, getter 필수, Getting rid of static utility classes: top-lvel functions and properties, hashsetof, IDE, import, import rename, imports, infix, infix function, java collection, java default value, java kotlin, java top level function, java 호환, joinKt, jointostring, jvmname, kotlin collection, kotlin static final, listof, Making functions easier to call, named arguments, No overriding for extension functions, overload 함수 생성, override, package, Private, private member, Protected, protected member, public 접근, receiver object, receiver type, Refactor, runtime overhead, runtime 적용 아님, setof, Setter, static method, static utility class, static variable, static 함수, StringBuilder, This, to, top level function, top level properties, top level property, top-level val, top-level var, typeof, Utility functions as extensions, [Kotlin Tutorial] 함수 정의하고 호출하기 #1 - Chap 3. Defining and calling functions, 기본 값, 단점, 실제 property, 함수 정의하고 호출하기


3.1. Creating collections in Kotlin


-

아래와 같이 쉽게 collection 을 만들 수 있다.

"type”Of 의 form 이다.

val hashSet = hashSetOf(1, 7, 53) // mutable

val set = setOf(“Gamza”, “Goguma”) // immutable


val list = arrayListOf(1, 7, 53) // mutable


val map = hashMapOf(1 to “one”, 7 to “seven”, 53 to “fifty-three”) // mutable

// to 는 normal function 이다 이는 나중에 다룬다, infix function 이라 부른다



-

Kotlin 은 자체 collection 이 없다. 다 Java 의 녀석을 사용한다.

그치만 extension 의 형태로 더 많은 기능들을 사용할 수 있다.

val strings = listOf(“first”, “second”, “fourteenth”)

println(strings.last())


val numbers = setOf(1, 14, 2)

println(numbers.max()) // extension function




3.2. Making functions easier to call


-

val list = listOf(1,2,3)

println(list) // [1, 2, 3]

println(joinToString(list, “; “, “(“, “)”)) // (1; 2; 3)

joinToString 은 built-in function이며 내부는 StringBuilder 로 구현되어 있다.




3.2.1. Named arguments


-

joinToString(list, “;”, “(“, “)”) 

위의 코드 readability 가 꽝이다!!


그래서 Java 에서는 이렇게 쓰곤 하지

joinToString(list, /*separator*/ “;”, /*prefix*/“(“, /*postfix*/“)”)


그러나 kotlin 은 이렇게(더 좋게) 할 수 있지

joinToString(list, separator = “;”, prefix = “(“, postfix=“)” )


단점(?)은 한번 argument name 을 쓰기 시작하면 그 이후 녀석들 모두 써줘야한다.

또 다른 단점은 Java 로 작성된 함수를 호출할 때는 이 녀석을 쓸 수 없다… ( Java8 은 이 기능이 있지만 Kotlin 은 Java6 부터 지원.. )


tip) IDE 가 해당 arg name 을 함께 rename 해주기 때문에 refactor 등을 잘 이용하자.




3.2.2. Default parameter values


-

Kotlin 에서는 default value 를 사용하므로써 overload 를 많이 막을 수 있다.

func <T> joinToString( collection: Collection<T>, separator: String = “, “, prefix: String = “”, postFix: String = “”) : String


단점(?) 은 trailing argument 만 생략할 수 있다는 것.

즉 위의 예에서 prefix 는 값 default 가 아닌 것으로 쓰면서 separator 는 자동으로 default 를 쓰도록 할 수 없다.

( named argument 를 사용하지 않는 일반적 호출의 경우 )


그치만 또 다른 방법은 있다!

named argument 를 사용하면 중간 녀석을 생략할 수 있다.

심지어 argument 순서도 변경할 수 있다 ( 짱짱! )

joinToString(list, suffix = “l”, prefix = “# “)



-

Kotlin  에서 default value 를 지정한 함수를 java 에서 사용하려면 @Jvm-Overloads annotation 을 붙여주면 된다.

그럼 자동으로 overload 함수들을 생성해준다

String joinToString(Collection<T> collection, String separator, String prefix, String postfix);


String joinToString(Collection<T> collection, String separator, String prefix);


String joinToString(Collection<T> collection, String separator);


String joinToString(Collection<T> collection);






3.2.3. Getting rid of static utility classes: top-lvel functions and properties


-

가끔 비슷한 역할을 하는 function 을 한 class 에 두기는 그래서 static function 만 갖는 class 를 두어서 그 녀석을 호출하게 하는 경우가 있다.

그 대표적 예는 Collections class. ( swap, sort, binary search 등 util 성 함수들이 모여있다. )


Kotlin 에서는 이런 녀석들을 특정 package 에 속하게는 하면서 class 는 만들지 않도록 할 수 있다.


예를 들면 join.kt 라는 파일을 만들고 그 안에 아래 코드를 넣는다.

함수 정의를 top-level 로 위치시킨다 ( class 바깥 )

package strings

fun joinToString(…): String { … } 


이 경우 Java 에서는 JoinKt.joinToString 으로 호출할 수 있고, Kotlin 에서는 그냥 joinToString 을 호출해주면 된다.


JoinKt 가 어글리하다면 @file:JvmName(“StringFunctions”) 를 써주면 된다.

그럼 자바에서 StringFunctions.joinToString 으로 호출할 수 있다.



-

function 과 마찬가지로 top-level 에 var 도 가질 수 있다.

이 경우 static variable 로 작동한다


val 의 경우는 당연히 static final 로 작동한다.


이 녀석들은 자동으로 getter/setter 들이 제공된다. ( val 은 물론 getter 만 )

val 을 getter 없이 const 로만 제공하려면 const 키워드를 넣어주면 된다.

const val UNIX_LINE_SEPARATOR = “\n”




3.3. Adding Methods to other people’s classes : Extension functions and properties


-

Extension function 이란 해당 class 를 통해 부를 수 있지만 해당 class 밖에 선언된 함수를 얘기한다.


package strings


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


해야 할 일은 extending 하고 싶은 class 나 interface 이름을 함수 이름 앞에 넣어주기만 하면 된다.

이 class 이름을 receiver type 라고 한다. ( 예제에서는 String )

그리고 함수 정의에 있는 this 는 receiver object 라 해서 receiver type instance 를 가르킨다

여기서 this 를 빼도 된다



-

이 Extension function 은 해당 class 의 소스코드가 없어도 할 수 있다.


단!!! 이 녀석은 private 이나 protected member 에는 접근 불가능하다. public 만 접근 가능하다

( encapsulation 을 깨면 안 되기에 )




3.3.1. Imports and extension functions


-

Extension function 을 정의한다고 자동으로 모든 프로젝트에서 쓸 수 있는 것은 아니다.

import 를 해주어야 한다.

import strings.lastChar // 혹은 import strings.*


val c = “Kotlin”.lastChar()


import 할 때 함수 이름도 바꿀 수 있다.

이 녀석은 왜 필요하냐? 다른 pakcage 에 정의된 duplicate 함수 이름일 때 대응하기 위해서다.

import strings.lastChar as last


val c = “Kotlin”.last()


물론 import 대신 full qualified function call 을 할 수도 있다.




3.3.2. Calling extension functions from Java


-

extension function 은 receiver object 를 첫번째 arg 로 받는 static method 이다.

그렇기 때문에 다른 어떤 runtime overhead 를 만들지는 않는다


/* Java */

// lastChar 가 StringUtils.kt 에 정의되어 있다면

char c = StringUtilsKt.lastChar("Java"); // Receiver 가 first argument 로 가야 한다는 것 꼭 기억!




3.3.3. Utility functions as extensions


-

Collections<T>.joinToString(…){…} 을 하면 일반적인 collection 에서 사용할 수 있고,

Collections<String>.join(…){ joinToString(…) } 로 String collection 전용으로 함수를 뚫을 수도 있다.




3.3.4. No overriding for extension functions


-

Extension function 은 override 할 수 없다.

override 로서 작동하지 않는다는 의미이다.


예를 들어 View 라는 녀석에도 a 라는 function 을, View 를 상속한 Button 에도 a 라는 function 을 extension 할 수는 있다.

그러나 override 로 작동하지 않고 정의된 static type 의 녀석이 호출된다. ( runtime 이 아닌 compile time 에 적용된다. )

( !! 개념 확실히 하는 것이 중요 !! )


fun View.a() = println(“I’m view!”)

fun Button.a() = println(“I’m button!”) // Button extends View


val view: View = Button()

view.a

// 결과는 I’m view!

이 로직을 헛갈리지 않으려면 extension 이 사실 static function 이고 첫번째 인자로 receiver object 를 받는다는 것을 기억하면 된다.


순서에 대한 감각을 익히려면 아래 코드를 만지면서 놀아보세요.

fun AView.a() = println("I’m an AView!")

fun BView.a() = println("I’m a BView!")


fun main(args: Array<String>) {

    val view:AView = BView()

println(view.a())

}


open class AView{

    open fun a() = println("I'm a AView desu")

}


class BView: AView(){

    override fun a() = println("I'm a BView desu")

}

( 순서는 member -> 명시적 type의 extension 이라고 보면 된다. member 가 override 되었다면 override 된 녀석이 불린다 )



-

그럼 member 변수와 signature 가 같으면 어떻게 되느냐?

member function 이 항상 우선순위를 갖는다. ( !!!!! 이것도 주의 !!!! )




3.3.5. Extension properties


-

이름은 properties 지만 실제 property 를 추가하는 것은 아니다. property 쓰듯이 쓸 수 있는 형태만 지원하는 것이다.

실제 property 추가가 아니기 때문에 getter 정의는 필수이다.


var String.lastChar: char

    get() = get(length - 1)

    set(value:Char){

        this.setCharAt(length-1, value)

    }


위와 같이 setter 도 정의할 수 있다.

저렇게 함으로 property 처럼 접근할 수 있다.

println(“Kotlin”.lastChar)



-

Java 에서 extension property 를 사용할 때에는 마찬가지로 아래와 static 함수 사용하듯 사용해야 한다.

StringUtilKt.getLastChar(“Kotlin”)





반응형

댓글