이번글은 프로그래밍의 꽃인 함수 입니다. 이미 알고 있으시겠지만 함수(function:기능)란 '특정 기능의 코드가 써있는 영역' 이라고 생각하시면 됩니다.
로그인기능 관련코드들이 써 있다면 'Login함수', 회원가입기능 관련 코드가 있다는 'Signup함수' 같은 식으로 만들어 필요할 때 원하는 기능의 함수들을 적절히 호출하여 전체 프로그램이 동작하도록 하는 것이 프로그래밍 이라고 보시면 됩니다.
이번에도 함수문법 자체를 수업하듯이 소개한다기 보다는, 자바와는 다른 코틀린의 함수 문법 위주로 소개를 하겠습니다. 이것도 사실 자바와 표기법의 차이가 많고 함수형프로그래밍 언어의 특징이 도입되어 다소 생소한 문법과 개념이 등장하니 한번에 이해한다기 보다 이런식으로 사용하는 구나 정도로 살펴보고 앞으로의 앱 개발 과정을 통해 익숙해지면 학습해보길 바랍니다.
새로운 코틀린 파일 Test05Basic5.kt 를 만들고 문법을 알아보겠습니다.
프로그램의 시작인 main()함수까지 작성하겠습니다.
7. 함수 function
1) 함수 정의 및 호출
특정 기능을 수행하는 코드를 작성하는 함수를 만드는 것을 함수를 정의한다고도 부르는데요. 이 별도의 함수는 일반적으로는 main()함수 영역의 바깥쪽에 만드는 것이 일반적입니다.
자바의 경우 완전체 객체지향언어를 원하는 언어여서 객체를 설계하는 class {..} 영역 밖에 무엇인가를 작성하는 것을 허용하지 않았습니다. 그래서 자바에서의 모든 기능(함수)은 class.. 객체안에만 존재할 수 있기에 함수라고 부르지 않고 메소드(method)라고 불렀습니다. 즉, 자바에서는 함수 라는 용어가 존재하지 않습니다.
코틀린은 객체지향언어가 아니고 함수형 언어의 특징을 가진 언어여서 객체 밖에 별도의 변수나 함수가 존재할 수 있습니다. 그래서 코틀린에서는 객체안에 있는 것을 메소드method 라고 부르고, 객체 밖에 있는 것을 함수function 이라고 구분하여 부릅니다. 이번글에서는 객체 밖에 있는 함수function만 소개하고자 합니다. 메소드는 향후 진행 될 코틀린의 객체지향 글에서 자세히 소개하겠습니다.
자. 그럼 console 화면에 "show function" 이라는 글씨를 출력하는 기능함수 show() 를 만들어 보겠습니다.
코틀린에서는 함수를 정의할 때 리턴타입의 위치에 fun 키워드를 사용합니다.
//시작 함수
fun main(){
}//main함수 .. 영역 끝
//1) 함수의 정의
//함수를 정의할 때 리턴타입의 위치에 fun 키워드를 사용 -- 파라미터와 리턴이 없는 함수
fun show(){
println("show function")
println()
}
함수를 만들었다고 해서 그 함수안에 작성한 코드가 자동으로 실행되는 것은 아닌것을 알고 있으시죠?
잘 알다시피 프로그램의 시작은 main()함수의 중괄호 { .. 로부터 ..} 중괄호 닫을 때까지만 실행되기에 main함수{..} 안에서 show()함수를 불러야만 실행됩니다. 이를 함수의 호출이라고 합니다.
그럼 main함수 안에서 show()함수를 호출하여 실행되도록 코드를 추가하겠습니다.
//시작 함수
fun main(){
//함수호출
show()
}//main함수 .. 영역 끝
//1) 함수의 정의
//함수를 정의할 때 리턴타입의 위치에 fun 키워드를 사용 -- 파라미터와 리턴이 없는 함수
fun show(){
println("show function")
println()
}
//**출력**
show function
2) 파라미터 전달
함수를 호출할 때 특정 값을 전달하면, 함수에 파라미터(매개변수)를 만들어 받아서 사용할 수 있는데 이 매개변수를 만들 때는 변수를 만들때 사용하는 var, val 키워드를 사용하면 안됩니다. 코틀린은 기본적으로 모든 매개변수는 값 변경이 불가능한 val 변수로 만들어집니다.
//시작 함수
fun main(){
//함수호출 - 파라미터에 값 전달 - 정수, 문자열 전달
output(100, "Hello")
}//main함수 .. 영역 끝
//파라미터를 가진 함수 ( 파리미터명 : 자료형 ) - var, val 키워드 명시 X , 자동 val로 지정됨 (자료형 생략 불가)
fun output(a:Int, b:String){ //전달된 정수, 문자열을 받기위해 Int, String 변수로 지정
println(a)
println(b)
println()
}
//**출력**
100
Hello
파라미터는 값 변경이 불가능 한 val 변수로 만들어지기에 값을 대입하는 코드를 사용하면 문법적 에러가 발생합니다.
fun output(a:Int, b:String){
println(a)
println(b)
//파라미터 값 변경 시도해보기
a=50 //ERROR - 매개변수는 자동 val
println()
}
3) 리턴하는 함수
자바와 다르게 함수를 만들 때 리턴타입을 먼저 작성하지 않고 함수 소괄호() 뒤에 : 후에 작성합니다.
두 정수를 파라미터로 받아 덧셈하여 그 연산 결과를 리턴해주는 함수를 만들고 이를 main()함수에서 호출하여 사용해보겠습니다.
//시작 함수
fun main(){
//리턴을 하는 함수 호출 - 두 정수 50, 30을 sum함수에 전달하고 연산된 결과 리턴값을 num변수로 받기
var num= sum(50, 30)
println("sum함수의 결과값 : $num ")
}//main함수 .. 영역 끝
//리턴하는 함수 [ 리턴타입을 함수()뒤에 : 후에 작성 ]
fun sum(a:Int, b:Int) :Int{
return a+b
}
//**출력**
sum함수의 결과값 : 80
참고로. 자바와 다르게 함수의 리턴이 없으면 void 타입이 아니고 Unit이라는 타입의 객체를 리턴하게 됩니다. 즉, 리턴값이 없는 게 아니기에 참조변수로 참조하는 것이 가능합니다. 물론. 특별히 이 Unit을 사용하는 것은 아니니 그렇구나 정도로 알아 두시기 바랍니다.
//시작 함수
fun main(){
//리턴이 없는 함수의 리턴을 받으면?? void가 아니라 Unit이라는 자료형이 됨.
var x= display()
println(x)
}//main함수 .. 영역 끝
//리턴타입이 없는 함수
fun display(){
println("display!!");
}
//**출력**
display!! //display()함수 안에 있는 println() 에 의해 출력된 글씨
kotlin.Unit //main()함수의 x 변수의 출력에 의한 글씨.. Unit 타입이라는 것을 알 수 있음.
1) ~ 3) 까지, 함수정의 및 호출부터 파라미터, 리턴타입까지 자바와 다른 코틀린의 기본 함수 문법을 알아봤습니다.
정리하면,
- 함수를 정의할 때는 fun 키워드를 사용 [ function(함수)의 약자 ]
- 자바와 다르게 class 밖에서도 함수를 정의 할 수 있음
- 파라미터(매개변수)를 만들 때 var, val 키워드를 명시하면 에러. [ default : val 변수 ]
- 리턴타입은 함수() 소괄호 다음에 : 콜론 표기 후 명시
♣ 코틀린 함수 문법의 특이점들
4) 함수선언의 단순화
함수를 정의할 때 return 키워드를 할당 연산자 [ = ] 로 대체하여 간단하게 표기하는 것이 가능합니다.
4.1) 단순화 문법의 표기법을 알아보기 전에 먼저, 기본적인 return 을 가진 함수를 살펴보겠습니다.
//시작 함수
fun main(){
// getData()함수의 return 값을 받기
val data= getData()
println(data) // 출력 : Hello
}//main함수 .. 영역 끝
//기본적인 return을 가진 함수
fun getData(): String{
return "Hello"
}
4.2) return 값을 할당연산자 = 로 바꾸어 함수 선언해보기
//시작 함수
fun main(){
// 단순화된 함수의 리턴값 받기 -- 사용법은 일반 return 키워드 함수 사용과 다른 점 없음
val data2= getData2()
println(data2) // 출력 : Hello
}//main함수 .. 영역 끝
fun getData2():String = "Hello"
getData()함수를 호출하면 Hello 값이 리턴되니, 호출하는 입장에서는 getData()함수가 Hello값을 가지고 있는 것으로 볼 수도 있으니 그냥 함수가 return 값을 가지고 있다는 형태의 표기법이 어색하지 않으니 중괄호{ .. }와 return 키워드를 생략하여 코드가 아주 간결해 지도록 하는 간편 문법입니다.
근데 함수의 리턴 코드가 다소 복잡한 경우도 있겠죠. 이를 알아보겠습니다.
4.3) 먼저, 조금 더 복잡한 리턴 코드가 있는 함수 만들어보기
//시작 함수
fun main(){
//함수 호출하면서 정수값 5를 전달하여 결과 받기
val data3= getData3(5)
println(data2) // 출력 : Good
}//main함수 .. 영역 끝
//좀더 복잡한 리턴 코드가 있는 함수 -- 파라미터로 값을 받아 조건에 따라 리턴값이 다른 함수
fun getData3(num:Int): String{
if( num < 10 ) return "Good" -- 전달받은 5가 10보다 작기에 Good 리턴
else return "Bad"
}
4.4.) 할당연산자로 조건에 따른 리턴도 가능합니다. 실행문의 마지막 값을 리턴할 수 있는 if표현식을 사용하는 겁니다.
//시작 함수
fun main(){
//함수 호출하면서 정수값 15를 전달하여 결과 받기
val data4= getData4(15)
println(data4) // 출력 : Bad
}//main함수 .. 영역 끝
//할당연산자로 조건에 따른 리턴도 가능 -- if 표현식 이용
fun getData4(num:Int): String = if(num<10){
"Good"
} else {
"Bad"
}
5) 익명함수
함수의 이름이 없는 함수하고 하여 '익명함수' 라고 부릅니다. 함수를 변수에 대입하여 전달할 수 있다는 점에서 함수형 프로그래밍 언어의 특징을 가장 잘 보여주는 문법입니다.
자바의 익명클래스에 대해 익숙하다면 어느정도 받아들이기 어렵지 않겠지만 익숙치 않다면 다소 어렵게 느껴질겁니다. 이해한다기 보다 이런문법이 있다는 정도로 받아들이고 추후 앱개발 과정에서 이벤트 리스너 처리 코드를 작성하면서 익숙해 지도록 하겠습니다.
일반 함수와 익명함수의 문법적 차이를 비교하기 위해 먼저, 일반적인 함수 부터 만들어 보겠습니다.
5.1) 기본적인 함수
//시작 함수
fun main(){
aaa() //일반함수 호출
}//main함수 .. 영역 끝
//기본적인 함수
fun aaa(){
println("aaa")
}
5.2) 익명함수
함수를 만들 때 함수명을 지정하지 않는 것이 익명함 수 입니다. 하지만 단순히 일반함수에서 이름만 없으면 호출할 방법이 없으니 문법적 에러입니다.
//익명함수 - 함수의 이름이 없는 함수 [ 당연히 그냥 이름만 지우면 에러 - why? 함수의 기능은 있지만 호출할 이름이 없으니까.
fun (){...} //ERROR
그래서 익명함수는 반드시 어떤 변수에서 참조되고 있어야 합니다. 이렇게 함수를 가진 변수명을 이용하여 함수를 호출할 수 있습니다.
//bbb 변수에 이름없는 함수(익명함수)를 대입
val bbb= fun(){
println("bbb")
}
이제 익명함수를 저장하고 있는 변수명 bbb 를 이용하여 익명함수를 호출해서 실행하겠습니다. 변수인 bbb 를 마치 함수이름 인양 호출하시면 됩니다.
//시작 함수
fun main(){
bbb() //익명함수를 참조하는 변수명을 이용하여 함수 호출
}//main함수 .. 영역 끝
//bbb 변수에 이름없는 함수(익명함수)를 대입
val bbb= fun(){
println("bbb")
}
그러고 보니 bbb 변수의 자료형은 무엇일까요? bbb는 함수를 저장하고 있으니 함수의 자료형이겠네요. 아주 중요하게 보셔야할 함수의 자료형 표기법을 알아보겠습니다.
5.3) 익명함수를 가진 변수의 자료형
모든 변수는 자료형을 가집니다.[명시적이든 자동추론이 되든] , 그럼 함수를 가진 변수의 자료형은? 람다표기법(화살표)을 사용합니다.
[ 익명함수의 자료형 : () -> 리턴타입 ] ** 리턴타입이 없으면 Unit [자바의 void역할]
//시작 함수
fun main(){
ccc() //명시적으로 익명함수의 자료형[()->리턴타입]이 지정된 참조변수.ccc 이름을 통해 익명함수 호출
}//main함수 .. 영역 끝
//[ 익명함수의 자료형 : () -> 리턴타입 ] ** 리턴타입이 없으면 Unit [자바의 void역할]
val ccc:()->Unit = fun(){
println("ccc")
}
5.4) 익명함수를 축약형으로 쓰고 싶다면?
즉, fun() 키워드가 굳이 없어도 익명함수임을 구별할 수 있을 듯 하여 {..}만으로 생략이 가능합니다.
//시작 함수
fun main(){
ddd() //fun()키워드 생략한 익명함수 호출 [ {} 까지는 생략불가 ]
}//main함수 .. 영역 끝
//익명함수 축약표기법
val ddd:()->Unit = {
println("ddd")
}
당연하게도 {...} 까지 생략하는 축약형은 불가능 합니다. 함수의 기능 코드 작성임을 인식하기도 해야하고. {}까지 없애면 실행문이 함수일때 리턴값을 대입하라는 글씨로 오인될 수 있습니다.(함수선언의 단순화). 즉, 익명함수 {}는 생략불가!
val ddd2:()->Unit = println("ddd2") //ERROR
5.5) 축약된 익명함수를 참조하는 변수의 자료형을 생략하는 자동 추론도 가능합니다.
//시작 함수
fun main(){
eee() //익명함수를 가진 변수의 자료형을 명시하지 않고 추론하도록..하고 사용
}//main함수 .. 영역 끝
//변수의 자료형은 자동 추론이 되니까 익명함수를 참조하는 변수의 자료형은 생략가능
val eee= {
println("eee")
}
♣파라미터 있는 익명함수
5.6) 파라미터를 전달받는 익명함수
문자열을 전달받아 그 글자의 글자수를 출력해주는 함수를 만들어 보겠습니다. 리턴값은 없는 함수입니다.
//시작 함수
fun main(){
fff("Hello") //파라미터를 전달받는 익명함수 호출 - 문자열을 전달받아 그 글자의 글자수를 출력해주는 함수 [리턴값은 없는 함수 ]
}//main함수 .. 영역 끝
//파라미터를 전달받는 익명함수 [리턴값은 없는 함수 ]
val fff= fun(s:String){
println("글자수:" + s.length)
}
//**출력**
글자수:5
5.7) 파라미터를 가진 익명함수의 자료형 [ (파라미터 자료형)->리턴타입 ]
//시작 함수
fun main(){
ggg("Nice") //익명함수의 자료형을 명시한 참조변수를 이용한 호출
}//main함수 .. 영역 끝
//이 익명함수를 가진 참조변수도 명시적으로 자료형을 명시할 수 있음. [ 파라미터:String 1개, 리턴타입 : Unit ]
val ggg:(String)->Unit = fun(s:String){
println("글자수:" + s.length)
}
//**출력**
글자수:4
5.8) 파라미터를 가진 익명함수의 축약표현
파라미터가 있는 익명함수도 fun()키워드를 생략할 수 있습니다. 다만, 파리미터를 만드는 소괄호()가 생략되었기에 {} 안에 참조변수 명을 쓰고 화살표 -> 로 실행문을 작성합니다.
자바와 비슷하지만 참조변수명과 화살표-> 가 중괄호 {} 안에 있다는 것이 차이가 있습니다.
//시작 함수
fun main(){
hhh("God") //fun()키워드 생략한 익명함수 호출 [ { } 안에서 파라미터 작성하는 코드 필요 ]
}//main함수 .. 영역 끝
//파라미터를 전달받는 함수도 fun()키워드를 생략할 수 있음. 단, { }안에 익명함수의 자료형[()->리턴타입] 같은 형식으로 코딩 필요
val hhh:(String)->Unit = {
s -> println("글자수:" + s.length) // "s->" 의 s가 이 익명함수의 파라미터 변수이름 ( s:String 처럼 자료형명시 해도 됨 )
}
//**출력**
글자수:3
5.9) 파라미터 1개 일때는 파라미터명과 화살표도 생략가능합니다.
//시작 함수
fun main(){
iii("He") //파라미터가 1개라면 { }안에서 생략가능 - 단, 만약 그 파라미터를 사용하고자 한다면 익명함수의 특별한 키워드 "it" 사용
}//main함수 .. 영역 끝
//혹시 파라미터가 1개뿐이라면 "s->" 생략해도 됨.
val iii:(String)->Unit = {
println("iii")
//단, 만약 그 파라미터를 사용하려면.. 익명함수의 특별한 변수 "it" 키워드 사용 [ it변수의 자료형은 참조변수에 명시한 익명함수 자료형(String) 으로 자동 선언됨
println("글자수:" + it.length)
}
//**출력**
iii
글자수:2
단, 익명함수 참조변수의 명시적 타입지정을 안하면 자동으로 파라미터가 없는 ()->Unit 으로 추론되기에 "it" 키워드의 사용이 불가능합니다.
val iii2= {
println("글자수:"+it.lenght) //ERROR - it 을 인식하지 못함
}
또한, {} 안에서 파라미터를 생략하지 않았더라도 익명함수의 참조변수 자료형을 자동 추론되게 할 수 없습니다. 즉, 파라미터가 있을때는 익명함수의 자료형을 참조변수에 명시거나 파라미터의 자료형을 명시적으로 표시해야 합니다.
//{} 안에서 파라미터를 생략하지 않았더라도 익명함수의 참조변수 자료형을 자동 추론되게 할 수 없음. 즉, 파라미터가 있을때는 익명함수의 자료형을 참조변수에 명시해야함.
val iii3= {
//s -> println("글자수:" + s.length) // ERROR
s:String -> println("글자수:" + s.length) // OK - 많이 활용되지는 않음.
}
5.10) 파라미터가 여러개인 익명함수의 자료형. [ (자료형1, 자료형2, ...) -> 리턴타입 ]
파라미터가 여러개일때는 it변수만으로 파라미터들을 표현하는 것이 불가능 하기에 파라미터의 생략은 불가능 합니다.
//시작 함수
fun main(){
jjj("sam", 20) //파라미터 여러개인 익명함수 호출 [ "it"키워드 불가 ]
}//main함수 .. 영역 끝
//파라미터 여러개도 당연히 가능 [ 파라미터가 여러개일때는 "it" 키워드는 사용 불가 ]
val jjj:(String, Int) -> Unit = {
name, age -> println("name: $name age: $age")
}
//**출력**
name: sam age: 20
♣리턴타입이 있는 익명함수
5.11) 리턴타입이 있는 익명함수
익명함수의 리턴타입 지정도 일반함수와 마찬가지로 함수 소괄호 () 뒤에 : 콜론 후 지정합니다.
//시작 함수
fun main(){
val number= kkk() // 리턴타입이 있는 익명함수 호출 [ number의 타입은 자동추론 ]
println( number ) // 출력 : 10
}//main함수 .. 영역 끝
//리턴타입이 있는 익명함수 [파라미터 없고 리턴타입이 Int인 익명함수]
val kkk= fun():Int{
return 10
}
5.12) 리턴타입이 있는 익명함수를 참조하는 변수의 자료형을 명시적으로 지정해보기
//시작 함수
fun main(){
val number2:Int= lll() // 익명함수의 자료형을 명시한 참조변수를 이용한 호출 [명시적으로 자료형 지정]
println( number2 ) // 출력 : 20
}//main함수 .. 영역 끝
//리턴타입이 있는 익명함수를 참조하는 변수의 자료형 명시적으로 지정
val lll: ()->Int = fun():Int{
return 20
}
5.13) 리턴타입이 있는 익명함수의 fun()키워드 생략하는 축약 표기법 사용해보기
* 주의! : 람다식처럼 축약 표기법을 사용했다면 return 키워드는 생략 해야만 합니다.
//시작 함수
fun main(){
val number3:Int= mmm() // fun()키워드를 생략한 익명함수 호출 - [ return 키워드도 삭제해야 함 ]
println( number3 ) // 출력 : 30
}//main함수 .. 영역 끝
//리턴타입이 있는 익명함수의 fun()키워드 생략하는 축약형
val mmm: ()->Int = {
30 // - [ * 주의 : return 키워드를 제거해야 함 *]
}
5.14) 축약 표기법안에 실행문이 여러개 인 경우
만약 축약된 {}안에 실행문이 여러개 인 경우에는 마지막 실행문의 값이 return 되는 값이 됩니다. if표현식과 동일합니다.
//시작 함수
fun main(){
val number4:Int= nnn() // fun()키워드를 생략한 익명함수의 {}안에 실행문이 여러줄인 함수 호출 [ 가장 마지막 실행문이 리턴값 ]
println( number4 ) // 출력 : 50
}//main함수 .. 영역 끝
//만약 {}안에 실행문이 여러개 라면? - 마지막 실행문의 값이 return 값
val nnn: ()->Int = {
30
println("중간 글씨") //{}영역은 실행문 작성 영역이므로 당연히 중간에 이렇게 리턴값이 아닌 실행문이 있어도 됨. 단, 이 println()이 마지막 실행문이면 에러. [리턴타입이 안 맞아서]
40
50 //이 값이 리턴값
//println("중간 글씨") //ERROR -- 이 함수의 리턴타입이 Int여서 println()함수가 마지막 값일 수 없음
}
//**출력**
중간 글씨
50
5.15) 파라미터와 리턴타입이 모두 있는 익명함수
두 개의 정수값을 전달받아 덧셈결과 값을 리턴해주는 함수를 익명함수로 만들고 이 함수의 자료형를 명시해 보겠습니다.
//시작 함수
fun main(){
val add= ooo(5,3) //파라미터와 리턴타입이 모두 있는 익명함수 호출 [두수를 전달받아 덧셈결과를 리턴하는 함수]
println(add) //출력 : 8
}//main함수 .. 영역 끝
//파라미터와 리턴타입이 같이 있는 익명함수 [두 수를 전달받아 덧셈결과를 리턴하는 함수 ]
val ooo:(Int, Int) -> Int = fun(a:Int, b:Int):Int{
return a+b
}
5.16) 파라미터와 리턴타입이 모두 있는 익명함수를 축약표현하기 - 람다식처럼
fun()키워드를 생략하였기에 파라미터는 {}안에 자료형없이 기입하고 화살표 -> 후 return 키워드를 생략한 후 결과값을 기입합니다.
//시작 함수
fun main(){
val add2= ppp(4,6) //fun()키워드 생략한 파라미터와 리턴타입이 같이 있는 익명함수 호출
println(add2) //출력: 10
}//main함수 .. 영역 끝
//fun()키워드 생략한 파라미터와 리턴타입이 같이 있는 익명함수 [두 수를 전달받아 덧셈결과를 리턴하는 함수 ] : {}안에 익명함수 타입(Int,Int)->Int 형태로 코딩 , return 키워드 역시 생략
val ppp:(Int, Int) -> Int = {
a, b -> a+b // (Int,Int)->Int 형태로 코딩 , return 키워드 역시 생략
}
5.17) 파리미터와 리턴타입의 자료형이 다른 익명함수 연습 - 4가지 형태로 연습
5.17-1) 파라미터와 리턴타입이 다른 일반함수
//기본적인 함수 [ String 입력을 받아 글자수를 Int 로 리턴해 주는 기능 함수]
fun stringLength(str:String): Int{
return str.length
}
5.17-2) 파라미터와 리턴타입이 다른 익명함수
//익명함수 [함수를 stringLength2라는 변수에 넣는 것 (String) -> Int 는 String를 매개변수로 받아 Int를 리턴해 준다는 표기법 ]
val stringLength2: (String) -> Int = fun(str:String):Int{
return str.length
}
5.17-3) 파라미터와 리턴타입이 다른 축약형 람다표기법 - fun(), return 키워드 생략
//익명함수 축약형 [ fun() , return 키워드 생략 ]
val stringLength3: (String) -> Int = {
str -> str.length
}
5.17-4) 축약형 람다표기법의 파라미터가 1개인 경우 파라미터명 생략 - fun(), return 키워드, 파라미터 생략 [ 숨겨진 it 파라미터 사용 ]
//파라미터가 1개이므로 생략한 익명함수 축약형 [ fun() , return 키워드 및 파라미터 생략 ]
val stringLength4: (String) -> Int = {
it.length // 생략된 파라미터 1개를 대체하는 it 변수
}
위 4개의 함수를 호출해 사용해 보겠습니다.
//시작 함수
fun main(){
//파라미터와 리턴타입이 다른 익명함수 사용의 마지막 연습
val len= stringLength("android")
println(len) //출력: 7
//익명함수로 만든 함수 호출
val len2= stringLength2("kotlin")
println(len2) //출력: 6
//축약표현 익명함수 호출
println( stringLength3("nice") ) //출력: 4
println( stringLength4("web") ) //출력: 3
}//main함수 .. 영역 끝
지금까지 살펴본 익명함수는 '고차함수'로 이용할 때 많이 사용됩니다. 다음으로 고차함수를 알아보겠습니다.
6) 고차함수
함수를 호출할 때 파라미터에 전달되는 값을 '인수 argument' 라고 부릅니다.
코틀린의 함수는 다른 함수를 인수로 전달 받을 수 있습니다. 이렇게 다른 함수를 인수로 사용하는 함수를 고차 함수라고 합니다. 즉, 파라미터에 함수를 받아서 이 함수를 사용한다는 겁니다. 처음 보시는 분들은 다소 어렵게 느껴하는 문법입니다. 이름부터 다소 난이도가 있어 보이기도 합니다. 사실. 고차함수 라는 이름은 크게 중요하지 않습니다. 개발자들도 '고차함수 만들어 봐' 라는 식의 말은 거의 사용하지 않습니다. 그냥 함수를 파라미터로 받는 함수가 고차함수 라는 것을 알고 있을 뿐이니 굳이 고차함수라는 용어에 너무 집착하지 말고 그냥 함수를 받아 사용하는 함수의 문법적 모습만 경험해 보시기 바랍니다. 추후 앱 개발 과정에서 매우 자주 사용하시게 될 겁니다. 그때 익숙해 져 봅시다.
고차함수를 알아보기 전에 먼저 함수를 객체처럼 다른 변수에 대입할 수 있다는 것을 먼저 알아보겠습니다.
6.1) 익명함수를 참조하는 변수를 다른 변수에 대입하기
//정수를 전달받아 출력하는 익명함수를 저장하는 sss 변수
var sss= fun(a:Int){
println("sss : $a")
}
var ttt= sss //위에서 만든 익명함수 sss 를 ttt변수가 참조하면 ttt()로 함수호출 가능함.
함수를 참조하는 변수 sss와 ttt 모두 같은 함수를 참조하고 있기에 두 변수명 중 어떤 것을 사용해도 호출이 가능합니다.
//시작 함수
fun main(){
sss(100)
ttt(200)
}//main함수 .. 영역 끝
//**출력**
sss : 100 // sss변수에 의해 호출된 출력
sss : 200 // ttt변수에 의해 호출된 출력
6.2) 익명함수를 축약한 람다표기법를 참조하는 변수도 다른 변수에 함수 전달 가능합니다. 즉, 코틀린은 모든 함수를 객체로 다룹니다.
//문자열을 파라미터로 받아 출력해 주는 람다 함수를 저장하는 xxx 변수
var xxx:(String)->Unit= { println(it) }
//xxx 변수가 참조하는 람다함수를 yyy변수에 대입
var yyy= xxx
함수를 참조하는 xxx화 yyy변수명을 이용하여 함수를 호출해 보겠습니다.
//시작 함수
fun main(){
xxx("Hello world")
yyy("Nice to meet you")
}//main함수 .. 영역 끝
//**출력**
Hello world // xxx변수에 의해 호출된 출력
Nice to meet you // yyy변수에 의해 호출된 출력
위 2가지 예제로 살펴봤듯이 코틀린은 함수를 마치 값처럼 다른 변수에 넘겨주면서 호출하는 것이 가능합니다.
이렇게 다른 변수에 함수를 넘겨줄 수 있다면, 어떤 함수의 파라미터(매개변수)에 다른 함수를 인수 값으로 넘겨주는 것도 가능합니다.
6.3) 고차함수
함수를 다른 변수에 대입할 수 있듯이 함수의 파라미터로 다른 함수를 전달하여 사용하는 것을 고차함수 라고 부릅니다.
첫번째 파라미터로 문자열을 받고, 두번째 파라미터로 함수를 전달받는 고차함수 getLength()라는 함수를 만들어 보겠습니다.
이 함수는 첫번째 파라미터로 전달받은 문자열을 두번째 파라미터로 전달받은 함수의 인수로 전달하는 기능을 구현합니다. 조금 이해가 어렵죠? 주석과 코드를 보면서 이해해 보겠습니다.
//'고차함수' : 함수의 파라미터로 다른 함수를 사용하는 것을 고차함수라고 부름 [다른변수에 함수를 대입할 수 있듯이]
// 두번째 파라미터 aaa는 익명함수를 인자로 받겠다고 표시
fun getLength(str:String, aaa: (String)->Int ): Int{
return aaa(str) //전달받은 함수(aaa)의 파라미터로 첫번째 파라미터인 str을 전달하면서 함수호출하고 결과 return
}
위 고차함수의 두번째 파라미터에 5.17-3)에서 사용했던 문자열의 길이값을 리턴해주는 함수를 전달하여 사용해 보겠습니다.
//시작 함수
fun main(){
//함수는 다른 함수를 인수로 취할 수 있습니다. 다른 함수를 인수로 사용하는 함수를 고차 함수라고 합니다. 이 패턴은 자바에서 콜백 인터페이스를 사용할 때와 동일한 방식으로 구성요소 간에 통신하는 데 유용합니다.
val len3= getLength("android",stringLength3) //아래 만든 익명함수를 2번째 파라미터에 전달
println(len3) //출력: 7
}//main함수 .. 영역 끝
//5.17-3)익명함수 축약형 [ fun() , return 키워드 생략 ]
val stringLength3: (String) -> Int = {
str -> str.length
}
//'고차함수' : 함수의 파라미터로 다른 함수를 사용하는 것을 고차함수라고 부름 [다른변수에 함수를 대입할 수 있듯이]
// 두번째 파라미터 aaa는 익명함수를 인자로 받겠다고 표시
fun getLength(str:String, aaa: (String)->Int ): Int{
return aaa(str) //전달받은 함수(aaa)의 파라미터로 첫번째 파라미터인 str을 전달하면서 함수호출하고 결과 return
}
고차함수에 전달하는 함수를 이전에 만들었던 함수를 사용하는 것이 아니라 함수를 호출하면서 그 자리에서 바로 정의하여 전달할 수도 있습니다.
//시작 함수
fun main(){
//함수는 다른 함수를 인수로 취할 수 있습니다. 다른 함수를 인수로 사용하는 함수를 고차 함수라고 합니다. 이 패턴은 자바에서 콜백 인터페이스를 사용할 때와 동일한 방식으로 구성요소 간에 통신하는 데 유용합니다.
val len3= getLength("android",stringLength3) //아래 만든 익명함수를 2번째 파라미터에 전달
println(len3) //출력: 7
//2번째 파라미터 자리에서 익명함수[String받아 Int를 리턴해주는]를 그 자리에서 설계하여 전달해보기
val len4= getLength("kotlin", { str -> str.length })
println(len4) //출럭: 6
}//main함수 .. 영역 끝
당연히 위와 다른 기능의 함수를 정의하여 전달 할 수 도 있습니다.
//시작 함수
fun main(){
//함수는 다른 함수를 인수로 취할 수 있습니다. 다른 함수를 인수로 사용하는 함수를 고차 함수라고 합니다. 이 패턴은 자바에서 콜백 인터페이스를 사용할 때와 동일한 방식으로 구성요소 간에 통신하는 데 유용합니다.
val len3= getLength("android",stringLength3) //아래 만든 익명함수를 2번째 파라미터에 전달
println(len3) //출력: 7
//2번째 파라미터 자리에서 익명함수[String받아 Int를 리턴해주는]를 그 자리에서 설계하여 전달해보기
val len4= getLength("kotlin", { str -> str.length })
println(len4) //출럭: 6
//전달되니 문자열을 Int 타입으로 형변환하는 함수를 전달받아 기능 다르게 동작하도록 해보기
val len5= getLength("450",{ str -> str.toInt() }) //이런식으로 원래 의도와 다른 함수를 전달하여 기능이 바뀌게 할 수도 있음.
println(len5 + 3) //출력: 453 ~ 450+3
}//main함수 .. 영역 끝
6.4) 특이한 고차함수 축약 람다표기법
고차함수의 파라미터안에 또 다른 함수를 쓰면 소괄호 () 안에 중괄호 {} 코드가 작성되기에 가독성이 나빠집니다. 이를 위해 람다식 {}를 소괄호 밖으로 빼서 작성하는 축약 표기법이 가능합니다. 처음에는 잘 읽어지지 않지만 익숙해지면 코딩이 너무 편해집니다.
// ** 코틀린문법에서는 람다식으로 표현된 함수의 파라미터를 () 밖에 작성하는 방식을 권장함. [마치 함수정의 하듯이]
val len6= getLength("ios"){ str -> str.length }
println(len6)
// ** 람다함수의 파라미터가 1개이므로 str-> 를 생략하고 it 이라는 특별한 변수명으로 대체 가능함.
val len7= getLength("mobile"){ it.length }
println(len7)
이 고차함수가 가장 많이 사용되는 곳은 안드로이드의 클릭리스너 작업에 많이 사용됩니다.
추후 SAM 변환 문법 소개시에 추가로 소개할 예정입니다.
7) 함수 파라미터의 default value
자바의 경우 함수를 호출할 때 파라미터의 개수만큼 인수를 전달하지 않으면 에러가 발생합니다. 그래서 함수 호출하면서 사용할 값이 아니더라도 반드시 값을 전달해야만 하는 불편함이 있었습니다. 그래서 코틀린은 혹시 값을 전달 받지 않으면 기본값을 지정할 수 있는 문법을 제공합니다.
//시작 함수
fun main(){
//함수 파라미터의 default value
zzz(5,3)
zzz() //a,b 가 모두 default 값으로 적용됨.
zzz(10) //b는 default 값으로 적용됨.
//혹시 특별하게 b에게 10을 적용하고 싶다면.. 함수 파라미터 선택 적용
zzz(b=5) //호출할때 변수명 지정
}//main함수 .. 영역 끝
//함수 파라미터의 default value
fun zzz(a:Int=1000, b:Int=2000){ //a에 값이 전달되지 않으면 1000, b에 값이 전달되지 않으면 2000
println("a: $a , b: $b")
}
//**출력**
a: 5 , b: 3
a: 1000 , b: 2000
a:10 , b: 2000
a:1000 , b: 5
함수 파라미터의 지정을 통하여 값 전달의 순서를 변경할 수도 있습니다.
//시작 함수
fun main(){
//함수 파라미터의 지정을 통하여 값 전달 순서를 변경할 수도 있음.
xxxxx("korea", "seoul")
xxxxx(city = "newyork", nation = "usa")
}//main함수 .. 영역 끝
//첫번째 파라미터 nation만 default 값 지정
fun xxxxx(nation:String="korea", city:String){
println(nation)
println(city)
println()
}
//**출력**
korea
seoul
usa
newyork
'소소한 소스코드' 카테고리의 다른 글
[안드로이드 Android] Kotlin 언어로의 전환 4. 배열 Array & 컬렉션 Collection (0) | 2025.02.19 |
---|---|
[안드로이드 Android] Kotlin 언어로의 전환 3. 연산자, 조건문, 반복문 자바와 문법 차이점 (0) | 2025.02.17 |
[안드로이드 Android] Kotlin 문자열 format 만들기 (0) | 2025.02.14 |
[안드로이드 Android] Kotlin 언어로의 전환 2. 자료형과 변수 (0) | 2025.02.14 |
[안드로이드 Android] Kotlin 언어로의 전환 1. 주요특징 및 화면출력 (0) | 2025.02.08 |