목차
기본 함수 선언
↑ 함수 정의시 fun 예약어를 사용해 다음처럼 사용할수 있습니다.
Syntax : fun FunctionName(param1:Type , ..) : returnType {
/*...... */
}
fun : 함수 선언 키워드
FunctionName : 함수 이름
param1:Type : 첫번째 파라미터 이름 : 파라미터 타입, 두번째 파라미터 이름 : 파라미터 타입,....
returnType : 리턴 타입 (Unit 일 경우 생략 가능)
함수 사용법
>> 함수 정의
fun add (first:Int , second:Int) : Int{
return first +second
}
>> 함수 사용
val a: Int =2
val b: Int = 5
val c =add(a,b)
Log.d(TAG,"a($a) + b($b) => $c")
>> 결과 값
a(2) + b(5) => 7
: 특별한 설명이 필요없는 정수 인자 2개 및 리턴으로 정수를 반환하는 함수 정의및 사용법입니다.
class 안의 멤머함수 일경우
class FuncTestFragment : Fragment() {
private fun add (first:Int , second:Int) : Int{
return first +second
}
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
val a: Int =2
val b: Int = 5
val d =this.add(a,b) // this 를 사용한 클래스 내부 호출
val e =FuncTestFragment().add(a,b) // class 객체를 생성후 멤버함수 호출
Log.d(TAG," d($d) , e($e) ")
}
}
> 결과 값
d(7) , e(7)
함수 인자 (name : Type)
: 파스칼 문법 표기법의 name:Type 을 사용하고 각 파라미터 구분은 콤마를 사용합니다.
위 add함수 선언 부분만 다음처럼 표시 가능합니다.
1> 기존 add 함수
fun add (first:Int , second:Int) : Int{
/*....*/
}
2> trailing comma 추가
fun add (first:Int , second:Int,) : Int{
/*....*/
}
3> trailing comma && 개행 추가
fun add (first:Int ,
second:Int,
) : Int{
/*....*/
}
trailing comma 는 git diff 시 유용하고 kotlin 1.4 버전 이상에서 동작합니다.
default arguments
: 인자에 값을 미리 넣어놓고 없을 경우 대비시 사용합니다.
fun add (first:Int = 1 , second:Int =2 ,) : Int{
return first +second
}
val a: Int =2
val b: Int = 5
val f =add(a,b)
val g =add(a) // second 파라미터 인자를 default 값 2 사용한 예제 ( 2 + 2 ==> 4)
val h =add() // first ,second 파라미터 모두 default 값을 사용한 예제 ( 1 + 2 ==> 3)
Log.d(TAG," f($f) , g($g) , h($h)")
<결과 값>
f(7) , g(4) , h(3)
: 초기값은 type 뒤에 = 추가후 대입합니다. ( ex> add(first:Int = 1 ,...)
class 안의 override 함수는 초기값을 넣을수 없습니다.
Android Studio 에서 class 안의 override 함수에 초기값을 넣을 경우 아래처럼 에러가 발생합니다.
override fun foo (i :Int = 3) { /*.... */ }
open class A {
open fun foo(i:Int =2){
Log.d("FuncTestFragment","class A i =$i")
}
}
class B : A() {
override fun foo (i :Int){
Log.d("FuncTestFragment","class B :: i =$i")
}
}
val clsB : B = B()
clsB.foo()
<결과 값>
class B :: i =2
==> override 함수는 초기값을 가질수 없습니다. 부모안의 open 함수에서 초기값을 넣으면
override 함수에서도 적용됩니다.
==> 초기값을 가지는 인자의 마지막 인수가 람다식일 경우 Named argument 나 괄호 외부로 이동할수 있습니다.
fun add2 (first:Int = 1 , second:Int =2 ,func:() -> Unit) : Int{
func()
Log.d("FuncTestFragment","first($first) , second($second)")
return first +second
}
fun main()
{
--> 가장 기본 형태의 호출 방법
add2(first=7,second=4 ,func={Log.d("FuncTestFragment","default Arg exp1")})
--> 파라미터의 이름 생략시 순서대로 값이 들어갑니다. (first=5 , second=3)
add2(5,3) {Log.d("FuncTestFragment","default Arg exp2")}
--> 파라미터의 순서도 이름을 넣으면 바꿀수 있습니다. (Named arguments)
마지막 인자가 람다식일 경우 () 바깥으로 뺄수도 있습니다. (Outside parentheses)
add2(second=2,first=3) {Log.d("FuncTestFragment","default Arg exp3")}
--> 파라미터를 생략하면 초기값이 적용됩니다.
add2(5) {Log.d("FuncTestFragment","default Arg exp4")} // second 값에 초기값 2 대입
--> 앞의 2개인자 생략하고 마지막 람다함수 인자만 있는 경우 () 안에 넣는 경우
add2(func ={Log.d("FuncTestFragment","default Arg exp5")})
--> 람다함수 인자를 () 바깥으로 이동한 경우
add2() {Log.d("FuncTestFragment","default Arg exp6")}
--> () 까지 생략한 경우
add2 {Log.d("FuncTestFragment","default Arg exp7")}
}
< 결과 값>
D/FuncTestFragment: default Arg exp1
D/FuncTestFragment: first(7) , second(4)
D/FuncTestFragment: default Arg exp2
D/FuncTestFragment: first(5) , second(3)
D/FuncTestFragment: default Arg exp3
D/FuncTestFragment: first(3) , second(2)
D/FuncTestFragment: default Arg exp4
D/FuncTestFragment: first(5) , second(2)
D/FuncTestFragment: default Arg exp5
D/FuncTestFragment: first(1) , second(2)
D/FuncTestFragment: default Arg exp6
D/FuncTestFragment: first(1) , second(2)
D/FuncTestFragment: default Arg exp7
D/FuncTestFragment: first(1) , second(2)
Named arguments
: 이름을 적어주고 함수를 호출하면 파라미터 순서에 신경 쓸 필요가 없습니다.
fun namedArgTestFunc(
str:String,
b_first:Boolean = true,
b_second:Boolean = false,
b_third:Boolean = false,
str2:String = "sub string",
){
Log.d(TAG,"str =$str , b_first =$b_first , b_second= $b_second, b_third =$b_third , str2 =$str2")
}
fun main()
--> 모든 인수에 값을 넣고 호출
namedArgTestFunc("Named Argu 1",true,true, true,"sub");
--> 첫번째 인자만 값 넣고 호출
namedArgTestFunc("Named Argu 2");
--> 첫번째 ,두번째 인자만 값 넣고 호출 (나머지는 default 값 적용됩니다.)
namedArgTestFunc("Named Argu 3",false);
--> Named Arguments 를 사용해 b_first 에는 초기값을 넣고 ,b_second 값만 넣고 호출
namedArgTestFunc("Named Argu 4", b_second = true);
< 결과 값>
D/FuncTestFragment: str =Named Argu 1 , b_first =true , b_second= true, b_third =true , str2 =sub
D/FuncTestFragment: str =Named Argu 2 , b_first =true , b_second= false, b_third =false , str2 =sub string
D/FuncTestFragment: str =Named Argu 3 , b_first =false , b_second= false, b_third =false , str2 =sub string
D/FuncTestFragment: str =Named Argu 4 , b_first =true , b_second= true, b_third =false , str2 =sub string
==> Named Argumnet 사용뒤에 값만 대입할경우 아래처럼 에러가 발생합니다.
namedArgTestFunc("Named Argu 5", b_second = true ,false); // error
리턴값이 Unit 인 함수
: Unit 이란 하나의 값을 가지는 타입이란 의미로 리턴을 명시적으로 생략 가능하다.
fun namedArgTestFunc(
str:String,
b_first:Boolean = true,
):Unit {
/*.... */
return Unit
}
--> 위 함수서 return Unit 은 return 만 적어도 됩니다.
fun namedArgTestFunc(
str:String,
b_first:Boolean = true,
):Unit {
/*.... */
return
}
--> 리턴 타입및 return 문도 생략 가능합니다.
fun namedArgTestFunc(
str:String,
b_first:Boolean = true,
){
/*.... */
}
단일표현식 함수
: 한줄 표현이 가능한 함수에 대해 {} 를 생략하고 = 를 추가한 함수표현
fun add (first:Int = 1 , second:Int =2 ,) : Int{
return first +second
}
/*single-expression functions */
==> 위 add() 함수를 single-expressin 함수로 바꾸기
fun add3 (first:Int = 1 , second:Int =2 ,) : Int = first +second
==> 리턴값이 유추가능하므로 생략 가능합니다.
fun add4 (first:Int , second:Int) = first +second
가변 인수 (vararg)
: 인자의 갯수가 정해지지 않은 경우에 사용하고 보통 마지막 인자에 사용합니다.
마지막 인자가 아닐경우 named argument 를 사용해야 합니다.
Syntax : vararg name: Type
vararg : modifier
name : Variable name
Type : Variable type
fun varArgTest(vararg va : String):Unit
{
for((i ,item) in va.withIndex()){
Log.d(TAG,"item[$i]=$item")
}
}
// generic function
fun <T> varArgTest2(vararg va : T):List<T>
{
val result =ArrayList<T>()
for(item in va){
result.add(item)
}
return result
}
fun main()
{
--> 인수가 한개인 경우
Log.d(TAG,"func_test4 : Variable argument 1")
varArgTest("one")
--> 인수가 2개인 경우
Log.d(TAG,"func_test4 : Variable argument 2")
varArgTest("one", "two")
--> 인수가 3개인 경우
Log.d(TAG,"func_test4 : Variable argument 3")
varArgTest("one","two", "three")
--> 인수타입이 정해지지 안은 경우는 generic을 사용합니다.
Log.d(TAG,"func_test4 : Variable arguments with generic")
val list:List<String> = varArgTest2("one","two", "three" ,"four")
for((i,item) in list.withIndex())
Log.d(TAG,"List[$i] = $item ")
}
<결과 값>
D/FuncTestFragment: func_test4 : Variable argument 1
D/FuncTestFragment: item[0]=one
D/FuncTestFragment: func_test4 : Variable argument 2
D/FuncTestFragment: item[0]=one
D/FuncTestFragment: item[1]=two
D/FuncTestFragment: func_test4 : Variable argument 3
D/FuncTestFragment: item[0]=one
D/FuncTestFragment: item[1]=two
D/FuncTestFragment: item[2]=three
D/FuncTestFragment: func_test4 : Variable arguments with generic
D/FuncTestFragment: List[0] = one
D/FuncTestFragment: List[1] = two
D/FuncTestFragment: List[2] = three
D/FuncTestFragment: List[3] = four
==> 인자로 Array 가 들어오는 경우는 2가지로 분리가 됩니다.
fun <T> varArgTest2(vararg va : T):List<T>
{
val result =ArrayList<T>()
for(item in va){
result.add(item)
}
return result
}
fun main()
{
val list:List<String> = varArgTest2("one","two", "three" ,"four")
Log.d(TAG,"func_test4 : Variable arguments with Arraylist")
==> arraylist 를 인수로 넘기면 결과는 인수가 3개만 나옵니다.
val list2 =varArgTest2(list,"five","six")
for((i,item) in list2.withIndex())
Log.d(TAG,"list2[$i] = $item ")
==> arraylist 의 각각의 아이템을 낱게로 추가시
toTypedArray() 와 spread 연산자(*)를 사용합니다.
Log.d(TAG,"func_test4 : Variable arguments with toTypedArray")
val list3 =varArgTest2(*list.toTypedArray(),"five","six")
for((i,item) in list3.withIndex())
Log.d(TAG,"list3[$i] = $item ")
==> primitive type 의 경우 spread 연산자만 사용하면 됩니다.
val a = arrayOf(1,2,3)
val list4 =varArgTest2(0,*a,4)
for((i,item) in list4.withIndex())
Log.d(TAG,"list4[$i] = $item ")
==> primitive type array의 경우도 toTypedArray와 함께 사용 해야 합니다.
val b: IntArray = intArrayOf(2,3,4) // IntArray is a primitive type array
val list5 =varArgTest2(0,1,*b.toTypedArray(),5)
for((i,item) in list5.withIndex())
Log.d(TAG,"list5[$i] = $item ")
}
<결과값>
D/FuncTestFragment: func_test4 : Variable arguments with Arraylist
D/FuncTestFragment: list2[0] = [one, two, three, four]
D/FuncTestFragment: list2[1] = five
D/FuncTestFragment: list2[2] = six
D/FuncTestFragment: func_test4 : Variable arguments with toTypedArray
D/FuncTestFragment: list3[0] = one
D/FuncTestFragment: list3[1] = two
D/FuncTestFragment: list3[2] = three
D/FuncTestFragment: list3[3] = four
D/FuncTestFragment: list3[4] = five
D/FuncTestFragment: list3[5] = six
D/FuncTestFragment: list4[0] = 0
D/FuncTestFragment: list4[1] = 1
D/FuncTestFragment: list4[2] = 2
D/FuncTestFragment: list4[3] = 3
D/FuncTestFragment: list4[4] = 4
D/FuncTestFragment: list5[0] = 0
D/FuncTestFragment: list5[1] = 1
D/FuncTestFragment: list5[2] = 2
D/FuncTestFragment: list5[3] = 3
D/FuncTestFragment: list5[4] = 4
D/FuncTestFragment: list5[5] = 5
infix notation (중위표기법)
: infix 키워드와 함꼐 선언하고 사용시 .(dot) 과 () : parentheses 를 생략 가능합니다.
<요구사항>
▼ 멤버함수 또는 확장 함수
▼ 한개의 인수를 가질것
▼ 초기값 사용 불가 && 가변인수 사용 불가
infix fun Int.multiply(mul:Int):Int{
return this * mul
}
infix fun Int.sum(a:Int):Int {
return (this + a)
}
infix fun Int.substract(a:Int):Int {
return this - a
}
fun main()
{
>> 중위표기법 미사용
val sum1 = 10.sum(5)
>> . 생략 표현
val subs = 10 substract(3) // omitting the dot
>> . && () 생략 표현법
val multi = 3 multiply 10 // omitting the dot and parentheses
>> 연산자 우선순위는 산술연산자보다 낮습니다.
val div2 = 10 divide 3 + 2 // lower precedence than arithmetic operator.
}
▼ 산술연산자보다 우선순위가 낮습니다.
Ex> 1 shl 2 +3 <====> 1 shl (2+3)
함수 사용범위
▼ 지역 함수 (Local functions)
==> 지역함수 바깥에 선언된 변수를 지역함수 안에서 사용가능합니다.
지역함수는 지정된 블럭/함수 밖에서는 사용 불가능 합니다.
fun localFuncTest() {
val outerVal = 55
Log.d(TAG,"localFuncTest")
fun localFunc(){
Log.d(TAG,"localFunc outerVal =$outerVal") // outerVal 사용되기전 정의 되어 있어야 함.
}
localFunc() // 선언후 호출해야 함
}
fun main()
{
Log.d(TAG,"func_test6 : Local && Member function")
localFuncTest()
}
< 결과 값>
FuncTestFragment: func_test6 : Local && Member function
FuncTestFragment: localFuncTest
FuncTestFragment: localFunc outerVal =55
▼ 멤버 함수 (Member functions)
==> 클래스(class) 나 오브젝트(object) 안에 정의된 함수
open class A {
open fun foo(i:Int = 5){
Log.d("FuncTestFragment","class A i =$i")
}
}
fun main()
{
Log.d(TAG,"func_test6 : Member function")
A().foo() //클래스 인스턴스 생성 및 함수호출
}
< 결과 값>
D/FuncTestFragment: func_test6 : Member function
D/FuncTestFragment: class A i =5
제네릭 함수
==> 일반 함수 선언에서 이름앞에 <> 를 적어주면 됩니다.
fun <T> func_name( item:T) :Unit { /*.... */}
가변인수 항목의 varArgTest2 함수 참조하세요.
fun <T:Number> generic_add(first:T,second:T):T
{
val result:T = (first.toDouble() + second.toDouble()) as T
return result
}
Tail recursive 함수
: tailrec 키워드 사용시 함수 재귀 호출 코드가 while() 루프 코드로 변경됩니다.
tailrec 사용한다고 모두 while() 루프 코드로 변환되지 않으니 꼭 decompile 해 확인하시기 바랍니다.
사용방법은 fun 키워드 앞에 tailrec 를 적어주면 됩니다.
private fun recursive_sum(n:Long, acc:Long):Long
{
return if (n <= 0) {
acc
} else {
recursive_sum(n-1, n+acc)
}
}
private tailrec fun recursive_sum2(n:Long, acc:Long):Long
{
return if (n <= 0) {
acc
} else {
recursive_sum2(n-1, n+acc)
}
}
fun main()
{
Log.d(TAG,"func_test8 : tail recursive function")
val a= recursive_sum(300000,1); >> 갤럭시 S6 에서 스택 오버플러워 발생
Log.d(TAG,"a=$a")
}
<결과 값>
I/art: Background partial concurrent mark sweep GC freed 218761(18MB) AllocSpace objects, 0(0B) LOS objects, 9% free, 146MB/162MB, paused 270us total 114.292ms
D/Error: ERR: exClass=java.lang.StackOverflowError
D/Error: ERR: exMsg=stack size 8MB
D/Error: ERR: file=FuncTestFragment.kt
D/Error: ERR: class=com.xxx.rc100_app.FuncTestFragment
D/Error: ERR: method=recursive_sum line=173
D/Error: ERR: stack=java.lang.StackOverflowError: stack size 8MB
fun main()
{
Log.d(TAG,"func_test8 : tail recursive function")
val a= recursive_sum2(300000,1); // tailrec modifier 사용시 스택 에러 발생 안함.
Log.d(TAG,"a=$a")
}
< 결과 값>
D/FuncTestFragment: func_test8 : tail recursive function
D/FuncTestFragment: a=45000150001
위의 recursive_sum() && recursive_sum2() 함수 Bytecode 변환후 java decompile 후 코드 내용
private final int recursive_sum(int n, int acc) {
return n <= 0 ? acc : this.recursive_sum(n - 1, n + acc);
}
private final int recursive_sum2(int n, int acc) {
while(n > 0) { // <---------- while 루프
int var10000 = n - 1;
acc += n;
n = var10000;
}
return acc;
}
그럼 수고하세요.