본문 바로가기
코틀린/개발자를 위한 코틀린 프로그래밍

[chapter03] 문장 제어처리 알아보기

by 측면삼각근 2023. 7. 24.
728x90
반응형

들어가기 전에

본 포스팅은 개발자를 위한 코틀린 프로그래밍 의 chapter단위로 공부하고, 정리, 부족한 내용의 추가 학습내용을 정리 하는 블로깅입니다.

이전 포스팅 ⬇️

[코틀린] 02) 코틀린에서는 모든 것이 객체이다.

 

[코틀린] 02) 코틀린에서는 모든 것이 객체이다.

코틀린에서는 모든 것을 객체로 본다. 내부에서 JVM의 기본형으로 변환되어 사용되지만 코틀린에서는 기본형이 없다.(참조형만 존재) // `javaClass`로 java클래스를, `javaClass.kotlin`으로 코틀린 클래

messycode.tistory.com

 


01 조건표현식 알아보기

비교연산자

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

연산자 표현식 메서드 전환
> a > b a.comparedTo(b) > 0
< a < b a.comparedTo(b) < 0
>= a >= b a.comparedTo(b) >= 0
<= a <= b a.comparedTo(b) <= 0
== a == b a?.equals(b) ?: (b === null)
!= a != b !a?.equals(b) ?: (b === null)

포함연산자

특정 범위에 속한 값에 포함 여부를 확인한다. 역시 참과 거짓의 결과를 제공

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

포함연산자는 비교 연산자로서 참과 거짓의 결과 외에 여러 활용법이 존재한다. [코틀린 공식문서 (keywords and operators)참조]

더보기

1. for loop에서의 활용

// iterator
for (item in collection) print(item)

//  range expression
for (item: Int in ints) {
    println(item)
}

2. contains 메서드에 상응하는 range, collection, entity에 존재하는지 확인

val count = 5;
if (count in 1..10 && count !in 5..7) {
    print("1과 10 사이에 속하지만, 5와 7사이에는 속하지 않는 숫자")
}

val array: IntArray = intArrayOf(1, 2, 3, 4, 5)
if (count in array) {
    print("숫자는 array에 포함되어있음")
}

3. When 표현식에서의 사용

when (x) {
    1 -> print("x == 1")
    2 -> print("x == 2")
    else -> {
        print("x is neither 1 nor 2")
    }
}

4. Generic의 반 공변성(Contravariance)으로 사용

fun fill(dest: Array<in String>, value: String) { ... }
// Array<in String> 는 자바의 Array<? super String> 와 동일

any, all, none 처리

여러 원소를 가진 배열이나 리스트 등에 특정 원소의 값이 있는지 확인, 논리값으로 처리하는 메서드

  • 모든것이 참인 경우 all
  • 모든것이 거짓인 경우 none
  • 하나라도 참인경우 any
val height = 46
val list = listOf(1, 2, null)

list.any({ it == null }) //true
list.all({ it == null }) //false
list.none({ it == null }) //false

논리연산자

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

동등성

  • 구조적 동등성: 동일한 값을 비교 (==)
  • 참조적 동등성: 객체 참조로 비교(===)
val a = 100
val b = 100
val c:Int? = null

println(a == b) // true
println(a.equals(b)) // true
println(a?.equals(b)) // true
println(a === b) // true

println(c == null) // true (널 값 끼리의 비교)
println(c === null) // true(널 객체 참조 비교)
println(c?.equals(a)) // null => 메서드를 아예 처리하지 않으므로 null
널러블 연산(?., ?:)
널에는 메서드가 없어서, 널이 들어와 메서드를 호출하면 널 예외가 발생한다. 그래서 널러블 연산을 하면 널이면 그 뒤에는 메서드를 처리하지 않는다.

실수처리

  • 숫자나 문자열은 항상 값과 레퍼런스가 동일해야 하지만, 성능을 위해 필요할 때마다 여러 개를 만들어 처리 할 수 있다. 그래서 값이 같지만 레퍼런서는 같지 않을 수 있으므로 값만 비교하는것을 권장한다.
    • (🤔 뭔소리지; 이해못함. 뭘 여러개 만들어 처리한다는거지...?)
val f:Float = 0.0F
val f2:Float = -0.0F
val i1: Int = 0
println(f == f2) // true
println(f === f2) // true

// println(i1 == f) (에러; 자료형이 다르면 비교할수 없음)
// println(i1 === f) (상동)
더보기

이해가 잘 되지 않아서 자바로 컴파일 해보았다.

위 코틀린 코드는 아래 자바코드로 변환되는데, primitive type은 비교연산자로 비교하고 있다.

float f = 0.0F;
float f2 = -0.0F;

boolean var3 = f == f2;
System.out.println(var3);
var3 = f == f2;
System.out.println(var3);

더욱 더 책의 내용이 이해되지 않아서 패스

함수

함수도 정의되면 하나의 객체가 만들어진다. 그래서 함수 객체도 참조적 동등성을 비교 할 수 있다.

fun add(x: Int, y: Int) = x + y
val addVal = ::add //함수 참조를 사용하여 레퍼런스를 가져올 수 있다.

println(::add.hashCode() == addVal.hashCode())  //true
println(addVal == ::add)  //true
println(addVal === ::add) // false 다른객체이므로

02 조건문 알아보기

코틀린 언어에서는 일반적인 조건을 처리하는 if와 패턴 매칭을 하는 when을 제공한다. 둘 다 문장 처리를 만들 수 있지만, 실제는 문장이 아니라 표현식, 즉 값을 반환한다.

if조건

if문은 표현식이 될 수 있다.

fun maxOf(a: Int, b: Int): Int {
  if (a > b) {
    return a
  } else {
    return b
  }
}

// 위 maxOf function은 아래와 같다.
fun maxOf(a: Int, b: Int) =
if (a > b) {
  a
} else {
  b
}


// 또한 아래와도 동일히 표현 할 수 있다.(one-line)
fun maxOf(a: Int, b: Int) = if (a > b) a else b

When 조건

When의 패턴매칭 방법

  • 단일 패턴매칭: 특정 값(정수, 문자열)이나 특정 패턴(자료형 체크, 포함관계)을 하나만 처리
  • 복합 패턴매칭: 여러 종류의 패턴을 하나의 구문에서 처리
  • 패턴이 없는 경우: if문 처럼 임의의 패턴을 지정해 처리
val cores = Runtimes.getRuntime().availableProcessors() //노트북 코어 수 읽기

when(cores){  //특정 값의 패턴 점검
  1 -> " 1 core"
  in 2...16 -> " $cores Cores"
  else -> "I want yout machine" // 패턴 불일치 조건 표시
}

아래처럼 변수를 when의 괄호에 정의하여 사용할 수도 있다.

fun systemInfoR():String = when(val cores = Runtimes.getRuntime().availableProcessors()){
  1 -> " 1 core"
  in 2...16 -> " $cores Cores"
  else -> "I want yout machine" // 패턴 불일치 조건 표시
}

if문 대용으로 사용 예시

val number = 10

if (number < 0) {
    println("Number is negative")
} else if (number == 0) {
    println("Number is zero")
} else if (number % 2 == 0) {
    println("Number is positive and even")
} else{
    println("Number is positive and odd")
}

// 위 코드는 아래와 동일하다

when{
    number < 0 -> println("Number is negative")
    number == 0 -> println("Number is zero")
    number % 2 == 0 -> println("Number is positive and even")
    else -> println("Number is positive and odd")
}

예외

예외도 표현식이다. 예외 처리를 변수에 할당해 처리하면 결괏값이 변수에 할당되는 것을 알 수 있다.

val x = try {
  throw Exception("예외");
} catch(e: Exception){
  200
} finally {
  300
}
// x = 200

03 순환 표현 알아보기

범위

범위/진행 연산자와 메서드

  • 범위연산자(..): 두 수, 두 문자, 두 만자열 사이에 지정해서 두 항목을 포함한 범위 객체 생성
  • rangeTo: 범위 연산자와 동일한 메서드
  • until: 범위 연산자와 동일. 마지막 항목이 포함되지 않음
  • downTo: 역방향 범위 생성
  • step: 범위 간격 처리. 실행시 범위 객체를 진행 객체로 변환함
  • first, last, step: 범위와 진행 내의 첫번째, 마지막, 간격 정보를 관리하는 속성
val range1 = 1..10
val range2 = 1.rangeTo(10)
println(range1 == range2) // true
println(range1) // 1..10
/*
range1.first => 1 , range1.last => 10
*/

val range3 = 1.until(10)
println(range3) // 1..9

val range4 = 10.downTo(1)
println(range4) // 10 downTo 1 step 1

val range5 = 1..10 step 2
println(range5) // 1..9 step 2
// last가 9이므로, 1..10이 아닌, 1..9가 된다.
// 🌟step메서드를 사용할 경우 마지막 값이 step을 처리하는 값으로 변경된다.

순환

순방향, 역방향, 문자 범위 순환

//순방향 순환
for (i in 1..10 step 2) {
    print("$i ")
}
// 1 3 5 7 9
println()

//역방향 순환
for (i in 10 downTo 1 step 2) {
    print("$i ")
}
// 10 8 6 4 2

// 문자 범위 순환
for (c in 'a'..'e') {
    print("$c ")
}
println()
// a b c d e

// 문자열 범위 역순환
for (c in 'e' downTo 'a') {
    print("$c ")
}
println()
// e d c b a

순환문 레이블 빠져나오기

순환문 앞에 [레이블명]@를 붙이고, break@[레이블명] 을 지정하면 지정하여 종료 할 수 있다.
break문 단독 사용시 다른 언어와 동일히 가장 가까운 순환문 종료.

loop@ for (i in 1..3) {
    for (j in 1..5) {
        if (j == 3) {
            println("순환 종료")
            break@loop
        }
        println("j: $j")
    }
}
/*
j: 1
j: 2
전체 for문 종료
*/

반복자(iterator)

여러 개의 원소를 가진 범위, 배열, 리스트 등은 반복형(Iterable) 클래스이며 이를 객체로 만들어도 반복형 객체이다. 이 반복형을 iterator클래스의 객체로 변환할 수 있다.

iterable -> iterator 변환시 내부의 원소를 순환 할 수 있는 메서드가 추가된다.

  • iterable내의 iterator메서드 실행시 iterator클래스의 객체로 변환된다.
  • iterator는 hasNext, next메서드를 가지고 있다.
  • iterator는 모든 원소를 조회하면 다시 객체를 생성해서 사용한다.
// 내부순환
val iIter = (1..10).iterator()
iIter.forEach { print("$it ") }
println()
// 1 2 3 4 5 6 7 8 9 10 

('h'..'j').forEach(::println) 
/*
h
i
j
*/


// 외부순환
val cIter = ('A'..'Z').iterator()
for (i in cIter) print("$i ")
println()
// 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 

// 외부순환
val r = ('a'..'c').iterator()
while (r.hasNext()) { 
print("${r.next()} ")
}
println()
// a b c

 

반응형