매개 변수나 함수의 반환 값으로 함수가 사용되는 함수
코틀린에서는 람다나 함수 참조를 사용해 함수를 변수에 넘길 수도 있고 그 자체가 값이 되기도 함
private fun highOrderFunction(sum: (Int, Int) -> Int, a: Int, b: Int): Int = sum(a, b)
fun main() {
println(highOrderFunction({ x, y -> x + y }, 20, 30))
}
<--출력 결과-->
50
highOrderFunction
파라미터(Int, Int) → Int인 함수(sum)와 Int, Int 총 3개를 받는 함수
함수의 body에서는 전달받은 함수 sum을 실행하며 두 개의 변수 a, b를 넘겨줌
private fun sum(a: Int, b: Int): Int = a + b
private fun sumFunction(): Int = sum(40, 2)
fun main(){
println($sumFunction()
}
<--출력 결과-->
42
val multiply = {x: Int, y: Int -> x*y}
println(${multiply(8,8)})
<--출력 결과-->
64
val helloWorld: () -> {println("Hello World!")}
val outSelfSum: (Int) -> Int = {a -> a+a}
helloWorld()
println(ourSelfSum(10))
<--출력 결과-->
Hello World!
20
val nestedLambda: () -> () -> Unit = {{print("nested")}}
val print: () -> Unit = {print("print")}
val print2 = {println("print")}
print()
print2()
<--출력 결과-->
print
print
fun main() {
callFunction({println("hello")} )
callFunction(){
println("hello")
}
callFunction {
println("hello")
}
}
private fun callFunction( call:() -> Unit ) = call()
<--출력 결과-->
hello
hello
hello
파라미터가 1개 이상인 람다식의 경우, 람다식을 제일 뒤로 이동해서 괄호 밖으로 빼서 표현 가능
제일 처음 살펴 본 highOrderFunction의 경우처럼 여러 개의 파라미터를 받는 경우, 람다식을 제일 마직막으로 이동시키면 메서드 밖으로 이동 가능
private fun highOrderFunction(a: Int, b: Int, sum: (Int, Int) -> Int): Int = sum(a, b)
fun main() {
println(highOrderFunction(20, 30, {x, y -> x + y}))
var result = highOrderFunction(20, 30){
x, y -> x + y
}
println(result)
}
<--출력 결과-->
50
50
코틀린에서 유틸리티성으로 제공하며 람다를 인자로 받아 동작하는 함수
확장함수의 형태로 모든 객체에서 호출 가능
객체를 초기화 할 때 실행할 코드가 있는 경우 등에서 자주 사용됨
java에서 객체의 내용을 참조하려면 object를 reference에 변수에 저장한 후 변수에 ‘ . ‘으로 접근했으나 코틀린에서는 표준함수를 사용하여 바로 접근 가능
let( ), apply( ), run( ), with( ), also( )
let( )을 호출하는 객체의 람다식 안에 파라미터로 넘김
‘it’을 통해 호출 객체에 접근
{ } 의 결과값을 반환
fun main() {
letTest()
}
fun letTest() {
var arr = arrayOf(1, 2, 3)
// let은 젤 마지막 줄이 리턴
var result = arr.let {
println("${it[1] + it[2]}")
it[1] + it[2]
} //let 람다식 계산 결과를 리턴
println(result)
//let 람다식에서 계산한 arr[1] + arr[2] 를 반환하고 arr[0]을 더해서 출력
val result2 = arr.let { it[1] + it[2] }.plus(arr[0])
println("더하기 결과: $result2")
}
<--출력 결과-->
5
5
더하기 결과: 6
fun main() {
alsoTest()
}
fun alsoTest(){
var student = Student("Park",11)
// also는 return x
var student2 = student.also {
it.age = 15
it.name = "kim"
}
println(student) //Student(name=kim, age=15)
println(student2) //Student(name=kim, age=15)
}
data class Student2(var name : String, var age : Int)
fun main() {
applyTest()
}
fun applyTest() {
var student = Student2("Park", 11)
var student2 = student.apply {
age = 15 //this 생략
name = "kim" //this 생략
}
println(student) //Student2(name=kim, age=15)
println(student2) //Student2(name=kim, age=15)
}
호출 객체를 파라미터로 넘기는 방식과 객체 없이 사용하는 방식 모두 가능
{ }의 결과값을 반환
fun main() {
runTest()
}
fun runTest() {
var a = 10
var b = 15
//객체없이 run 단독 사용
var result = run {
var c = a + b
println(c) //25
c
}//더하기 작업 수행 후 결과 c 반환
//객체에 run 사용.
result = result.run {
plus(5)
}
println(result) //30
}
인자로 받는 객체를 블럭의 파라미터로 전달
run( ) 함수와 기능은 거의 동일
Safe Call 지원하지 않음 → 종종 let과 함께 사용됨
with은 ‘ . ‘ 없이 ( )로 인자를 받는다.
var newName = people?.let{
with(it){
age = 20
name
}
}
data class People(var name : String, var age : Int)
fun main() {
withTest()
}
fun withTest(){
var people = People("Park", 15)
var newAge = with(people){
age = 20
age
}
println(newAge) //20
}
클래스나 함수를 정의할 때 타입을 정하지 않고 포괄적으로 받아 들일 수 있는 상태로 선언
타입 매개 변수를 (< >)를 클래스나 인터페이스 이름 뒤에 붙이면 제네릭하게 만들 수 있음
class Box<T>(t: T){
var value = t
}
// kotlin
class Car2 {
}
// 항목을 담거나 뺄 수 있는 제네릭 인터페이스 Container 정의
interface Container<T> {
fun put(item: T)
fun take(index: Int) : T
}
// 자동차(Car)를 담거나 뺄 수 있는 클래스 Garage 정의
class Garage : Container<Car2> {
private val items:MutableList<Car2> = arrayListOf();
override fun put(item: Car2) {
items.add(item)
}
override fun take(index:Int): Car2 {
return items.get(index)
}
}
data class ToDo(val title: String,
val content: String,
var idx: Int = todoIdx++,
var completed: Boolean = false)
interface ToDoContainer<T> {
companion object Todo{
var todoIdx = 1
}
fun addNewItem(item: T)
fun markCompleted(idx: Int)
}
제네릭을 통한 Todo list 작성
ToDoContainer에 제네릭을 사용하고 매개변수로 Todo 클래스를 대입
filter 함수는 조건에 알맞은 리스트를 반환
class ToDoContainerImpl : ToDoContainer<ToDo> {
var todoItems: MutableList<ToDo> = arrayListOf()
override fun addNewItem(item: ToDo) {
println("addNewItem : $item")
todoItems.add(item)
}
override fun markCompleted(idx: Int) {
todoItems.filter { it.idx == idx }[0].let {
it.completed = true
println("$idx is marked as completed")
println("$it")
}
}
}
제네릭을 통한 Todo list 작성
fun main(){
var todayTodoList = ToDoContainerImpl()
todayTodoList.addNewItem(ToDo("자바공부", "자바 기본은 마스터 했음."))
todayTodoList.addNewItem(ToDo("코틀린공부", "코틀린 공부를 시작하자!!!!"))
todayTodoList.markCompleted(1)
}
<--출력 결과-->
addNewItem : ToDo(title=자바공부, content=자바 기본은 마스터 했음., idx=1, completed=false)
addNewItem : ToDo(title=코틀린공부, content=코틀린 공부를 시작하자!!!!, idx=2, completed=false)
1 is marked as completed
ToDo(title=자바공부, content=자바 기본은 마스터 했음., idx=1, completed=true)
타입 매개변수의 업 혹은 다운 캐스팅을 할 수 있도록 지원되는 키워드
out : B<Sub 타입>을 B<Super 타입>에 대입할 수 있도록 지원
in : C<Super 타입>을 C<Sub 타입>에 대입할 수 있도록 지원
class A<T>
class B<out T>
class C<in T>
fun main() {
//T 로 선언된 경우
val a: A<Int> = A<Int>() // 양쪽 자료형 일치해야 함.
//out T 로 선언된 경우
val b1: B<Number> = B<Number>(); // 양쪽 자료형 일치 -> OK
val b2: B<Number> = B<Int>(); // 왼쪽이 Super 면 -> OK
// val b3: B<Int> = B<Number>(); // 왼쪽이 Sub 면 -> 컴파일 오류
//in T 로 선언된 경우
val c1: C<Number> = C<Number>() // 양쪽 자료형 일치 -> OK
val c2: C<Int> = C<Number>() // 오른쪽이 Super면 -> OK
// val c3: C<Number> = C<Int>() // 오른쪽이 Sub 면 -> 컴파일 오류
/* <*> 은 상관없이 대응 */
val star1: A<*> = A<Number>()
val star2: A<*> = A<Int>()
val star3: A<*> = A<String>()
}