728x90

https://kotlinlang.org/docs/control-flow.html

 

Conditions and loops | Kotlin

 

kotlinlang.org

 

Conditions and loops

If expression

코틀린에서는 if는 표현식이다.(값을 반환할 수 있다.)

그렇기에 코틀린에서는 삼항 연산자(condition ? then : else)가 없다.

If가 이 역할을 하기 때문이다.

fun main(){
    val a = 1
    val b = 2
    
    var max = 0
    
    max = if (a > b) a else b
    
    val maxOr100 = if (a > 100) a else if(b > 100) b else 100
}

 

When expression

When은 다양한 곳을 분기할 수 있는 조건식을 정의한다.

C언어에서의 switch라고 생각하면 된다.

fun main(){
    val x = 0
    
    when(x){
        0 -> println("x is 0")
        1 -> println("x is 1")
        2 -> println("x is 2")
        else -> println("x is neither 1 nor 2")
    }
}

When은 만족하는 condition이 있을 때까지 순서대로 매칭해 간다.

else는 아무것에도 해당하지 않을 때를 정의한다.

 

만약 When이 표현식으로 사용된다면 Else branch는 필수적이다.

그래도 컴파일러가 모든 경우의 수를 커버하고 있다고 알 수 있으면, Else는 생략 가능하다.

enum class Bit{
    ZERO, ONE
}

fun main(){
    val a = Bit.ZERO
    
    when(a){
        Bit.ZERO -> println("Bit.ONE")
        Bit.ONE -> println("Bit.TWO")
    }
}

 

값이 범위 안에 속하는지도 확인할 수 있다.

fun main(){
    val a = 5
    
    when(a){
        in 1..10 -> println(a)
        else -> println("else")
    }
}

 

여기서도 is로 타입을 검사하면 스마트캐스팅이 되어 추가적으로 검사할 필요가 없다.

fun hasPrefix(x: Any) = when(x) {
    is String -> x.startsWith("prefix")
    else -> false
}

 

For loops

for는 iterator를 순환합니다.

for (item in collection) print(item)

 

숫자를 반복하고 싶다면, range expression을 사용한다.

for (i in 1..3) {
    println(i)
}
for (i in 6 downTo 0 step 2) {
    println(i)
}

 

While loops

while과 do-while은 조건이 만족할 때까지 계속 ㅁ반복한다.

while: 조건을 체크하고 그것이 참이면 실행한다. 이것을 반복한다.

do-while: 일단 블록을 실행하고 조건을 체크한다.

while (x > 0) {
    x--
}

do {
    val y = myFunc()
} while (y != null) // y is visible here!

 

Returns and jumps

Returns and jumps

fun main(){
    val a:Int? = null

    val b = (a ?: return).toString()

    println(b)
}

return은 본인이 속한 가장 가까운 함수를 리턴한다

break은 본인이 속한 가장 가까운 loop를 정지한다.

continue는 루프의 다음 스텝으로 넘어간다.

 

Break and continue labels

fun main(){
    loop@ for (i in 1..10) {
        for (j in 1..10) {
            if(i != j) break@loop
            println("$i $j")
        }
    }
}

코틀린에서는 표현식에 label을 붙일 수 있다.

라벨은 @로 끝나며, abc@와 같은 형태가 된다.

break와 continue를 사용하여 해당 부분으로 jump 할 수 있다.

 

Return to labels

fun main(){
    listOf(1, 2, 3, 4, 5).forEach lit@{
        if (it == 3) return@main
        print(it)
    }
    print(" done with explicit label")
}

이렇게 return에서도 라벨을 사용하여 리턴할 수 있다.

 

Exceptions

예외 처리는 크게 2가지로 나뉜다.

Throwing exceptions: 문제가 발생 했을 때 알린다.

Catching exceptions: 문제가 발생 했을 때 어떤 식으로 처리할 것인지 다룬다.

 

이런 예외 처리는 코드를 더 예측가능하게 도와줄 수 있다.

 

Throw exceptions

throw IllegalArgumentException()

throw 키워드를 사용하여 Exception을 발생시킨다.

Exception이 발생하는 것은 런타임 중에 예상하지 못한 문제가 발생했다는 것을 알리는 것이다.

 

Throw Exceptions with precondition functions

코틀린은 조건을 체크하고 에러를 발생시켜주는 precondition function이 존재한다.

Precondition function Use case Exception thrown
require() 유저의 input이 valid 한지 확인 IllegalArgumentException
check() Object 혹은 변수의 상태가 valid 한지 확인 IllegalStateException
error() 잘못된 상태나 조건을 알린다. IllegalStateException

 

  • require() function
fun getIndices(count: Int): List<Int> {
    require(count >= 0) { "Count must be non-negative. You set count to $count." }
    return List(count) { it + 1 }
}
  • check() function
fun main() {
    var someState: String? = null

    fun getStateValue(): String {

        val state = checkNotNull(someState) { "State must be set beforehand!" }
        check(state.isNotEmpty()) { "State must be non-empty!" }
        return state
    }
    // If you uncomment the line below then the program fails with IllegalStateException
    // getStateValue()

    someState = ""

    // If you uncomment the line below then the program fails with IllegalStateException
    // getStateValue() 
    someState = "non-empty-state"

    // This prints "non-empty-state"
    println(getStateValue())
}

check에서 condition이 false라면 exception을 throw한다.

  • error() function
class User(val name: String, val role: String)

fun processUserRole(user: User) {
    when (user.role) {
        "admin" -> println("${user.name} is an admin.")
        "editor" -> println("${user.name} is an editor.")
        "viewer" -> println("${user.name} is a viewer.")
        else -> error("Undefined role: ${user.role}")
    }
}

fun main() {
    // This works as expected
    val user1 = User("Alice", "admin")
    processUserRole(user1)
    // Alice is an admin.

    // This throws an IllegalStateException
    val user2 = User("Bob", "guest")
    processUserRole(user2)
}

그냥 exception을 발생시켜버린다.

 

Handle exceptions using try-catch blocks

exception이 발생하면, 그래도 프로그램을 이어가기 위해 exception을 handling 해야 한다.

그럴 때 try-catch를 사용한다.

try {
    // exception을 발생시키는 코드
} catch (e: YourException) {
    // exception을 handling 하는 코드
}

 

또한 if와 마찬가지로 try-catch는 expression으로 사용 할 수 있다.

fun main() {
    val num: Int = try {
    	10/0
    } catch (e: ArithmeticException) {
        -1
    }
    println("Result: $num")
}

 

또, 하나의 try에 대하여 여러개의 Catch를 작성 할 수 있다.

 

The finally block

finally block은 무조건 실행되는 부븐이다.

이 finally block은 try에서 exception이 발생하더라도 무조건 실행된다.

그렇기에 finally는 네트워크나 파일과 같은 리소스를 Close 하는 것에서 중요하다.

try {
    // Exception이 발생 할 수 있는 코드
}
catch (e: YourException) {
    // Exception을 handling 하는 코드
}
finally {
    // 무조건 실행되어야 하는 코드
}

이 finally 블록은 catch 블록을 작성하지 않아도 작성할 수 있다.

 

 

'백엔드 > 코틀린' 카테고리의 다른 글

Idioms  (1) 2024.10.01
Basic syntax  (0) 2024.09.28
코틀린 시작  (0) 2024.09.28
728x90

https://kotlinlang.org/docs/idioms.html

 

Idioms | Kotlin

 

kotlinlang.org

 

Create DTOs (POJOs/POCOs)

코틀린에서는 Data만 가지는 클래스를 Data class라고 따로 정의할 수 있다.

data class User(val name: String, val email: String)

 

여기에서 POJOs, POCOs의 개념이 나오는데

POJO(Plain Old Java Object):  자바 모델이나 기능, 프레임워크 등을 따르지 않은 자바 오브젝트를 지칭하는 말이다.

POCO(Plain Old CLR Object)는 자바가 아닌 닷넨 오브젝트들을 말한다.

Default values for function parameters

fun foo(a: Int = 0, b: String = "") { ... }

 

함수를 선언 할 때, 함수에 기본 값을 줄 수 있다.

foo(1, "123) 이렇게 호출 할 수도 있지만, foo(1) 이렇게 호출 할 수도 있는 것이다.

이런 경우에는 미리 선언해둔 기본값이 들어가게 된다.

 

Filter a list

fun main(){
    val list = listOf(1, 2, 3, 4, 5)

    val filteredList1 = list.filter{
        x -> x > 3
    }

    println(filteredList1)

    val filteredList2 = list.filter{it > 3}

    println(filteredList2)
}

 

리스트에서 조건 filter를 통해, 원하는 값들을 추출 할 수 있다.

이 때 람다 뿐 만 아니라, it을 사용해서도 filter 할 수 있다.

 

Check the presence of an element in a collection

fun main(){

    val emailsList = listOf("trust1204@gmail.com", "trust1204@naver.com")

    if ("trust1204@gmail.com" in emailsList) {
        println("HI trust1204@gmail.com")
    }
}

컬렉션에서 in을 사용하여 해당 값이 컬렉션에 존재하는지 확인 할 수 있다.

String interpolation

fun main(){
    
    val name = "seungkyu"
    
    println("hi $name")
}

$을 통해 문자열 안에 문자열을 끼워 넣을 수 있다.

 

Instance checks

open class A

class B: A()

fun main(){

    val a = A()

    when(a){
        is B -> println("It is B")
        is A -> println("It is A")
        else -> println("I dont know")
    }

}

 

is [Class name]을 통해 해당 클래스의 인스턴스인지 검사 할 수 있다.

 

Read-only list

val list = listOf(1, 2, 3)

이렇게 읽을 수만 있는 리스트를 생성할 수 있다.

이 데이터는 add, put 과 같은 함수로 추가 혹은 수정이 불가능하다. 

Read-only map

val map = mapOf("a" to 1, "b" to 2, "c" to 3)

위와 마찬가지로 읽을 수만 있는 map을 생성할 수 있다.

to로 key와 value를 mapping 한다.

 

Access a map entry

fun main(){
    val map = mapOf("a" to 1, "b" to 2, "c" to 3)

    println(map["a"])

}

이런 식으로 map에 접근하여 데이터를 읽을 수 있다.

 

Traverse a map or a list of pairs

fun main(){
    val map = mapOf("a" to 1, "b" to 2, "c" to 3)

    for((k,v) in map){
        println("$k = $v")
    }

}

map의 key와 value를 반복문으로 한 번에 읽어올 수 있다.

 

Iterate over a range

for (i in 1..10) { ... }  //includes 10
for (i in 1..<10) { ... } //not include 10
for (x in 2..10 step 2) { ... }
for (x in 10 downTo 1) { ... }
(1..10).forEach { ... }

..과 <을 이용해서 iterate의 범위를 정할 수 있다.

Lazy property

fun main(){
    val p: String by lazy { 
        if(0 > 1)
            "Hello World"
        else
            "Hi world"
    }
}

val 타입에서 by lazy로 실행된 이후에 값을 할당 할 수 있다. 

Create a singleton

Object Seungkyu{
	val name = "seungkyu"
}

Object 키워드를 사용하여 싱글톤 객체를 만들 수 있다.

Use inline value classes for type-safe values

@JvmInline
value class UserId(private val id: String)

value 클래스로 만들면, 클래스가 데이터를 직접 참조하기 때문에 객체 생성에 대한 cost를 아낄 수 있다고 한다.

 

Instantiate an abstract class

abstract class InstantiateAnAbstractClass {
    abstract fun doSomething()
    abstract fun sleep()
}

fun main(){
    val myObject = object : InstantiateAnAbstractClass() {
        override fun doSomething() {
            println("It is doSomething")
        }

        override fun sleep() {
            println("It is sleeping")
        }
    }

    myObject.doSomething()
}

이렇게 추상 클래스를 바로 만들 수 있다.

If-not-null shorthand

fun main(){
    val a: String? = "seungkyu!!"

    println(a?.length)
}

null 가능 변수에 ?. 을 붙여서 만약 null이 아니면 가져올 값을 지정할 수 있다.

 

If-not-null-else shorthand

fun main(){
    val a: String? = null

    println(a?.length ?: "hello!!")
}

null이 아닐 때 뿐만 아니라, null 일 때 가져올 값을 지정할 수 있다.

만약 ?: 앞의 값이 null이라면 뒤에 있는 값으로 가져오게 된다.

 

Execute a statement if null

fun main(){
    val values= mapOf<String, Int>()

    println(values["a"] ?: throw NullPointerException())
}

 

?: 를 통해 null 일 때 Exception을 throw 할 수 있다.

 

Get first item of a possibly empty collection

fun main(){
    val emails = listOf<String>()

    val mainEmail = emails.firstOrNull()

    println(mainEmail)
}

이런 식으로 first item을 가져오거나 null을 가져 올 수 있다.

 

Execute if not null

fun main(){
    val value1 = "String"
    val value2:String? = null

    value1?.let {
        println(it)
    }

    value2?.let {
        println(it)
    }
}

?.let을 이용하여 해당 데이터가 null이 아닐 때 실행할 값을 실행할 수 있다.

 

Return on when statement

fun main(){
    println(myFunc(3))
}

fun myFunc(value: Int): String{
    return when(value){
        1 -> "one"
        2 -> "two"
        3 -> "three"
        else -> "no"
    }
}

 

이렇게 함수 Return 부분에서 when을 사용하여 값을 return 할 수 있다.

이 때, 어떠한 경우에도 값이 return 될 수 있도록 꼭 when을 지정해주어야 한다.

 

try-catch expression

fun main(){
    val result = try{
        throw NullPointerException()
    }catch (e: NullPointerException){
        1
    }

    println(result)
}

값을 할당 할 때에도, try-catch Expression을 사용하여 값을 대입할 수 있다.

 

Builder-style usage of methods that return Unit

fun main(){
    arrayOfMinusOnes(10).forEach { print("$it ") }
}

fun arrayOfMinusOnes(size: Int): IntArray {
    return IntArray(size).apply { fill(-1) }
}

return 할 때에도, 자바의 Builder 스타일을 사용해 값을 반환 할 수 있다.

 

Single-expression functions

fun theResult1() = 1111

fun theResult2(value: Int) = when(value){
    1 -> "one"
    2 -> "two"
    else -> "other"
}

fun main(){
    println(theResult1())
    println(theResult2(1))
}

함수를 이렇게 하나의 표현식으로도 작성할 수 있다.

 

Call multiple methods on an object instance (with)

class Bird{
    fun fly(){
        println("Bird fly")
    }

    fun go(x: Int, y: Int){
        println("Bird go $x $y")
    }
}

fun main(){
    val myBird = Bird()

    with(myBird){
        fly()

        for(i in 1..3){
            go(i,i)
        }
    }
}

with를 사용해서 하나의 인스턴스에서 다양한 함수들을 호출 할 수 있다.

 

Generic function that requires the generic type information

inline fun <reified T: Any> hello(value: String): T =  value as T

fun main() {
    
}

타입 정보를 같이 받는 Generic Function을 구현 할 수도 있다.

 

Mark code as incomplete (TODO)

fun main(){
    println("Hello Kotlin")
    TODO("아직이요")
}

TODO를 이용하여 아직 작성하지 않은 코드라고 알릴 수 있다.

 

자동으로 코드를 삽입해주어서, 실행이 가능하다.

물론 TODO()의 코드에 도착하면 그때는 에러가 발생한다.

'백엔드 > 코틀린' 카테고리의 다른 글

Concepts:Control flow  (1) 2024.10.05
Basic syntax  (0) 2024.09.28
코틀린 시작  (0) 2024.09.28
728x90

https://kotlinlang.org/docs/basic-syntax.html

 

Basic syntax | Kotlin

 

kotlinlang.org

 

Package definition and imports

패키지는 소스파일 가장 위에 명시한다.

package seungkyu

class PackageDefinitionAndImports {

}

 

코틀린에서는 디렉토리와 패키지를 똑같이 맞출 필요는 없다고 한다.

 

Program entry point

코틀린 애플리케이션이 시작하는 부분은 main 함수 부분이다.

package seungkyu

fun main(args: Array<String>) {
   println("Hello, world!")

    println(args[0])
}

 

만약 애플리케이션 실행에서 매개변수를 받고 싶으면, 메인 함수에 저렇게 문자열 배열로 받을 수 있다.

 

print 함수는 매개변수를 standard output에 출력한다.

package seungkyu

fun main(){
    print("seungkyu")
    
    println("seungkyu")
}

println은 print에서 한 line을 차지하여 출력하는 함수이다.

다음 출력은 해당 출력의 다음줄부터 시작된다.

Read from the standard input

readln은 standard input으로부터 읽어오는 함수이다.

이 함수는 한 줄 전체를 읽어온다.

package seungkyu

fun main(){
    print("Enter your name: ")

    val name = readln()

    print("Your name: ")
    println(name)

    println("is contain new line? : ${name.contains("\n")}")
}

 

엔터키는 제외하고 읽어오는 것을 볼 수 있다.

Functions

아래는 2개의 Int를 매개변수로 받고 Int를 반환하는 함수이다.

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

 

아래와 같이 명시적이라면 return과 괄호를 생략 할 수 있다.

fun sum2(a: Int, b: Int) = a + b

 

만약 return이 아무것도 반환하지 않는다면 Unit으로 return type을 준다.

fun printSum(a: Int, b: Int): Unit {
    println("sum of $a and $b is ${a + b}")
}

 

이 때 Unit은 생략이 가능하다.

fun printSum(a: Int, b: Int) {
    println("sum of $a and $b is ${a + b}")
}

 

Variables

코틀린은 val, var 키워드로 변수 선언이 가능하며, 그 뒤에 변수의 이름을 적어준다.

 

val은 할당이 한 번만 일어나는 변수를 선언 할 때 사용하며, 후에 값이 변하지 않는다.

선언 이후에는 read-only이다.

val x: Int = 5

 

그에 비해 var은 선언 이후에도 값을 변경 할 수 있다.

fun main(){
    var x: Int = 5
    x += 1
}

 

위에서는 변수의 type을 :로 같이 명시해 준 것을 볼 수 있는데, 사실 코틀린은 변수의 타입을 추정이 가능하기 때문에 변수 타입은 생략을 해주어도 된다.

 

var x = 5

 

아래와 같이 변수의 타입까지만 선언해두고 값은 나중에 할당하는 것도 가능하다.

fun main(){
    val x = 5
    
    val c: Int
    c = 3
}

 

자바와는 다르게, 변수를 클래스 밖인 top level에 선언도 가능하다.

val value = 0

fun main(){

    println(value)
}

 

Creating classes and instances

늘 그렇듯, 클래스를 정의하기 위해서는 class 키워드를 사용한다.

class Shape

 

클래스의 속성들은 선언이나 body에 작성할 수 있다.

class Rectangle(val height: Double, val length: Double){
    val perimeter = (height + length) * 2
}

 

이 때, 클래스 선언부에 작성된 매개변수를 바탕으로 기본 생성자가 만들어진다.

class Rectangle(val height: Double, val length: Double){
    val perimeter = (height + length) * 2
}

fun main(){
    val rectangle = Rectangle(10.0, 20.0)
    println("The Perimeter is ${rectangle.perimeter}")
}

 

클래스 간의 상속은 : 을 사용하면 된다.

클래스는 기본적으로 상속이 불가능한 final 상태이지만, 앞에 open 키워드를 붙여주면 상속 가능한 클래스가 된다.

 

open class Shape

class Rectangle(val height: Double, val length: Double): Shape() {
    val perimeter = (height + length) * 2
}

Comments

주석은 //와 /* */를 사용해서 작성 할 수 있다.

//주석입니다.

/* 이것은
주석 블럭입니다 */

 

String templates

기본적으로 String은 ""이런 형태로 있을 것이다.

자바에서는 문자열들을 붙일 때 +를 사용했지만 코틀린에서는 $을 사용하여 문자열 안에 넣을 수 있다.

val mystring = "${1+2}, but now is $a"

 

Conditional expressions

조건식도 If를 사용한다.

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

 

코틀린에서는 3항 연산자가 없으며, 대신 if 또한 표현식으로 사용된다.

fun max_result(a: Int, b: Int) = if (a > b) a else b

 

for loop

다른 언어와 마찬가지로 for를 사용해서 반복문을 작성한다.

 

fun main(){
    val items = listOf("apple", "banana", "kiwi")
    for (item in items) {
        println(item)
    }
}

 

배열에서 in으로 각각의 item을 가져올 수 있다.

while loop

이것도 마찬가지로 while의 괄호 안에 있는 조건이 true일 동안 반복하게 된다.

fun main(){
    val items = listOf("apple", "banana", "kiwi")
    var index = 0
    while (index < items.size) {
        println("item at $index is ${items[index]}")
        index++
    }
}

 

when expression

C언어에서의 switch이다.

C언어와는 다르게 다양한 타입으로 분기할 수 있다.

 

또한 이것도 표현식이기 때문에 when을 사용하여 변수에 값을 할당 할 수 있다.

이 때는 else로 모두 해당하지 않는 경우를 꼭 지정해주어야 한다.

fun main(args: Array<String>) {
    val obj = "hi"
    
    val result = when (obj) {
        "hi" -> "hello"
        "hello" -> "hi"
        else -> ""
    }
}

 

Ranges

in 연산자를 사용하여 숫자가 범위 내에 있는지 확인한다.

fun main(){
    val x = 10
    val y = 9

    if(x in 1..y+1){
        println("fits in range")
    }
}

 

반복문에서도 이렇게 사용할 수 있다.

for (x in 1..5){
	print(x)
}

 

downTo와 step을 사용하여 증가인지 감소인지와 간격을 지정해 줄  수 있다.

fun main(){
    for (x in 1..10 step 2) {
        print(x)
    }
    println()
    for (x in 9 downTo 0 step 3) {
        print(x)
    }
}

 

 

Collections

collection을 반복하는 방법

for (item in items){
	println(item)
}

 

map을 사용하여 해당 collection을 반복 할 수도 있다.

    val fruits = listOf("banana", "avocado", "apple", "kiwifruit")
    fruits
    	//a로 시작하는 단어 찾기
        .filter { it.startsWith("a") }
        //정렬
        .sortedBy { it }
        //대문자로 변환
        .map { it.uppercase() }
        //각각을 출력
        .forEach { println(it) }

 

Nullable values and null checks

코틀린에서는 null을 허용할 때 명시를 해주어야 한다.

타입의 마지막에 ?를 붙여주면 nullable 타입이다.

fun main(){
    val a: Int? = null
    val b: Int = 1
}

 

Type checks and automatic casts

is는 인스턴스가 해당 타입이 맞는지 확인하는 연산자이다.

그리고 is로 검사가 끝나면 해당 타입으로 자동 캐스팅해준다.

fun getStringLength(obj: Any): Int? {
    if (obj is String) {
        // `obj` is automatically cast to `String` in this branch
        return obj.length
    }

    // `obj` is still of type `Any` outside of the type-checked branch
    return null
}

fun main() {
    fun printLength(obj: Any) {
        println("Getting the length of '$obj'. Result: ${getStringLength(obj) ?: "Error: The object is not a string"} ")
    }
    printLength("Incomprehensibilities")
    printLength(1000)
    printLength(listOf(Any()))
}

 

'백엔드 > 코틀린' 카테고리의 다른 글

Concepts:Control flow  (1) 2024.10.05
Idioms  (1) 2024.10.01
코틀린 시작  (0) 2024.09.28
728x90

거의 1년간 코틀린으로 스프링부트를 사용하고 있었지만, 사실 나의 코틀린은 자바에서 단지 널 안정성만 추가된 언어였다.

 

물론 널 안정성 만으로도 자바에서 코틀린으로 넘어 올 가치는 있지만, 그래도 더 나은 개발자가 되기 위해서는 코루틴까지 자유롭게 사용할 수 있어야 한다는 생각이 들었다.

 

코틀린은 아직 한국어 자료가 많이 없기 때문에 이 참에 공식문서를 참고하여 코틀린을 깊게 공부해보려 한다.

 

https://kotlinlang.org/docs/home.html

 

Kotlin Docs | Kotlin

 

kotlinlang.org

 

'백엔드 > 코틀린' 카테고리의 다른 글

Concepts:Control flow  (1) 2024.10.05
Idioms  (1) 2024.10.01
Basic syntax  (0) 2024.09.28

+ Recent posts