제어문
if문
- java와 같은 형태로 사용 가능함
- 코틀린에서는 if문을 하나의 표현식으로 간주하며, 값을 반환한다. 변수의 대입문에 식처럼 사용이 가능하다.
(아래와 같이 a>b이면 a가 result에 대입, else에 해당하면 b가 result에 대입된다)
val result = if(a>b) a else b
- 조건에 따라 여러 표현식을 실행해야 하는 경우,
블록의 마지막 값이 반환된다.(if문 내의 블럭 내에 여러 줄의 식이 있다면 리턴값은 마지막줄이 된다.)
--> Kotlin에서는 삼항 연산자를 지원하지 않으므로 이를 if문으로 구현할 수 있는 것이다.
val a = -9
val b = -11
val max = if(a>b){
println("$a is larger than $b ")
println("max variable holds value of a")
a
}else{
println("$b is larger than $a")
b
}
when 문
- Switch 구문에 대응하는 구문, 실행해야할 구문은 화살표 뒤에 해당. when구문은 표현식으로 사용될 수 있다.
val x = readLine()!!
when (x) {
1 -> print("x == 1")
2 -> print("x == 2")
else -> { // Note the block
print("x is neither 1 nor 2")
}
}
- when문을 조건문으로 사용한 경우
val start = 0
val end = 100
val score = 60
when(score){
in 90..end->println("우수함")
50->println("평균임")
in start..end -> println("범위에 있음")
else -> println("범위를 벗어남")
}
for문
- For each 구문과 비슷
1. 배열에 있는 값을 가져다가 반복할 수 있다
val numbers = arrayOf(1,2,3,4,5)
for(num in numbers){
println(num)
}
2. index를 기반으로도 반복할 수 있다
val numbers = arrayOf(1,2,3,4,5)
for(index in numbers.indicies){
println("number at $index is ${numbers[index]}")
}
여기서 indicies는 arrayOf안에 있는 원소들의 index를 의미한다
3. for 내부도 표현하는 방법 여러가지
for(i in 1..3){}
for(i in 0..10 step 2){} // 2씩 건너띄면서 i 증가
for(i in 10 downTo 0 step 2{}
for(i in 1 until 10){}
repeate(10){} // 10번 반복해
val numbers = arrayOf(2,4,6,8,10)
for(index in numbers.indices step 2){
println(numbers[index])
}
//2 6 10 출력됨
while 문 : java와 동일 -> do while도 쓸 수 있다.
var x = 10
while(x>0){
println(x)
x--
}
fun main(){
val items = listOf("사과","오렌지","김태헌")
var index = 0
do{
println("${index}에 있는 item은 ${items[index]}이다.")
index++
}while(index<items.size)
}
Break구문
: 가장 가까이의 반복문을 벗어난다
- Labeled break -> 반복문을 한꺼번에 벗어나고 싶을때 사용
-> 반복문에 label을 붙여주면 된다. label은 label명@을 붙여서 표시해준다.
first@ for(i in 1..4){
second@ for(j in 1..2){
println("i= $i j= $j")
if(i==2)
break@second
}
}
// i= 1 j= 1
// i= 1 j= 2
// i= 2 j= 1
// i= 3 j= 1
// i= 3 j= 2
// i= 4 j= 1
// i= 4 j= 2
각 반복문마다 label 붙여주는 것도 가능하다.
-->(주의) break@first -> 공백이 있으면 안된다.
Continue 구문
: 이 구문을 만나면 아래의 구문 실행 생략하고 제어를 반복문으로 옮기는 경우
- Labeled continue : Labeled break문과 비슷하다 (반복문 사용할때 유용하게 사용된다)
here@ for (i in 1..5){
for(j in 1..4){
if(i==3||j==2)
continue@here
println("i=$i; j=$j")
}
}
Functions 함수
fun 함수(인수1:자료형1, 인수2:자료형2, ...) : 반환자료형 {}
인자 생략 가능,반환값이 없는 경우 Unit 및 생략가능
fun main(){
fun greet(str:String):Unit{
println(str)
}
greet("201811177 김태헌")
}
-> 코틀린에서의 인자는 immutable variable( val에 해당-> 변경 불가 )
반환값이 없는 경우 Unit사용해도 된다.
- 간단한 값을 반환하는 경우 { } 생략 가능
fun main(){
fun greet(str:String):Unit{
println(str)
}
// greet("201811177 김태헌")
fun getName(firstName:String, lastName:String):String = "$firstName $lastName"
println(getName("김","태헌"))
}
디폴트 매개변수
- 매개변수의 값을 입력하지 않으면 디폴트 값을 가지게 된다.
--> 함수 오버로딩과 비슷한 효과를 낼 수 있다
첫번째 인자는 default, 두번째 인자는 새로운 값을 주고싶을때?
--> 매개변수의 이름을 사용하면 매개변수의 위치에 상관없이 사용 가능함
fun main(args:Array<String>){
foo('x',2) //x 2
foo() // a 15
foo('y')// y 15
foo(number=234) // a 234
}
fun foo(letter:Char='a',number:Int = 15){
println("$letter $number")
}
함수의 다양한 종류
infix 함수 : 함수의 중위적 표현 가능
ex> shift 연산자는 일반 함수였는데 연산자처럼 호출이 가능했음
infix함수이면 p.createPyramid(4)라고 해도 되지만 p createPyramid 4 이렇게 호출도 가능
class Structure(){
infix fun createPyramid(rows:Int){
var k=0
for(i in 1..rows){
k=0
for(space in 1..rows-i){
print(" ")
}
while(k!=2*i-1){
print("* ")
++k
}
println()
}
}
}
fun main(){
val p = Structure()
p createPyramid 4 //p.createPyramid(4)와 동일
}
재귀함수(Recursive Function) : 무한 루프에 빠지지 않도록 주의해서 사용
fun factorial(n:Int):Long {
return if(n==1) n.toLong() else n*factorial(n-1)
}
-> 함수가 계속 stack에 쌓여서 stack overflow를 자주 발생시킨다 -> 코틀린에서는 이를 해결하기 위해 꼬리 재귀 함수(Tail Recursive Function)이라는 것이 있다.
꼬리 재귀 함수(Tail Recursive Function)
: 기존의 재귀함수는 모든 재귀 호출이 완료될 때까지는 결과를 얻을 수 없었으나, 꼬리 재귀에서는 계산이 먼저 수행되고, 재귀 호출이 수행되는 구조
- 컴파일러가 stackoverflow가 발생하지 않도록 효율적인 순환 기반의 버전으로 최적화한 것이다.
꼬리 재귀는 '재귀 호출이 끝나면 아무 일도 하지 않고 결과만 바로 반환되도록 하는' 방법이다. 이 방식을 사용하면 이전 함수의 상태를 유지하지도 않고 추가 연산을 하지도 않아서 스택이 넘쳐나는 문제를 해결할 수 있게 된다.
val eps = 1E-15 // 10^-15
tailrec fun findFixPoint(x:Double = 1.0) : Double
= if(Math.abs(x-Math.cos(x))<eps)x else findFixPoint(Math.cos(x))
// 꼬리 재귀함수는 아래처럼 순환구조가 되는 형식으로 만들어진다.
private fun findFixPoint():Double{
var x=1.0
while(true){
val y = Math.cos(x)
if(Math.abs(x-y)<eps) return x
x = Math.cos(x)
}
}
일반 재귀는 값을 받으면, 그 값에 연산을 하고 다른 함수에게 전달을 해줬었다. 하지만 꼬리재귀는 '아무것도 하지 않고' 값을 전달한다.
예제. 꼬리재귀 함수
--> 꼬리 재귀 함수를 사용해서 피보나치 수열의 n번째 항을 반환하는 fibonacci 함수를 만들어주세요.
import java.math.BigInteger
fun main(){
val n=10000
val first = BigInteger("0")
val second = BigInteger("1")
println(fibonacci(n,first,second))
}
fun fibonacci(n:Int,a:BigInteger,b:BigInteger):BigInteger{
return if(n==0) a else fibonacci(n-1,b,a+b)
}
stackoverflow를 해결하려면 순환구조로 바꾸어 줘야 한다.
import java.math.BigInteger
fun main(){
val n=10000
val first = BigInteger("0")
val second = BigInteger("1")
println(fibonacci(n,first,second))
}
tailrec fun fibonacci(n:Int,a:BigInteger,b:BigInteger):BigInteger{
return if(n==0) a else fibonacci(n-1,b,a+b)
}
람다 함수(Lambda)
- Kotlin에서는 함수 반환 자료형 생략도 가능했고 블록도 생략해서 함수를 하나의 변수처럼 표현할 수 있었다.
fun add(x:Int,y:Int):Int{
return x+y
}
fun add2(x:Int,y:Int) = x+y
한번 사용하고 마는 함수들 이름도 필요한가..?
- 람다 함수 : 익명 함수를 간결하게 표현할 수 있는 방법
val add:(Int,Int) ->Int = {x:Int,y:Int->x+y}
val add = {x:Int,y:Int->x+y}
함수가 일반 타입처럼 사용된다.
- 코틀린의 람다식
- 형식 : { 매개변수 -> 함수내용 }
- 람다 함수는 항상 { } 로 감싸서 표현
- 인수 목록을 나열하고 -> 이후에 본문 위치
- 인자는 ( ) 로 감싸지 않음
- 인자는 형식 추론이 가능하므로 타입 생략 가능
- 함수 반환값은 함수 내용의 마지막 표현식(함수 반환값이 있다면)
- 변수에 람다식을 저장하고, 변수를 일반 함수처럼 사용
- 변수에 대입하지 않으면 이후 람다 함수를 사용할 수 없음 -> 정의된 이후에 사용할 수 없다.
-----> 이런 경우
- 람다 함수 뒤에 ()를 추가하여 함수 호출
- Run() 함수에 대입해도 바로 함수가 호출되어 실행
{println("Hello")}()
run{println("World")}
fun main(){
val add = {x:Int,y:Int->x+y} //타입 추론
val add2:(Int,Int)->Int = {x,y -> x+y} //타입 명시적으로 줌
println(add(10,20))
println(add2(10,20))
val add3 = {x:Int,y:Int -> println(x+y)} //반환안하고 출력
add3(10,20)
println({x:Int,y:Int->x+y}(10,20))
}
SAM(Single Abstract Method) 변환
- 추상 메서드 하나를 인수로 사용할 때만 대신 함수 인수 전달
코틀린에서는 추상 메소드 하나를 인수로 사용할 때는 함수를 인수로 전달하면 편하다. 자바로 작성된 메소드가 하나인 인터페이스를 구현할 때는 대신 함수를 작성할 수 있다.
안드로이드에서는 버튼의 클릭 이벤트를 구현할 때 onClick() 추상 메소드만을 가지는 View.OnClickListener 인터페이스를 구현한다.
버튼을 클릭했을 때 실행되는 함수들은 onClickListener라는 interface에 정의되어 있다.
자바에서는 changeBtn 객체에 setOnClickListener라는 함수를 통해서 interface객체를 입력으로 넣어주게 되는데 onClickListener라는 것은 인터페이스이니까 인터페이스를 구현하는 방법은 여러가지가 있다. 여기서는 익명클래스에 객체를 만들어서 사용하는 경우이다. 익명 클래스로 만들어도 클래스의 이름은 없지만 인터페이스이니까 인터페이스에 정의된 abstract method를 구현해 준다.
Java
changeBtn.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v){
changeBtn.setBackgroundColor(Color.CYAN);
}
});
Kotlin
changeBtn.setOnClickListener{
view->changeBtn.setBackgroundColor(Color.CYAN)
}
// 구현되는 인터페이스 내에 추상메서드가 딱 하나만 가지고 있을때 객체 대신 람다함수를 인자로 전달
-> 인터페이스에 추상메서드가 하나일때만 쎈 변환 적용.
view라는 인자도 함수내에서 사용안하면 _ 사용하거나 생략할 수 있다.
changeBtn.setOnClickListener{
_->changeBtn.setBackgroundColor(Color.CYAN)
}
changeBtn.setOnClickListener{
changeBtn.setBackgroundColor(Color.CYAN)
}
인수를 사용해야될 필요가 있으면 it이라는 키워드를 사용하면 된다. it은 생략된 객체를 접근할 때 사용할 수 있다.
changeBtn.setOnClickListener{
it.visibility = View.INVISIBLE
}
람다식의 인수가 하나인 경우는 it(view) 로 인수 접근 ---> 여러개인 경우 안됨
- 인터페이스에서 추상메서드는 하나인데 추상메서드의 인자가 여러개 있는경우?
--> 인자를 나열해주면 된다
Kotlin
changeBtn.setOnTouchListener{
v,event-> // 인자 나열
changeBtn.setBackgroundColor(Color.CYAN) //함수 본문
false //반환
}
- 함수를 인자로 전달했었다
함수도 타입, 값으로도 대입이 된다.
- 함수 타입의 변수 선언 : 함수 타입 변수에서는 리턴을 쓰지 않고, 마지막 줄이 반환됨
val functionType1 : () -> Unit
val functionType2 : (Int) -> Unit
val functionType3 : (Int, String) -> String
functionType1 = { println("greenjoa")}
functionType2 = {age-> println("나이")}
functionType3 = {age,name->println("나이") "나이"}
High - Order Function
- 함수의 인수로 함수나 람다식을 받거나 반환할 수 있는 함수
확장 함수
- 클래스에 새로운 함수를 추가
- 기존 방식은 상속을 통해 새로운 클래스를 만들고, 함수 추가
- 확장함수는 클래스 밖에서 정의된 클래스의 멤버 함수
- 멤버 함수를 오버로딩한 경우 확장 함수가 호출됨
- ex> string의 처음과 마지막 문자 삭제 함수 -> string클래스에 존재하지 않는 함수
fun String.removeFirstLastChar():String = this.substring(1,this.length-1) // String내의 substring함수
예제>
fun calculate(x:Int, y:Int,func:(Int,Int)->Int):Int{
return func(x,y)
}
fun String.removeFirstLastChar():String = this.substring(1,this.length-1)
fun main(){
println(calculate(10,20){x,y->x+y})
println(calculate(10,20){x,y->x*y})
println(calculate(10,20){x,y->if(x>y)x else y})
println("HelloWorld".removeFirstLastChar())
}
'App > Android' 카테고리의 다른 글
[4] Kotlin - 클래스, 컬렉션 (0) | 2022.03.13 |
---|---|
[2] Kotlin (0) | 2022.03.08 |
[1] Android 소개 (0) | 2022.03.08 |
RecyclerView (0) | 2021.08.25 |
Fragment (0) | 2021.08.23 |