Kotlin in Action - Chapter2) Kotlin basics

7hong13·2023년 6월 18일
0

Kotlin in Action

목록 보기
2/3
* Contents

Part1: Introducing Kotlin
	Chapter1: Kotlin: what and why
    Chapter2: Kotlin basics ✔
    Chapter3: Defining and calling functions
    Chapter4: Classes, objects, and interfaces
    Chapter5: Programming with lambdas
    Chapter6: The Kotlin type system
    
Part2: Embracing Kotlin
	Chapter7: Operator overloading and other conventions
    Chapter8: Higher-order functions
    Chapter9: Generics
    Chapter10: Annotations and reflection
    Chapter11: DSL construction

기본 요소: 함수와 변수

함수

코틀린에서 함수는 아래와 같은 형태로 정의한다.

함수가 expression을 반환할 경우, 함수의 중괄호 및 return 문을 생략해 표현할 수 있다.

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

중괄호로 감싼 함수의 경우 block body를, expression을 바로 반환하는 함수의 경우 expression body를 갖는다고 한다.

statements vs expressions
1) statements: 값을 반환하지 않는다.
2) expressions: 값을 반환한다. 따라서 return 값으로 활용 가능하다. (코틀린에서 if는 expression이다.)

변수

변수를 나타내는 키워드는 다음과 같다.

  • val: value의 약자. 한번 변수를 할당하면 재할당할 수 없다(immutable reference).
  • var: variable의 약자. 변수의 재할당이 가능하다(mutable reference).

이 때, val이 참조하는 객체 자체가 불변(immutable)한 것은 아니다.
해당 객체가 담는 값은 변할 수 있다.

val languages = listOf("Java") // languages 자체는 immutable reference이다.
languages.add("Kotlin") // 하지만 languages가 가리키는 객체는 mutable 하다.

문자열 템플릿

$, ${ }를 사용해 문자열 보간(string interpolation)에 활용할 수 있다.
해당 기능을 코틀린에선 문자열 템플릿(string templates)이라고 부른다.

fun main(args: Array<String>) {
	val name = if (args.isNotEmpty()) args[0] else "kotlin"
    println("Hello $name!")
}

// 변수 뿐만 아니라 expression 자체도 문자열 템플릿으로 활용 가능하다.
fun main(args: Array<String>) {
    println("Hello ${if (args.isNotEmpty()) args[0] else "kotlin"}!")
}

클래스 및 프로퍼티

클래스

클래스 선언시 생성자, getter, setter 등이 필요한 자바와 달리, 코틀린의 클래스 선언은 간단하다.

class Person(val name: String)

위와 같이 본문 코드 없이 data만 갖는 클래스를 value objects라고 부른다.

프로퍼티

 /* Java */
public class Person {
	// field
	private final String name;
    
    // 생성자
	public Person(String name) {
		this.name = name;
	}
    
    // getter(accessor)
	public String getName() {
		return name;
	} 
}

자바에서는 값을 필드(field)에 저장하고, 해당 필드에 접근할 수 있는 getter 및 setter를 제공한다.
그리고 이러한 필드 및 접근자를 모두 합쳐 프로퍼티라 지칭한다.

하지만 코틀린은 변수 선언시 이러한 필드 및 접근자가 한번에 생성된다.

class Person(
	val name: String, // field + getter
    var isMarried: Boolean, // field + getter + setter
)

따라서 코틀린에선 변수 자체를 프로퍼티라 지칭한다.
변수를 선언하면 필드 및 접근자가 자동 생성된다.

  • val: read-only 프로퍼티. 필드 및 getter를 자동 내포한다.
  • var: 수정 가능한 프로퍼티. 필드, getter 및 setter를 자동 내포한다.

custom 접근자

custom 프로퍼티 접근자를 선언할 수도 있다.
아래는 custom getter의 예시이며, 프로퍼티에 접근할 때마다 getter를 호출하면서 값을 새로 계산한다.

class Rectangle(val height: Int, val width: Int) {
	val isSquare: Boolean
		get() {
    		return height == width
		}
}

enums, when

enums

enum class Color {
    RED, ORANGE, YELLOW, GREEN, BLUE, INDIGO, VIOLET
}

자바와 마찬가지로 코틀린에서도 enum class를 선언할 수 있다.

enumsoft keyword로 예약어이지만, class 앞에 쓰이는 게 아니면 변수명, 함수명 등으로 활용 가능하다.
반면 class는 변수명으로 활용이 불가해 clazz, aClass와 같은 변수명으로 대체한다.

이러한 열거형 클래스는 단순 값의 목록이 아니다.
자바처럼 프로퍼티 및 함수도 내부에 선언 가능하다.

enum class Color(val r: Int, val g: Int, val b: Int) {
	RED(255, 0, 0), ORANGE(255, 165, 0), YELLOW(255, 255, 0), GREEN(0, 255, 0), BLUE(0, 0, 255), INDIGO(75, 0, 130), VIOLET(238, 130, 238);
	
    fun rgb() = (r * 256 + g) * 256 + b
}
>>> println(Color.BLUE.rgb())
255

when

enum class와 많이 쓰이는 식은 when이다.
자바의 switch와 기능적으로 유사하지만, switch는 statement인 반면 when은 expression이다.

fun getWarmth(color: Color) = when(color) {
    Color.RED, Color.ORANGE, Color.YELLOW -> "warm"
    Color.GREEN -> "neutral"
    Color.BLUE, Color.INDIGO, Color.VIOLET -> "cold"
}

// 인자값 없이도 사용 가능하다.
fun mixOptimized(c1: Color, c2: Color) =
    when {
        (c1 == RED && c2 == YELLOW) ||
        (c1 == YELLOW && c2 == RED) -> ORANGE
        (c1 == YELLOW && c2 == BLUE) ||
        (c1 == BLUE && c2 == YELLOW) -> GREEN
        (c1 == BLUE && c2 == VIOLET) ||
        (c1 == VIOLET && c2 == BLUE) -> INDIGO
        else -> throw Exception("Dirty color")
    }

when 사용시 smart casts가 적용되어 명시적인 타입 캐스팅이 불필요하다.

interface Expr
class Num(val value: Int) : Expr
class Sum(val left: Expr, val right: Expr) : Expr

fun eval(e: Expr): Int =
    when (e) {
    	is Num -> e.value // e가 Num으로 smart cast 된다.
    	is Sum -> eval(e.right) + eval(e.left) // e가 Sum으로 smart cast 된다.
    	else -> throw IllegalArgumentException("Unknown expression")
}

반복문

여타 언어와 마찬가지로 코틀린에서 반복문은 while, do-while, for을 사용한다.
그 중 for 문을 활용해 반복문을 더 편하게 다룰 수 있다.

Ranges

코틀린에선 구간을 다루는 여러 문법(.., until, downTo)을 제공한다.

// 아래 구간은 inclusive range, 즉 0과 10을 포함한다.
val oneToTen = 1..10
for (i in oneToTen) {
	println(i)
}

// 변수 할당 없이도 활용 가능하다.
for (i in 1..10) {
	println(i)
}

val list = listOf(1, 2, 3) // size: 3
// 마지막 값을 포함하지 않는다. (0부터 1씩 증가해 2까지 도달.)
for (i in 0 until list.size) {
	println(list[i])
}

// 10부터 1씩 감소해 0까지 도달한다.
for (i in 10 downTo 0) {
	println(i)
}

Progression

구간을 1이 아닌 특정 단위의 값을 기준으로 돌 수 있을 때, 해당 구간을 progression이라고 부른다.
이 때 함께 쓰이는 문법이 step이다.

// 0, 2, 4, 6, 8, 10을 방문한다.
for (i in 0..10 step 2) {
	println(i)
}

collection과 반복문

반복문은 collection 내부 아이템들을 체크할 때 흔히 사용된다.
이 때 활용되는 키워드가 in이다.

val list = listOf("1", "2", "3")
// item은 "1", "2", "3"을 순차적으로 가리킨다.
for (item in list) {
	println(item) 
}

// withIndex()를 통해 index를 함께 반환받을 수 있다.
for ((index, element) in list.withIndex()) {
	println("$index" $element")
}

특정 범위 혹은 collection 내에 아이템 포함 여부를 확인할 때도 in을 사용한다.

println(c in 'a'..'z') // true, a <= c && c <= z와 동일
println("1" !in listOf("1", "2")) // false

예외 처리

코틀린의 기본 exception handling은 자바와 유사하다.
다만 throw가 expression이라는 점에서 자바와 차이가 있다.

if (percentage !in 0..100) {
    throw IllegalArgumentException("A percentage value must be between 0 and 100: $percentage")
}

// 조건에 맞을 경우 percentage를 숫자로 초기화하며,
// 그렇지 않을 경우 예외를 던진다.
val percentage = if (number in 0..100) {
	number
} else {
	throw IllegalArgumentException("A percentage value must be between 0 and 100: $number")
}

try, catch, finally


fun readNumber(reader: BufferedReader): Int? {
    try {
        val line = reader.readLine()
        return Integer.parseInt(line)
    } catch (e: NumberFormatException) {
        return null
	} finally {
        reader.close()
    }
}

자바와 유사하게 try-catch-finally 구문을 통해 예외 처리를 한다.
하지만 자바와 달리 코틀린은 checked exceptionsunchecked exceptions를 구분짓지 않는다.
따라서 어떤 exception이 해당 함수에서 발생할지 명시적으로 지정하지 않는다.

자바의 checked exceptions 및 unchecked exceptions

  • checked exceptions: 컴파일 타임에 확인되어 반드시 에러 처리를 해줘야 하는 exceptions.
  • unchecked exceptions: 런타임에 발생 가능하며 에러 처리가 강제되지 않는 exceptions.

try 또한 expression이며 리턴값으로 지정할 수 있다.

fun readNumber(reader: BufferedReader) {
    val number = try {
        Integer.parseInt(reader.readLine()) // 예외가 발생하지 않으면 해당 값을 반환한다.
    } catch (e: NumberFormatException) {
		null // exception 발생시 null을 반환한다.
	}
    println(number)
}
profile
안드로이드/코틀린

0개의 댓글