코틀린 (Kotlin) 함수형 프로그래밍
함수형 프로그래밍
- 불변성(immutable)
- 참조 투명성(referential transparency)
- 일급 함수(first-class-function)
- 게으른 평가(lazy evaluation)
순수한 함수
- 동일한 입력->동일한 결과
- 부수효과 없음
- 부수효과...
- 전역 변수, 객체 상태변경
- 파일, 네트워크 출력
- 예외발생
일급 함수
- 함수를 함수 매개변수로 사용
- 함수를 반환값으로 사용
- 함수를 변수에 저장 가능
게으른 평가
val lazy: String by lazy {
//long work...
}
print(lazy)
print(lazy)
값을 사용할 때 lazy{...} 작업이 실행되어 할당된다.
코드가 실행될때 할당되지 않기때문에 오래걸리는 작업을 필요할때 실행하여 작업을 효율적으로 동작가능.
val 로만 가능하며 최초 한번만 할당되고 두번째부터는 값이 재사용된다.
시퀀스(sequence)
val infinite = generatieSequence(0) { it + 10 }
infinite.take(10).forEach {println("$it")}
실제 사용할때 사용되기때문에 무한대 자료구조로서 사용할 수 있다.
Unit
public object Unit {
override fun toString() = "kotlin.Unit"
}
Unit 은 자바의 void 처럼 아무것도 반환하지 않을때 사용.
Unit 은 참조형 타입이다.
확장함수
private fun String.addHelloPrefix(): String = "Hello, ${this}"
if 조건 표현식
조건문을 구문과 표현식으로 사용할 수 있다.
표현식으로 사용된 경우 else 가 필요하며 변수에 할당할 수 있다.
val x: Int = 1
val y: Int = 2
val max: Int = if (x > y) x else y
when 조건
switch 와 동일하게 사용가능.패턴 매칭 사용 가능.
fun checkValue(value: Any) = when (value) {
"kotlin" -> "kotlin"
in 1..10 -> "1..10"
11, 15 -> "11 or 15"
is User -> "User"
else -> "SomeValue"
}
if else 처럼 동일하게 사용 가능.
when 에 value 없이 사용.
fun checkCondition(value: Any) = when {
value == "kotlin" -> "kotlin"
value in 1..10 -> "1..10"
value === User("Joe", 76) -> "=== User" //주소값 비교
value == User("Joe", 76) -> "== User(Joe, 76)"//데이터equls비교
value is User -> "is User"
else -> "SomeValue"
}
List<Int> 처럼 <Int> 를 패턴매칭 할 수 없다.
numbers.isEmpty() -> 0
else -> numbers.first() + sum(numbers.drop(1))
}
for문
for((index, item) in collection.withIndex()) {println($index, $item)
}
for(i in 1..3)) {...}
for(i until 1..3) {...}
for(i in 6 downTo 0 step 2) {...}
인터페이스(interface)
- 다중 상속
- 추상 함수
- 함수 본문 구현
- 여러 인터페이스에서 같은 이름 함수 가능
- 추상 프로퍼티
interface Foo {
fun printFoo()
fun printFoo2() {
println("Foo2")
}
fun printKotlin() { println("Foo Kotlin") }
}
interface Bar {
fun printBar()
fun printKotlin() { println("Bar Kotlin") }
}
class Kotlin: Foo, Bar {
override fun printFoo() {...}
override fun printBar() {...}
override fun printFoo2() { println("Kotlin-Foo2") } //kotlin.printFoo2()->"Kotlin-Foo2" 출력
//다중 상속에서 동일한 함수 호출시
overrde fun printKotlin() {
super<Foo>.printKotlin()
super<Bar>.printKotlin()
}
}
추상 프로퍼티
인터페이스에서는 프로퍼티 초기화할 수 없으나 get() 으로 사용 가능하며 get() 있으면 override 하지 않아도 됨.
interface Foo {
val bar: Int
get() = 3
}
class Kotlin : Foo {
override val bar: Int = 3
}
클래스(class)
class User(var name:String, val: age: Int = 20)
getter(), setter() 를 자동으로 생성.
기본값 설정 가능.
데이터 클래스(data class)
getter, setter, hashCode, equals, toString, copy, componentN 자동 생성 됨.
enum 클래스
enum class Error(val num: Int) {
WARN(2) {
override fun getErrorName():String {
retrun "WARN"
}
}
abstract fun getErrorName(): String
}
sealed 클래스
클래스를 묶은 클래스.
하위클래스는 sealed class와 동일한 파일에서만 선언 가능.
sealed class Expr
data class Const(val number: Double) : Expr()
data class Sum(val e1: Expr, val e2: Expr) : Expr()
object NotANumber : Expr()
fun getSum(p1: Double, p2: Double): Sum {
return Sum(Const(p1), Const(p2))
}
when 으로 패턴매칭이 가능하며 else 를 작성할 필요 없다.
fun eval(expr: Expr): Double = when(expr) {
is Const -> expr.number
is Sum -> eval(expr.e1) + eval(expr.e2)
NotANumber -> Double.NaN
}
객체 분해
componentN 함수가 객체 분해시 사용.val user: User = User("kotlin", 20)
val (name, age) = user
val (name, _) = user //_는 사용안 할때
for ((name, age) in users) { ... }
{ p1, p2 -> "$p1, $p2" } // 매개변수 두개를 받음
{ (p1, p2) -> "$p1 $p2" } //튜플 하나로 받아 객체 분해
{ p1, p2 -> "$p1, $p2" } // 매개변수 두개를 받음
{ (p1, p2) -> "$p1 $p2" } //튜플 하나로 받아 객체 분해
컬렉션
불변: List, Set, Map...가변: MutableList, MutableSet, Mutablemap...
val list: List<Int> = listOf(1,2,3,4,5)
list.add(6) //컴파일 오류. List,Set 은 불변이기때문에 add함수가 없다
val newList = list.plus(6) //새로 생성해서 추가
list 출력 -> 1,2,3,4,5
newList 출력 -> 1,2,3,4,5,6
list.add(6) //컴파일 오류. List,Set 은 불변이기때문에 add함수가 없다
val newList = list.plus(6) //새로 생성해서 추가
list 출력 -> 1,2,3,4,5
newList 출력 -> 1,2,3,4,5,6
val list: MutableList<Int> = mutableListOf(1,2,3,4,5)
list.add(6) //정상
맵(map)
val map1 = mapOf(1 to "One", 2 to "Two") //{{1=one, 2=two}val map2 = map1.plus(Pair(3, "Three")) //{1=one, 2=two, 3=three}
val mutablemap = mutableMapOf(1 to "One", 2 to "Two")
mutablemap.put(3, "Three")
제네릭
fun <T> head(list: list<T>) : T { ...}let 함수
자신을 파라메터로 넘겨 마지막 블럭값을 리턴.널처리에 사용.
fun<T,R> T.let(block: (T) -> R): R
val name = user?.let { it.lastName + it.firstname } ?: "error"
with 함수
fun <T,R> with(receiver: T, block: T.() ->R): R여기서 T.() 는 람다 리시버로 block 함수에서 this를 사용하지않고 함수나 프로퍼티에 접근할 수 있다.
val stringToInt : String.() -> Int = { toInt() //String 함수 바로 접근 가능 }
val result = with(person) {
name = "kotlin"
age = 20
this //객체 자체 접근시 this 사용
}
run 함수
1. fun <T,R> T.run(block: T.() ->R) : Rval result = person.run {
name="kotlin"
age = 20
this
}
2. fun <R> run(block: () -> R): R
객체를 생성하기 위한 명령문을 하나의 블록으로 묶는 용도
코드 가독성 향상
val person = run {
val name = "kotlin"
val age = 20
Person(name, age)
}
apply 함수
fun <T> T.apply(block: T.() -> Unit): Tblock 함수 내에 반환값이 없고 자신을 반환.
객체만 변경할때 유용.
val result = person.apply {
name = "kotlin"
age = 20
}
run 함수와 다르게 this 없이 person객체를 바로 할당 가능
also 함수
fun <T> T.also(block: (T) -> Unit): T객체 자체를 변경할 때 사용.
val result = person.also {
it.name = "kotlin"
it.age = 20
}
*apply, also 는 빌더패턴과 동일한 용도로 사용
close 함수
closeable object 를 제공하는 객체에 대해 클로즈 작업을 자동으로 해줌FileInputStream("...").use { property.load(it) } // 자동 close
공변 <out T>
타입 S가 T의 하위 타입일 때 Box[S]가 Box[T] 의 하위 타입인 것kotlin < jvm < language 상속 관계가 존재할 때,
covariant(languageBox) //컴파일 오류
covariant(jvmBox)
covariant(kotlinBox)
fun covariant(value: Box<out JVM>) {...}
반공변 <int T>
타입 S가 T의 하위 타입일 때 Box[S]가 Box[T] 의 상위 타입인 것
covariant(languageBox)
covariant(jvmBox)
covariant(kotlinBox) //컴파일 오류
fun covariant(value: Box<in JVM>) {...}covariant(jvmBox)
covariant(kotlinBox) //컴파일 오류
in, out 키워드에서 in은 입력값(set) 에서만 사용할수 있고 out은 반환값(get)에서만 사용할 수 있음
댓글
댓글 쓰기