ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • 안드로이드 앱 개발을 위한 Kotlin 기초
    IT 생활/안드로이드 앱 개발 2020. 10. 19. 00:42

    안녕하세요.

    안드로이드 앱 개발을 막 시작한 초짜 류똥구입니다.

    앞선 포스팅에서 안드로이드 개발 환경을 준비한 뒤에 "Hello World"를 출력하는 앱을 만들어 봤습니다.

    그 때 안드로이드 스튜디오에서 프로젝트를 생성하는 과정에서 다음과 같이 사용할 언어를 선택할 때 기본 설정인 Kotlin으로 만들었습니다.

    안드로이드 프로젝트 설정 화면

    그래서 이번 포스팅에서는 Kotlin에 대해서 설명하는 시간을 가져보려고 합니다.


    Kotlin

    개발을 좀 해보신 분들이면 들어봤을 JetBrains에서 만든 언어로 Java와 상호 운용이 가능합니다.

    다시 말해서 Kotlin으로 작성 중인 코드에서 Java를 사용하여 바로 사용이 가능합니다.

    Java가 JVM (Java Virtual Machine)에서 동작하듯이 Kotlin도 JVM에서 동작하게 됩니다.

    (Kotlin에서 Java를 지원하기 위해서 인 것 같습니다.)

     

    Kotlin의 가장 큰 장점은 간결한 문법으로 Java 대비 적은 량의 코드로 프로그램을 작성할 수 있는 것 입니다.

    기존의 Java 개발자들의 생산성을 증대 시키면서 이주하기 쉽게 만든 언어라고 생각하면 되겠습니다.

     

    구글에서는 그동안 안드로이드 개발에서 Java를 사용하다가 Kotlin도 공식 언어로 지정하고 있으며, Kotlin에 힘을 실어주고 있습니다. 그래서 안드로이드 스튜디오에서 프로젝트 생성할 때, 사용 언어로 Kotlin이 기본 설정되어 있었던 것 입니다.

     

    본 글은 Kotlin에 대해서 자세히 다루는 것을 목적으로 하지 않기 때문에, Kotlin에 대한 설명은 이 정도만 하도록 하겠습니다. 개인적으로 Kotlin에 관심이 있으신 분은 Kotlin의 공식 사이트 (영문)에 방문해 보시기 바랍니다.

     

    Kotlin Programming Language

     

    kotlinlang.org

     

    Kotlin 문법

    본 글에서 설명하는 Kotlin의 문법은 Kotlin 공식 사이트의 "Basic Syntax" 내용을 번역한 것 입니다.

     

    Basic Syntax - Kotlin Programming Language

     

    kotlinlang.org

    Package definition (패키지 선언)

    소스코드 최상단에 소스코드가 속한 패키지의 이름을 선언해 줍니다.

    package my.demo
    
    // 이하 소스 코드

     

    Package import

    import 키워드를 이용하여 소스 코드 내에서 사용할 패키지를 지정해 줍니다.

    다음은 kotlin에 내장되어 있는 text 패키지를 import한 예제입니다.

    package my.demo
    
    import kotlin.text.*
    
    // 소스 코드

    참고로 kotlin.text.*는 문자열과 정규식을 처리하기 위한 패키지이며 Kotlin 소스코드에서는 default로 import 됩니다.

    또한, import 키워드를 통해서는 패키지뿐만 아니라 함수 등도 import 할 수 있습니다.

     

    Functions (함수)

    함수를 선언하기 위해서는 fun 키워드를 사용하며, 함수는 다음과 같은 형식으로 작성하면 됩니다.

    fun 함수이름(매개변수_이름: 자료형): 반환_자료형 {
        // 함수 내용
        return 반환값
    }

    함수를 지칭할 이름 이후에 함수 내에서 사용할 매개변수를 선언해 줍니다.

    매개변수를 여러 개 사용할 경우에는 콤마(,)를 이용하여 구분하여 줍니다.

    fun 함수이름(매개변수_이름1: 자료형, 매개변수_이름2: 자료형): 반환_자료형 {
        // 함수 내용
        return 반환값
    }

    함수가 종료할 때, 함수를 호출 한 곳으로 반환할 값은 return 키워드로 지정할 수 있으며 반환할 값의 자료형은 매개변수 이후에 선언해 줍니다.

    만약, 반환할 값이 없다면 다음과 같이 반환값과 자료형을 생략할 수 있습니다.

    fun 함수이름(매개변수_이름1: 자료형, 매개변수_이름2: 자료형){
        // 함수 내용
    }

    함수 선언 시 반환값이 없다는 것을 명시적으로 알려주고자 하려면 다음과 같이 작성하면 됩니다.

    fun 함수이름(매개변수_이름1: 자료형, 매개변수_이름2: 자료형): Unit {
        // 함수 내용
    }

    참고로, 함수의 내용이 간단한 수식이면 다음과 같이 축약하여 작성이 가능합니다.

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

     

    Main function

    프로그램을 시작할 때 시작 시점이 필요한데, 일반적으로 main함수가 프로그램의 시작 지점이 됩니다.

    main 함수는 다른 함수와 동일하며 이름을 main으로 해서 다음과 같이 작성합니다.

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

     

    Variables (변수)

    변수는 이름 그대로 변할 수 있는 것을 의미하며 val 키워드를 이용하여 선언할 수 있습니다.

    fun main() {
        val a: Int = 1  // 선언과 동시에 값 설정
        val b = 2   // 변수 b의 타입이 `Int`로 추론되어 설정됨
        val c: Int  // 값 설정이 없을 때는 데이터 타입을 명시해야 함
        c = 3       // 이미 선언된 변수에 값 설정
        println("a = $a, b = $b, c = $c")
    }

    c나 java와 같이 변수의 영역이 지정됩니다. 즉, {} 내에 선언된 변수는 {} 안에서만 {}을 벗어나면 접근할 수 없게 됩니다.

    {} 밖에서 선언된 변수는 {}안에서도 참조할 수 있습니다.

    // 최상위 영역에 선언된 변수
    val PI = 3.14
    var x = 0    
    
    fun incrementX() { 
        x += 1 // 상위 영역에 선어된 변수 참조 가능
    }
    
    fun main() {
        println("x = $x; PI = $PI") // 상위 영역에 선어된 변수 참조 가능
        incrementX()
        println("incrementX()")
        println("x = $x; PI = $PI")
    }

     

    Comments (주석)

    주석은 사람이 읽을 수 있는 언어로 작성된 프로그램 코드가 컴퓨터가 이해가능한 언어로 변경될 때 포함이 되지 않는 내용을 의미합니다.

    프로그램 작성자가 필요한 내용을 코드 상에 기입해 놓을 때 사용합니다.

    대부분의 최신 언어들과 같이, 코틀린도 단행 주석(single-line comment)과 다행 주석(multi-line comment)를 지원합니다.

    // This is an end-of-line comment
    
    /* This is a block comment
       on multiple lines. */
       
    /* The comment starts here
    /* contains a nested comment */     
    and ends here. */

    단행 주석은 //를 이용하며, //가 있는 부분부터 시작하여 행의 마지막 까지를 주석으로 처리합니다.

    다행 주석은 /* */를 이용하며, 한 줄 이상을 포함할 수 있고 다행 주석은 중첩하여 사용이 가능합니다.

     

    String template (문자열 형식)

    문자열을 형식화 할 때 사용하는 것으로 문자열 내에서 $를 이용하여 객체에 접근할 수 있습니다.

    fun main() {
        var a = 1
        val s1 = "a is $a" // 위에서 선언한 a의 값을 참조합니다.
    
        a = 2
        val s2 = "${s1.replace("is", "was")}, but now is $a"  // 문자열 s1에 replac를 사용
        println(s2)  // 출력 : a was 1, but now is 2
    }

     

    conditional expressions (조건 연산)

    다른 언어들과 같은 조건 연산자를 사용할 수 있으며 if 문도 하나의 연산으로 사용할 수 있습니다.

    fun maxOf(a: Int, b: Int): Int {
        if (a > b) {   // if문에 비교 연산자 사용
            return a
        } else {
            return b
        }
    }
    
    fun maxOf(a: Int, b: Int) = if (a > b) a else b   // if문을 연산자 처럼 사용

     

    Nullable values and null checks

    null 값을 가질 수 있는 경우에는 nullable인 것을 명시적으로 표시해 줘야합니다. nullable은 ?를 이용하여 표시하여 줍니다.

    fun parseInt(str: String): Int? {  // 반환값이 Int이거나 null임을 명시함
        return str.toIntOrNull()  // 입력받은 문자열이 숫자면 숫자로 반환하고 숫자가 아니면 null 반환
    }
    
    fun printProduct(arg1: String, arg2: String) {
        val x = parseInt(arg1)
        val y = parseInt(arg2)
    
        if (x != null && y != null) {
        	// 명시적으로 x,y가 null이 아닌것을 확인. x,y는 non-nullable로 형변환 됨
            println(x * y)
        }
        else {
            println("'$arg1' or '$arg2' is not a number")
        }    
    }
    
    
    fun main() {
        printProduct("6", "7")  // 출력 : 42
        printProduct("a", "7")  // 출력 : 'a' or '7' is not a number
        printProduct("a", "b")  // 출력 : 'a' or 'b' is not a number
    }

     

    Type checks and automatic casts (자료형 확인과 자동 형변환)

    is 연산자로 연산의 결과가 자료형의 객체인 것을 확인할 수 있습니다. 변경 불가한 지역 변수나 property에 대해서는 명시적인 형변환이 필요하지 않습니다.

    fun getStringLength(obj: Any): Int? {
        if (obj is String) {
    	// obj가 String이므로 obj는 자동으로 String으로 형변환 됨
            return obj.length
        }
    
        return null
    }

    is 연산에 조건 연산자를 결합하여 다음과 같이 작성할 수도 있습니다.

    fun getStringLength(obj: Any): Int? {
        if (obj !is String) return null
    
        return obj.length
    }
    
    또는 
    
    fun getStringLength(obj: Any): Int? {
        if (obj is String && obj.length > 0) {
    	return obj.length
        }
    }

     

    for loop (for 반복문)

    다른 여타 언어들과 동일한 방법으로 for문을 사용 할 수 있습니다.

    val items = listOf("apple", "banana", "kiwifruit")
    for (item in items) {  // 리스트에 포함된 각 항목들에 대해서 작업 수행
        println(item)
    }
    
    또는 
    
    val items = listOf("apple", "banana", "kiwifruit")
    for (index in items.indices) {  // 리스트의 각 항목 번호에 대해서 작업 수행
        println("item at $index is ${items[index]}")
    }

     

    while loop (while 반복문)

    다른 여타 언어들과 동일한 방법으로 while문을 사용 할 수 있습니다.

    val items = listOf("apple", "banana", "kiwifruit")
    var index = 0
    while (index < items.size) {  // index 값이 리스트의 크기인 3이 될 때까지 실행
        println("item at $index is ${items[index]}")
        index++
    }

     

    when expression (when 연산자)

    코틀린에 있는 연산자로 특정 조건에 맞춰서 수행할 수 있는 기능을 제공해 준다.

    fun describe(obj: Any): String =
        when (obj) {
            1          -> "One"          // obj가 숫자 1일때 "One"을 반환
            "Hello"    -> "Greeting"     // obj가 문자열 "Hello"이면 "Greeting"을 반환
            is Long    -> "Long"         // obj가 Long 자료형이면 "Long"을 반환
            !is String -> "Not a string" // obj가 문자열이 아니면 "Not a string"을 반환
            else       -> "Unknown"      // 위에 명시한 조건이 모두 아닐 경우 "Unknown"을 반환
        }
    
    fun main() {
        println(describe(1))       // 출력 : One
        println(describe("Hello")) // 출력 : Greeting
        println(describe(1000L))   // 출력 : Long
        println(describe(2))       // 출력 : Not a string
        println(describe("other")) // 출력 : Unknown
    }

     

    Ranges

    코틀린에서는 숫자의 범위를 in 연산자와 ..로 지정할 수 있다. 예를 들어 1부터 5까지의 숫자 범위를 지정한다고 하면 "in 1..5"로 표기하면 된다.

    fun main() {
        val x = 10
        val y = 9
        if (x in 1..y+1) {
            println("fits in range")
        }
    }

    특정 숫자가 범위 내에 존재하는지는 다음과 같이 확인 할 수 있다.

    fun main() {
        val list = listOf("a", "b", "c")
    
        if (-1 !in 0..list.lastIndex) {
            println("-1 is out of range")
        }
        if (list.size !in list.indices) {
            println("list size is out of valid list indices range, too")
        }
    }

    progress는 다음과 같이 지정할 수 있다.

    fun main() {
        for (x in 1..10 step 2) { // 1부터 시작해서 2번째의 숫자 처리
            print(x)    // 출력 : 13579
        }
        println()
        for (x in 9 downTo 0 step 3) { // 9부터 시작하여 3번째 숫자 처리
            print(x)  // 출력 : 9630
        }
    }

     

    Collections

    콜렉션은 데이터 자료를 모아서 지정할 수 있는 자료형이다.

    콜렉션이 특정 객체를 포함하고 있는지는 in 연산자를 사용하여 확인 할 수 있다.

    fun main() {
        val items = setOf("apple", "banana", "kiwifruit")
        when {
            "orange" in items -> println("juicy")
            "apple" in items -> println("apple is fine too")
        }
    }

    람다 연산을 이용하여 콜렉션에 필터링과 맵핑을 할 수 있다.

    fun main() {
      val fruits = listOf("banana", "avocado", "apple", "kiwifruit")
      fruits
        .filter { it.startsWith("a") }  // a로 시작하는 값만 추려냄. { "avocado", "apple" }
        .sortedBy { it } // 정렬. { "apple", "avocado" }
        .map { it.toUpperCase() }  // 각 항목을 값을 대문자로 변경. { "APPLE", "AVOCADO" }
        .forEach { println(it) }  // 각 항목을 출력
    }

     

    Creating basic classes and their instances (클래스 선언 및 객체 생성)

    fun main() {
        val rectangle = Rectangle(5.0, 2.0)
        val triangle = Triangle(3.0, 4.0, 5.0)
        println("Area of rectangle is ${rectangle.calculateArea()}, its perimeter is ${rectangle.perimeter}")
        println("Area of triangle is ${triangle.calculateArea()}, its perimeter is ${triangle.perimeter}")
    }
    
    abstract class Shape(val sides: List<Double>) {
        val perimeter: Double get() = sides.sum()
        abstract fun calculateArea(): Double
    }
    
    interface RectangleProperties {
        val isSquare: Boolean
    }
    
    class Rectangle(
        var height: Double,
        var length: Double
    ) : Shape(listOf(height, length, height, length)), RectangleProperties {
        override val isSquare: Boolean get() = length == height
        override fun calculateArea(): Double = height * length
    }
    
    class Triangle(
        var sideA: Double,
        var sideB: Double,
        var sideC: Double
    ) : Shape(listOf(sideA, sideB, sideC)) {
        override fun calculateArea(): Double {
            val s = perimeter / 2
            return Math.sqrt(s * (s - sideA) * (s - sideB) * (s - sideC))
        }
    }

    이상으로 안드로이드 앱 개발을 위해서 코틀린의 문법을 간단하게 알아보았습니다.

    코틀린에 대해서 자세히 알아보실 분들은 코틀린 공식 사이트의 문서들을 참고하시기 바랍니다. 

    안드로이드 앱 개발을 하면서 필요하다 싶은 내용은 틈틈히 포스팅을 해보도록 해보겠습니다.

    댓글

Designed by Tistory.