본문 바로가기

Study/Kotlin

Kotlin / 문장 제어처리 알아보기

반응형

1. 조건 표현식

비교연산자

이항연산자로 두 항의 크기가 작거나 또는 같음을 비교한다

비교연산의 결과는 논리값 true/false 중 하나가 반환된다. (Boolean)

 

연산자 의미 표현식 메서드 전환
> 크다 a > b a.compareTo(b) > 0
< 작다 a < b a.compareTo(b) < 0
>= 크거나 같다 a >= b a.compareTo(b) >= 0
<= 작거나 같다 a <= b a.compareTo(B) <= 0
== 같다 a == b a?.equals(b) ?: (b === null)
!= 같지 않다 a != b !(a?.equlas(b) ?: (b === null)

포함연산자

특정 범위에 속한 값에 포함 여부를 확인할 때 사용한다.

포함 여부를 결정해서 참과 거짓의 결과를 제공한다.

 

연산자 표현식 상응하는 메서드
in a in b b.contains(a)
!in a !in b !b.contains(a)

- 모든 것이 참인 경우 : all 메서드

- 모든 것이 거짓인 경우 : none 메서드

- 하나라도 참인 경우 : any 메서드

 

논리연산자

특정 비교연산 등을 다시 묶어서 논리값을 판단하는 연산자이다.

 

연산자 의미 표현식 상응하는 메서드
|| 두 조건식이 전부 거짓인 경우만 거짓 (a>b)||(a<c) (a>b)or(a<c)
&& 두 조건식이 전부 참인 경우만 참 (a>b)&&(a<c) (a>b)and(a<c)

동등성

객체의 상태인 값이나 객체의 레퍼런스가 같은지 즉 동일한 객체인지 비교하는 방법이다.

 

- 구조적 동등성(Structural equality) : 동일한 값을 비교 (==)

- 참조적 동등성(Referential equality) : 객체 참조로 비교 (===)

 

- 널러블 연산 (?. / ?:) : 널에는 메서드가 없어서 널이 들어와 메서드를 호출하면 널 예외가 발생한다. -> 널러블 연산을 하면 널이면 그 뒤에 메서드를 처리하지 않는다.

 

  /* 동등성 연산 처리 */
    val a = 100
    val b = 100
    val c = null
    
    println(a == b) // true
    println(a.equals(b)) // true
    
    // 널타입 처리는 널도 체크
    println(a?.equals(b) ?: (b === null)) // true
    // 객체 참조 비교
    println(a === b) // true

    // 널값끼리 비교
    println(c == null) // true
    // 널 객체 참조 비교
    println(c === null) // true
    // 정상적인 값과 널값 비교
    println(a?.equals(c) ?: (c === null)) // false

 

실수는 값과 레퍼런스가 같다.

 

    /* 실수 처리 */
    val d = 0.0
    val e = -0.0
    
    println(d == e) // true
    println(d === e) // true

 

2. 조건문

if조건

 

/* 단순 조건 처리 : 단일표현식 */
    val a = if(10>20) " 성공 " else " 실패 "
    println(a) // 실패

/* 단순 조건 처리 : 코드 블록 */
    val b = if(10>20){
        true
    }else {
        false
    }
    println(" 변수 = $b") // 변수 = false

 

when조건

 

/* when 값 매칭 */
    val cores = Runtime.getRuntime().availableProcessors()

    when(cores){
        1-> println(" 1 core")
        in 2..16 -> println(" $cores Cores")
        else -> println("I want your machine")
    }
    
  // ==> 결과 : 8 cores
  
  
/* when 내부에 지역변수 정의와 함수 반환에 직접 표기 */
  
  fun systemInfoR(): String=
    when(val cores = Runtime.getRuntime().availableProcessors()){
        1-> " 1 core"
        in 2..16 -> " $cores Cores"
        else -> "I want your machine"
    }
    
  println(systemInfoR()) // 8 cores

 

if문 대용으로 when을 사용할 수 있다.

 

    val number = 10
    if(number < 0){
        println("음수")
    }else if(number == 0){
        println("영")
    }else if(number % 2 == 0){
        println("짝수")
    }else{
        println("홀수")
    }
    
    when{
        number < 0 -> println("음수")
        number == 0 -> println("영")
        number % 2 == 0-> println("짝수")
        else -> println("홀수")
    }
    
    // --> 짝수

 

예외

특정 기능의 처리가 맞지 않을 경우 프로그램 내에서 예외를 던질 수 있고, 컴파일러나 JVM 내의 처리에서도 예외를 발생시킬 수 있다.

예외가 발생되면 현재의 프로세스가 중단된다.

이를 방지하고 정상적인 조치를 하려면 예외를 잡고 내부적으로 정상 처리를 추가해야한다.

 

예외 처리 (try catch finally)

 

    try{
        add()
    }catch(e: Exception){
        println(e)
    }finally{
        println("정상적으로 처리")
    }
    // 결과 --> 정상적으로 처리

 

예외 던지기 ( throw catch)

예외를 코드에서 발생시키는 것이다.

 

    try{
        throw Exception("예외 발생")
    }catch(e:Exception){
        println(e)
    }finally{
        println("정상적으로 처리")
    }
    // 결과 -->
    // java.lang.Exception: 예외 발생
    // 정상적으로 처리

 

위의 코드에서는 throw로 예외를 발생시켰고, 예외 메세지를 그대로 출력하는 것으로 끝났다.

finally는 예외의 발생과 상관없이 필수적으로 실행된다.

 

3. 순환 표현

범위

- 범위 연산자(..) : 두 수, 두 문자, 두 문자열 사이에 지정해서 두 항목을 포함한 범위 객체 생성

- rangeTo 메서드 : 범위 연산자와 동일

- until 메서드 : 범위 연산자와 동일하지만 마지막 항목이 포함되지 않는다

- downTo 메서드 : 역방향 범위를 만들때 사용

- step 메서드 : 범위의 간격을 처리하는 메서드. 실행하면 범위 객체를 진행 객체로 변환한다.

- first, last, step 속성 : 범위와 진행 내의 첫 번째, 마지막, 간격 정보를 관리하는 속성

 

    val range1 = 1..10 // 순방향
    val range2 = 1.rangeTo(10) // 순방향
    val range3 = 1.until(10) // 순방향, 마지막 미포함
    val range4 = 10.downTo(1) // 역방향
 
    println("range1 first = ${range1.first} / last = ${range1.last}") // range1 first = 1 / last = 10
    println("range2 first = ${range2.first} / last = ${range2.last}") // range2 first = 1 / last = 10
    println("range3 first = ${range3.first} / last = ${range3.last}") // range3 first = 1 / last = 9
    println("range4 first = ${range4.first} / last = ${range4.last}") // range4 first = 10 / last = 1
    
    val range5 = 1.rangeTo(10).step(2) // 범위를 만든 후에 step 메서드로 진행 객체로 변환

    println("range5 first = ${range5.first} / last = ${range5.last} / step = ${range5.step}")
    //range5 first = 1 / last = 9 / step = 2

 

for 순환

범위와 진행을 사용해 반복 순환을 처리한다.

 

    for(i in 1..5){
        print("$i, ") // 1, 2, 3, 4, 5
    }
    for(i in 1.rangeTo(5)){
        print("$i, ") // 1, 2, 3, 4, 5
    }
    for(i in 1.until(5)){
        print("$i, ") // 1, 2, 3, 4, 
    }

 

순환을 반복하는 continue와 순환을 중단하는 break가 있다.

 

    for(i in 1..10){
        if(i % 2 == 0){
            println("continue $i")
            continue // 가장 가까운 순환으로 이동
        }

        if(i == 7){
            println("break $i ")
            break // 가장 가까운 순환을 종료
        }
    }

    // --> 결과
    //continue 2
    //continue 4
    //continue 6
    //break 7

 

위의 코드에서는 첫 번째 조건은 짝수인지 확인, 두 번째 조건은 특정 값과 같은지 확인했다.

 

순환문 내부에 내포된 순환문이 있을 경우 전체 순환을 종료하려면 레이블을 사용해서 외부 순환문까지 빠져나올 수 있도록 처리한다.

순환문 앞에 레이블명 + @ 를 붙이고 break + @ 레이블명을 지정한다.

 

    loop@ for(i in 1..3){
        for(j in 1..5){
            if(j==3){
                println(" 내포 순환 ")
                break@loop
            }
            println(" for 순환 $j")
        }
    }
    
    // --> 결과
    // for 순환 1
    // for 순환 2
    // 내포 순환

 

while/do while 순환

특정 조건이 만족할 때까지 순환을 처리한다.

- while 순환 : 조건식을 만족할 때까지 순환한다.

- do while 순환 : 먼저 한 번 실행한 후에 조건을 확인하고 참일 경우에 순환한다.

조건식 결과가 항상 참이면 무한 순환을 처리하므로 순환의 마지막 처리점은 항상 내부에 로직으로 처리해야 한다.

 

    // while
    var n = 0
    while(n < 3){
        println(n)
        n++
    }
    // --> 결과
    //0
    //1
    //2

    // do while
    var m = 0
    do{
        println(m)
        m++
    }while (m<3)

    // --> 결과
    //0
    //1
    //2

 

반복자

여러 개의 원소를 가진 자료형인 범위, 배열, 리스트 등은 반복형(Iterable) 클래스이다.

이 반복형을 iterator 메서드로 반복자(Iterator) 클래스의 객체로 변환할 수 있다.

 

반복형과 반복자의 차이

- 반복형을 반복자로 자료형을 변환하면 내부의 원소를 순환할 수 있는 메서드가 추가된다.

 

반복형 -> 반복자로 변환

- 반복자는 현재 상태를 조회하는 hasNext 메서드, 다음 원소를 조회하는 next 메서드가 추가된다

- 반복자의 순환은 hasNext 메서드로 상태를 확인하고 next 메서드로 원소를 조회하면서 처리한다

- 반복자의 특징은 한번 모든 원소를 조회하면 다시 객체를 생성해서 사용한다

 

범위반복자

범위를 반복자로 만들고 변환해서 내부/외부 순환을 처리할 수 있다.

 

    val i = 1..10
    val c = 'a'..'z'

    val ilter = i.iterator() // 정수 범위를 반복자로 처리
    val clter = c.iterator() // 문자 범위를 반복자로 처리

    ilter.forEach{print("$it,")} // 내부 순환
    println()
    for(i in clter)print("$i, ") // 외부 순환

    // --> 결과
    //1,2,3,4,5,6,7,8,9,10,
    //a, b, c, d, e, f, g, h, i, j, k, l, m, n, o, p, q, r, s, t, u, v, w, x, y, z,

 

범위 객체를 반복자 객체로 변환 후에 반복자에서 제공하는 hasNext, next 메서드로 순환을 처리할 수 있다.

 

    val r = ('a'..'c').iterator() // 반복자 처리
    while(r.hasNext()){ // 외부 순환
        println(r.next()) // 반복자 내의 원소를 하나씩 조화
    }

    ('a'..'c').forEach(::println) // 내부 순환
    
    // --> 결과
    //a
    //b
    //c
    //a
    //b
    //c

 

========

 

when >> 코틀린 특정버전 이상부터 when 쓸때 else를 무조건 써야하게 변경되었다.

enum, seald 경우 모든 케이스를 구현하면 else를 안써도 된다. 

 

map은 iterable 확장함수 

 

Nothing, Unit 차이?

Nothing은 타입이 없다.

값을 반환하지 않을때는 보통 Unit을 쓴다.

Generic 할때 * 사용할때 >> Generic 타입을 Nothing으로 바꿀때도 있긴 하다

 

Unit -> Void 같이 아무것도 반환 안할때. 

Nothing -> Error를 던질때 많이 사용한다. 종료될 상황이 아닐때. 

 

함수참조 ::, 메소드 레퍼런스 (p143 참고)

- 변수에 할당할 수 있다.

함수 자체도 일급객체로 보기때문에 변수에 선언하거나 리턴할 수 있다.

함수를 전달할때 (return 등등) :: 많이 사용한다.

클래스를 만들때 변수로 함수를 전달할때가 있다. 

 

fun main(){
	val numbers = listOf(
    	Number(1,2, ::plus)
    	Number(1,2, ::minus)
    	Number(1,2, ::times)
    	Number(1,2, ::div)
    )
    
    numbers.forEach{
    	println(it.operator(it.a,it.b)) // 함수가 어떻게 동작하는지 모르게 사용 가능
    }
    
    val name: String? = ""
    name?.let{
    	println(it)
    }
    name?.let(::println) // 이렇게 사용도 가능
}

fun getOperator(operator: String): (Int, Int) -> Int{
	return when(operator){
    	"+" -> ::plus
    	"+" -> ::plus
    	"+" -> ::plus
    	"+" -> ::plus
    }
}

class Number(
	val a:Int,
    val b:Int,
    val operator: (Int,Int) -> Int,
)

fun plus(a: Int, b: Int): Int{
	return a+b
}

fun minus(a: Int, b: Int): Int{
	return a-b
}

fun times(a: Int, b: Int): Int{
	return a*b
}

fun div(a: Int, b: Int): Int{
	return a/b
}

 

디컴파일 방법

Tools > Kotlin > Show Kotlin Bytecode > Decompile