인프런에서 제공하고 있는 강의를 보고 정리한 글입니다.
확장 함수
어떤 클래스 안에 있는 메소드처럼 호출할 수 있지만, 함수는 밖에서 선언할 수 있도록 제공한다.
fun main() {
val str = "ABC"
str.lastChar() // 원래 멤버함수에 있는 것처럼 사용한다.
}
// 확장 함수. String 을 확장한다
fun String.lastChar() {
return this[this.length-1] // 불려진 instance 에 접근이 가능함.
}
- 확장함수는
public
인데, 확장 함수에서 수신객체 클래스의 private 함수를 가져오면 깨지는 것이 아니다. 애초에private
protected
를 가져올 수 없다. - 확장함수와 멤버함수의 시그니처가 동일하면, 멤버함수가 호출된다. 확장함수를 만들었지만, 다른 기능의 똑같은 멤버함수가 생기면, 오류가 발생할 수 있으니, 주의해야한다.
- 확장 함수가 override 되면, 해당 변수의 현재 타입. 정적인 타입에 의해 어떤 함수가 호출될지 결정된다. 다음과 같이 말이다.
val train: Train = Train()
train.isExpansive() // Train 의 확장함수
val str1: Train = Srt()
str1.isExpansive() // Train 의 확장함수
val str2 : Srt = Srt()
str2.isExpansive() // srt 의 확장함수
- Java 에서 Kotlin 확장함수 호출이 가능하다. 정적 메소드를 부르는 것처럼 사용이 가능하다.
- 확장 함수의 개념은 확장 프로퍼티와도 연결된다.
fun String.lastChar() : Char {
return this[this.length - 1]
}
// 확장 프로퍼티의 원리는 확장함수 + custom getter 와 동일하다
val String.lastChar : Char
get() = this[this.length - 1]
정리하자면, 확장 함수의 원본 클래스의 private, protected 멤버 접근이 되지 않고, 멤버 함수, 확장 함수 중 멤버 함수에 우선권이 있으며, 확장 함수는 현재 타입을 기준으로 호출된다.
Infix 함수
중위함수. 함수를 호출하는 새로운 방법이다. 앞전에서 서술했던, downTo
, Step
도 중위호출함수이다.
변수.함수이름(argument) 대신에, 변수 함수이름 argument 이렇게 호출하는 방식이다.
inline fun Int.add(other: Int) : Int {
return this + other
}
지역함수
함수 안에 함수를 선언하는 것이다. 함수를 호출하면 좋을 것 같은데, 이 함수를 현재 함수 내에서만 쓰고 싶을 때 사용한다. 하지만 depth 가 깊어지기도 하고 코드가 그렇게 깔끔하지는 않아서, 실무에서 자주 쓰이지는 않는다.
fun createPerson(firstName: String, lastName: String) : Person {
fun validName(name: String, fieldName: String) {
if (name.isEmpty()) {
throw IllegalArgumentException("error")
}
}
validateName(firstName, "firstName")
validateName(lastName, "lastName")
}
Functional Programming
코틀린에서는 기본적으로 함수 그 자체가 값이 될 수 있고, 변수에 할당되거나 파라미터로 넘기는 것이 가능하다는 점이 자바와 다른 점이다. 자바의 경우, 함수를 넘겨주는 것 “처럼” 보여주고 있기 때문이다.
다음 예제들이 있으며, 다음과 같이 사용한다.
fun main() {
val fruits = listOf(
Fruit("사과", 1_000),
Fruit("바나나", 1_000),
Fruit("바나나", 3_000),
Fruit("수박", 2_000),
Fruit("수박", 2_000),
Fruit("사과", 1_000),
)
val isApple = fun(fruit: Fruit): Boolean {
return fruit.name == "사과"
}
val isApple2 = { fruit: Fruit -> fruit.name == "사과" }
// 위와 마찬가지로 똑같이 Boolean 값을 반환한다. 함수의 타입 : (파라미터 타입.. 여러개) -> 반환타입 형식으로도 선언이 가능하다
val isApple2 : (Fruit) -> Boolean = { fruit: Fruit -> fruit.name == "사과" }
// 함수 호출 방법
isApple(fruits[0])
isApple.invoke(fruits[0])
// filterFruit 호출
filterFruits(fruits, isApple)
filterFruits(fruits, { fruit: Fruit -> fruit.name == "사과" })
// 중괄호와 화살표를 활용하는 형태, 함수에서 받는 함수 파라미터가 마지막에 위치해있으면
// 소괄호 안에 중괄호 들어간게 어색해 보일 수 있으므로, 뺄 수 있다.
// 파라미터가 한 개인 경우에는 그냥 it 이라고 사용하면 추론이 된다. fruit -> fruit.name 이 it -> it.name 으로 가능하다.
filterFruits(fruits) { it.name == "사과" }
}
private fun filterFruits(fruits: List<Fruit>, filter: (Fruit) -> Boolean): List<Fruit> {
val result = mutableListOf<Fruit>()
for (fruit in fruits) {
if (filter(fruit)) {
results.add(fruit)
}
}
return fruits
}
Closure
Java에서는 람다를 쓸 때, 람다 밖에 있는 변수를 사용하는 경우 제약이 있었다. 하지만 코틀린에서는 아무런 문제 없이 동작이 가능하다. 코틀린에서는 람다가 시작하는 지점에 사용하고 있는 변수들을 모두 포획해서 정보를 가지고 있기 때문이다. 이렇게 해야만 람다를 진정한 일급 시민으로 간주할 수 있고, 이 데이터 구조를 Closure 라고 부른다.
'[ Kotlin ]' 카테고리의 다른 글
코틀린에서 부가적으로 알아둘만한 것들 - 19강 (0) | 2024.08.29 |
---|---|
코틀린에서 컬렉션을 함수형으로 다루는 방법 -18강 (0) | 2024.08.29 |
코틀린에서 배열과 컬렉션을 다루는 방법 - 15강 (0) | 2024.08.28 |
코틀린에서 다양한 클래스를 다루는 방법 - 14강 (0) | 2024.08.28 |
코틀린에서 중첩 클래스를 다루는 방법 - 13강 (0) | 2024.08.28 |