코틀린 함수 정의는 fun으로 함
fun double(x: Int): Int {
return 2 * x
}
// 사용은 요렇게
val result = double(2)
Stream().read() // 클래스 생성 후 사용
override할 때 default value 없애는것도 가능
open class A {
open fun foo(i: Int = 10) { /*...*/ }
}
class B : A() {
override fun foo(i: Int) { /*...*/ } // No default value is allowed.
}
//named argument로 baz 사용
fun foo(
bar: Int = 0,
baz: Int,
) { /*...*/ }
foo(baz = 1) // The default value bar = 0 is used
// default parameter뒤에 마지막 변수가 람다면 named argument 혹은 괄호를 통해 마지막 변수를 전달함
fun foo(
bar: Int = 0,
baz: Int = 1,
qux: () -> Unit,
) { /*...*/ }
foo(1) { println("hello") } // baz = 1, 괄호 밖
foo(qux = { println("hello") }) // bar = 0 and baz = 1
foo { println("hello") } // bar = 0 and baz = 1, qux는 괄호 밖
// 정의
fun reformat(
str: String,
normalizeCase: Boolean = true,
upperCaseFirstLetter: Boolean = true,
divideByCamelHumps: Boolean = false,
wordSeparator: Char = ' ',
) { /*...*/ }
// 사용
reformat(
"String!",
false,
upperCaseFirstLetter = false,
divideByCamelHumps = true,
'_'
)
// default 값을 모두 사용한 경우
reformat("This is a long String!")
// 특정 값들만 default 값을 사용한 경우
reformat("This is a short String!", upperCaseFirstLetter = false, wordSeparator = '_')
// spread(*) 를 사용해서 vararg 변수 넘기기(이름 사용)
fun foo(vararg strings: String) { /*...*/ }
foo(strings = *arrayOf("a", "b", "c"))
JVM에서 함수가 실행되면 Named argument를 사용 못할 수 있음. 자바 bytecode가 함수 parameters의 이름을 항상 보존하지만은 않기 때문에~~
함수가 유용한 값을 반환하지 않을 때 반환 타입은 Unit
임. 그리고
Unit
일 경우 반환타입을 정의 하지 않아도 됨
fun printHello(name: String?): Unit {
if (name != null)
println("Hello $name")
else
println("Hi there!")
// 명시적으로 무언가를 반환하지 않아도 됨.
}
// return type Unit
fun printHello(name: String?) { ... }
// 이렇게 함수를 정의할 수도 있음
// 이 경우에는 컴파일러가 타입을 유추할 수 있어서 리턴타입을 명시적으로 정의하지 않아도 됨.
fun double(x: Int): Int = x * 2
Unit
일 경우를 제외하고는 명시적으로 리턴타입이 있어야함.
싱글이 아닌경우 코드블록에서 어떤짓을 할지 몰라서 컴파일러가 반환타입을 유추헐 수가 없음 그래서 명시적으로 적어줘야함.
보통 함수의 마지막 변수로 vararg
수정자를 사용 변수를 정의함.
// 정의
fun <T> asList(vararg ts: T): List<T> {
val result = ArrayList<T>()
for (t in ts) // ts is an Array
result.add(t)
return result
}
// 사용
val list = asList(1, 2, 3)
한 함수에서 오직 한개의 파라미터만 vararg
가 될 수 있음.
vararg
가 마지막 변수가 아니면, 뒤에 오는 변수들은 이름을 붙여서 넘겨줘야함.
함수타입이면 괄호 밖에 람다로 전달 할 수 있음.
vararg
함수를 사용할 때, 이미 가지고있는 array를 사용하고 싶을 때 앞에 *(spread
) 를 붙이면됨.
원시타입 array를 넘기고 싶으면 toTypedArray()
를 사용하면 됨
// array 사용
val a = arrayOf(1, 2, 3)
val list = asList(-1, 0, *a, 4)
// primative 사용
val a = intArrayOf(1, 2, 3) // IntArray is a primitive type array
val list = asList(-1, 0, *a.toTypedArray(), 4)
infix
키워드 사용법
제한 사항
infix fun Int.shl(x: Int): Int { ... }
// 표기법 1
1 shl 2
// 표기법 2
1.shl(2)
아래 묶인 예시들은 같은 의미임 infix
함수는 산술 기호, 타입 캐스팅, rangeTo 보다 더 우선순위가 낮음.
1 shl 2 + 3
== 1 shl (2 + 3)
0 until n * 2
== 0 until (n * 2)
xs union ys as Set<*>
== xs union (ys as Set<*>)
아래예시는 operators && and ||, is- and in-checks는 infix
함수보다 우선순위가 낮음
a && b xor c
== a && (b xor c)
a xor b in c
== (a xor b) in c
+,-,rnageTo,as >
infix
> &&, ||, is, in
infix
함수는 receiver랑 변수 둘 다 필요함.
현재 receiver에서 infix 표기법을 사용해 메소드를 호출할 때 this를 명시적으로 사용해야함.
class MyStringCollection {
infix fun add(s: String) { /*...*/ }
fun build() {
this add "abc" // Correct
add("abc") // Correct
//add "abc" // Incorrect: the receiver must be specified
}
}
코틀림 함수는 파일의 탑레벨에 선언될 수 있음. (= 함수를 가진 클래스를 만들 필요가 없다. Java, C#, Scala(3부터는 된다고함) 처럼)
그 외에도 멤버 함수나 확장함수도 locally하게 선언할 수 있음.
// 함수 내부에 함수
// 내부 함수는 바깥 함수에 정의된 로컬 변수에만 접근 가능
fun dfs(graph: Graph) {
//
fun dfs(current: Vertex, visited: MutableSet<Vertex>) {
if (!visited.add(current)) return
for (v in current.neighbors)
dfs(v, visited)
}
dfs(graph.vertices[0], HashSet())
}
// 위 함수랑 동일 visited가 로컬 변수로 사용한거
fun dfs(graph: Graph) {
val visited = HashSet<Vertex>()
fun dfs(current: Vertex) {
if (!visited.add(current)) return
for (v in current.neighbors)
dfs(v)
}
dfs(graph.vertices[0])
}
클래스나 오브젝트 내무에 정의되는 멤버 함수 .을 통해 호출됨
코틀린은 꼬리 재귀라고 불려지는 함수형 프로그래밍 스타일을 지원하고있음.
일반적인 반복문을 사용하는 일부 알고리즘을 짤 때, 스택 오버플로우의 리스크 없이 재귀함수를 구현할 수 있게해줌. tailrec이라는 수정자를 함수 앞에 붙여주면 됨
val eps = 1E-10 // "good enough", could be 10^-15
//
tailrec fun findFixPoint(x: Double = 1.0): Double =
if (Math.abs(x - Math.cos(x)) < eps) x else findFixPoint(Math.cos(x))
위 코드는 수학적 상수인 코사인의 fixPoint를 찾는 코드.
결과가 더 이상 변하지 않을 때까지 1.0부터 Math.cos를 반복적으로 호출해서 대해 0.7390851332151611라는 결과를 지정된 eps 정밀도에 사용하면 됩니다.
아래 전통 스타일로 해도 결과 코드는 동일... 하다고,,,
val eps = 1E-10 // "good enough", could be 10^-15
private fun findFixPoint(): Double {
var x = 1.0
while (true) {
val y = Math.cos(x)
if (Math.abs(x - y) < eps) return x
x = Math.cos(x)
}
}
tailrec
수정자를 사용하려면 함수가 자기자신을 호출하는게 마지막이어야함.
재귀 호출 이후에 더많은 코드가 있으거나, try/catch/finally
코드 블락 내에서나 open functions에서는 꼬리 재귀를 사용할 수 없음. 현재는 JVM과 Kotlin,Native에서 사용할 수 있음.