[코틀린 뽀개기] 기본 신텍스

miak·2021년 11월 14일
1

코틀린 뽀개기

목록 보기
4/4
post-thumbnail

코틀린 공식 홈페이지의 Basic syntax을 번역한 것입니다.
(일부 생략이 있을 수 있습니다.)

오역, 오타를 포함한 자유로운 의견 항상 감사드립니다 :)

예제와 함께 기본 신텍스들을 살펴봅니다. 각 섹션에는 심화 내용으로 이어지는 링크가 첨부되어 있습니다.
JetBrains 아카데미에서 제공하는 무료 코틀린 기초과정을 통해 코틀린 핵심 기능을 배울 수도 있습니다.

패키지 정의와 import

패키지는 코드 최상단에 정의되어야 합니다.
디렉토리 경로와 패키지를 일치시킬 필요는 없습니다. 즉 소스파일이 임의의 경로에 존재해도 괜찮습니다.

package my.demo

import kotlin.text.*

// ...

Package

프로그램 시작점

main 함수에서 어플리케이션이 시작합니다. main 함수는 String 배열 파라미터를 받을 수 있습니다.

fun main() {
    println("Hello world!")
}

fun main(args: Array<String>) {
    println(args.contentToString())
}

출력하기

print를 사용해 내용을 출력할 수 있습니다. println는 내용 마지막에 줄바꿈을 추가합니다.

함수

두 정수를 파라미터로 받고 정수를 반환하는 함수는 아래와 같이 표현됩니다.

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

함수는 표현식(expression)으로 작성할 수도 있습니다. 이때 반환값은 타입추론이 됩니다.

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

반환값이 의미없는 경우 Unit 타입을 사용하며, 이는 생략할 수 있습니다.

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

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

Function

변수

val을 사용해 불변 지역 변수를 선언할 수 있습니다. 이는 오직 한 번만 값을 할당할 수 있으며 이후 변경할 수 없습니다. 선언과 할당을 동시에 하는 경우(1) 타입 추론이 가능해 타입을 생략할 수 있지만, 변수 선언과 할당 사이에 지연이 있는 경우(2) 타입을 명시해줘야 합니다.
변경 가능한 변수(3)는 var를 사용합니다.

// 1. 변수 선언과 할당을 동시에 하는 경우
val a: Int = 1 
val b = 2 

// 2. 변수 선언 후 할당하는 경우
val c: Int
c = 3

// 3.
var x = 5
x += 1

코드 상단에 선언할 수도 있습니다.

val PI = 3.14
var x = 0

fun incrementX() { 
    x += 1 
}

Class Properties

클래스와 객체 (Instance) 생성

해당 챕터는 의역된 부분이 있습니다.

class를 사용해 클래스를 선언합니다. 클래스 속성 (property)은 선언부나 내부에 정의할 수 있습니다.

class Rectangle(var height: Double, var length: Double) { // 선언부
    var perimeter = (height + length) * 2 // 내부
}

클래스 선언에 포함된 속성들에 아래와 같이 접근할 수 있습니다.

val rectangle = Rectangle(5.0, 2.0)
println("The perimeter is ${rectangle.perimeter}")

:을 통해 클래스를 상속할 수 있습니다. 클래스는 final 속성이 기본값이므로 상속 가능한 클래스로 만드려면 open 키워드를 명시해야 합니다.

open class Shape

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

Classes
Objects and Instances

주석

// 한줄

/* 여
   러
   줄 */
   
/* 주석
/* 중첩도 *⁠/
허용됩니다 */

문서화를 위한 코틀린 주석 신택스

문자열 템플릿

var a = 1
val s1 = "a is $a"  // 단순 변수의 경우 중괄호 생략 가능
// 단, 점(.), 쉼표(,), 공백 전까지 변수로 처리하는 것 주의

a = 2
val s2 = "${s1.replace("is", "was")}, but now is $a" // 문자열에 표현식 삽입 가능

String Templates

if/when/for/while

본문 대신 링크로 연결된 Conditions and loops 내용을 담고 있습니다.

if 표현식

코틀린에서 if는 표현식으로, 값을 반환합니다. 덕분에 if 문으로도 충분한 표현이 가능하므로 삼항표현자 (ternary operator) (condition ? then : else)가 없습니다.

var max: Int
if (a > b) {
    max = a
} else {
    max = b
}

// As expression
val max = if (a > b) a else b

if 문의 브랜치는 코드블록일 수 있으며, 블록 안의 최종 표현식이 블록의 반환값이 됩니다. 따라서 만일 if 표현식을 사용해 값을 반환하거나 변수에 할당하는 경우 else 브랜치가 반드시 필요합니다.

val max = if (a > b) {
    print("Choose a")
    a
} else {
    print("Choose b")
    b
}

when 표현식

다른 언어의 switch와 유사한 when은 여러 브랜치에 대한 조건 표현식을 정의합니다.

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

인수(x)를 조건을 만족할 때까지 각 브랜치에 순차적으로 대입합니다.
when은 문장 (statement)일 수도, 표현식 (expression)일 수도 있습니다. 표현식일 경우 브랜치에서 처음 매칭하는 값이 반환값이 됩니다. 문장으로 사용되는 경우 각 브랜치의 반환값은 무시됩니. if와 같이 각 브랜치는 코드블록일 수 있으며, 블록의 마지막 표현식이 반환값이 됩니다.
else 브랜치는 조건을 만족하는 브랜치가 없을 때 사용됩니다. when을 표현식으로 사용하고자 하는 경우 else 브랜치가 반드시 필요합니다. 다만 컴파일러가 조건 브랜치로 모든 경우를 포함할 수 있다고 판단하는 경우는 else를 정의하지 않을 수 있습니다. 대표적으로 enum 클래스sealed 클래스의 서브타입이 있습니다.

enum class Bit {
  ZERO, ONE
}

val numericValue = when (getRandomBit()) {
    Bit.ZERO -> 0
    Bit.ONE -> 1
}

이 외 아래와 같은 사용이 가능합니다.

when (x) {
    // 쉼표를 사용해 공통 케이스 표현
    0, 1 -> print("x == 0 or x == 1")
    
    // range
    in validNumbers -> print("x is valid")
    in 2..10 -> print("x is in the range")
    !in 10..20 -> print("x is outside the range")
    
    // 브랜치 조건에 표현식 사용
    s.toInt() -> print("s encodes x")
    
    // 타입 체크 - 컴파일러의 smart cast를 통해 클라스 매서드와 속성에도 접근할 수 있습니다.
    is String -> x.startsWith("prefix")
    
    else -> print("otherwise")
}

인수(x)가 없는 whenif-else if와 같이 사용할 수 있습니다.

when {
    x.isOdd() -> print("x is odd")
    y.isEven() -> print("y is even")
    else -> print("x+y is odd")
}

아래와 같이 when의 구문 (subject)에서 변수를 선언할 수 있습니다. when subject에서 선언된 변수는 when 내부에서만 사용할 수 있습니다.

fun Request.getBody() =
        when (val response = executeRequest()) {
            is Success -> response.body
            is HttpError -> throw HttpException(response.status)
        }

for loop

for 루프는 반복자 (iterator)를 제공하는 모든 것을 순회할 수 있습니다. Iterator를 제공할 경우 반드시 operator 표시가 된 아래의 멤버 혹은 extension function이 있어야 합니다.

  • Iterator<>를 반환하는 iterator()
  • next()
  • Boolean을 반환하는 hasNext()
for (item in collection) print(item)
// or
for (item: Int in ints) {
	print(item)
}

Range나 배열을 순회하는 for문은 index-based 루프로 컴파일되어 iterator를 생성하지 않습니다.

// Range
for (i in 6 downTo 0 step 2) println(i)

// 인덱스와 함께 순회 (1)
for (i in array.indices) {
    println(array[i])
}

// 인덱스와 함께 순회 (2)
for ((index, value) in array.withIndex()) {
    println("the element at $index is $value")
}

while loop

whiledo-while문은 조건이 만족되는 한 계속해서 본문을 반복합니다. 둘의 차이는 조건을 확인하는 시점으로, do-while은 조건과 무관하게 본문이 1회 실행되는 것을 보장합니다.

  • while : 조건 확인 > 본문 실행 > 조건 확인 > ...
  • do-while : 본문 실행 > 조건 확인 > 본문 실행 > 조건 확인...
while (x > 0) {
    x--
}

do {
    val y = retrieveData()
} while (y != null) // 변수 y를 사용할 수 있음

Break, continue

Returns and jumps

Range

in 또는 !in 연산자를 사용해 range 포함 여부를 확인할 수 있습니다.

if (x in 1..10) println("x is in range")

val list = listOf("a", "b", "c")
if (list.size !in list.indices) {
    println("list size is out of valid list indices range")
}

// Range, progression iteration
for (x in 1..5) print(x)
for (x in 1..10 step 2) print(x)
for (x in 9 downTo 0 step 3) print(x)

Ranges and progressions

컬렉션

아래와 같은 활용이 가능합니다. 자세한 개념은 Collections overview를 참고하세요

// Iteration
for (item in itemCollection) println(item)

// in 연산자를 사용해 컬렉션에 포함 여부 확인
when {
    "orange" in itemCollection -> println("juicy")
    "apple" in itemCollection -> println("apple is fine too")
}

// 람다식 사용
val fruits = listOf("banana", "avocado", "apple", "kiwifruit")
fruits
    .filter { it.startsWith("a") }	// ["avocado", "apple"]
    .sortedBy { it }			// ["apple", "avocado"]
    .map { it.uppercase() } 		// ["APPLE", "AVOCADO"]

Nullable 변수와 null 체크

null을 허용하는 경우 반드시 ?를 뒤에 표시해야합니다.

// null을 반환할 수도 있는 함수
fun parseInt(str: String): Int? {

}

null-safety

타입 체크와 자동 형변환 (automatic cast)

is 연산자로 표현식의 타입을 확인할 수 있습니다. 불변 지역변수 혹은 불변 속성이 특정 타입으로 확인되면 cast를 명시하지 않아도 됩니다.

fun getStringLength(obj: Any): Int? {
    if (obj is String) {
        // 해당 브랜치에서 `String`으로 자동 형변환
        return obj.length
    }

    // 타입체크 한 브랜치 밖에서는 여전히 `Any` 타입
    return null
}

fun getStringLength(obj: Any): Int? {
    if (obj !is String) return null

    // 해댱 브랜치에서 `String`으로 자동 형변환
    return obj.length
}

fun getStringLength(obj: Any): Int? {
    // `obj is String`이후로 `String`으로 자동 형변환
    if (obj is String && obj.length > 0) return obj.length

    return null
}

Classes
Type casts

profile
서버 개발자

0개의 댓글