[Kotlin Tutorial] 함수 정의하고 호출하기 #1 - Chap 3. Defining and calling functions |
참조 : Kotlin in Action
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”)
'프로그래밍 놀이터 > Kotlin, Coroutine' 카테고리의 다른 글
[Kotlin Tutorial] 클래스, objects, 그리고 인터페이스 #1 - Chap4. Classes, objects, and interfaces (0) | 2017.08.11 |
---|---|
[Kotlin Tutorial] 함수 정의하고 호출하기 #2 (0) | 2017.08.03 |
[Kotlin Tutorial] Kotlin 기초 #2 - Chap2. Kotlin basics (0) | 2017.07.25 |
[Kotlin Tutorial] Kotlin 기초 #1 - Chap2. Kotlin basics (0) | 2017.07.25 |
[android] Kotlin (코틀린) 이 뭐야? (0) | 2017.07.25 |
댓글