본문 바로가기

책 요약하기/코틀린을 활용한 안드로이드 프로그래밍

#3. 안드로이드를 위한 기본적인 Kotlin 문법 2021-01-27

- 요약 -

1. Kotlin은 객체지향 프로그래밍을 지원하며 주요 특징은 다음과 같습니다.

  • Java와 100% 상호 호환됩니다.
  • Java보다 문법이 간결합니다.
  • 프로그램의 안정성을 높여줍니다. (?, !!)
  • var 또는 val 예약어를 통해 데이터 형식을 선언하지 않고 변수를 선언할 수도 있습니다.

2. Kotlin에서 많이 사용되는 기본적인 데이터 형식은 Char, String, Byte, Short, Int, Long, Float, Double, Boolean 등이 있습니다.

3. if 문은 조건이 true인지 false인지에 따라서 어떤 작업을 할 것인지를 결정하고 when 문은 여러 가지 경우에 따라서 어떤 작업을 할 것인지를 결정합니다. (when은 switch문이랑 비슷함)

4. 일차원 배열을 선언하고 값을 대입하는 방법은 다음과 같습니다.

var one : Array<Int> = Array<Int>(4, {0})    // size : 4,  init : 0
one[0] = 10
one[3] = 30

5. for문은 다음과 같은 두 가지 형식을 지원합니다.

for (변수 in 시작..끝 step 증가량) {
    // 반복 내용 실행
}
for (변수 in 배열명.indices) {
    // 반복 내용 실행
}

6. 변수에는 전역변수와 지역변수가 있다. 전역변수는 모든 메소드에서 사용되고 지역변수는 메소드 내부에서만 사용됩니다.

7. 클래스는 어떤 기능을 하도록 하기 위해 생성하는데, 객체지향 관점에서는 클래스를 실세계의 객체가 가질 수 있는 상태와 행동으로 정의합니다.

8. 생성자는 클래스의 이름과 통일하게 하고, 메소드의 데이터 형식은 생략합니다. (생성자 정의시 constructor(...) { ... })

9. 메소드 오버로딩은 한 클래스 내에서 메소드의 이름이 같아도 파라미터의 개수나 데이터 형식만 다르면 됩니다.

10. 정적 구성 요소에는 정적 필드, 정적 메소드, 상수 필드가 있으며, 모두 companion object { ... } 안에 필드를 작성합니다.

11. 클래스 상속은 기존의 클래스가 가지고 있는 것을 그대로 물려받으면서 필요한 필드나 메소드를 추가로 정의 하는 것을 의미합니다.

12. 추상 클래스는 인스턴스화가 불가능하며, 클래스 앞에 abstarct 키워드를 붙여서 지정합니다. 추상클래스를 사용하는 이유는 추상클래스를 상속받는 클래스는 추상클래스 내부에 추상 메소드들을 반드시 구현하도록 강제하기 때문입니다.

13. 다형성은 서브클래스(자식클래스)에서 생성한 인스턴스를 자신의 클래스 변수에 대입할 수 있는 것을 말합니다.

( 추상클래스 animal을 상속받는 dog, cat이 있다면 var animal <- 클래스 변수만 선언해서 dog클래스의 메소드 혹은 필드나 cat클래스의 메소드, 필드를 필요에 따라 대입하여 사용 할 수 있습니다. ex) animal = cat(), animal = dog() )

14. 인터페이스는 추상 클래스와 비슷합니다. 인터페이스는 class 키워드 대신 interface 키워드를 사용하여 정의하고, 내부에는 추상 메소드를 선언합니다. (★ 인터페이스는 선언 시 이름 앞에 i를 붙이는 것이 관행입니다.)

15. 람다식은 함수를 익명 함수 형태로 간단히 표현한 것입니다.  람다식을 사용하면 코드가 간결해지고 가독성이 높아집니다.

16. 클래스와 인터페이스가 많아지면 관리하기 어려우므로 패키지 단위로 묶어서 관리합니다.

17. 제네릭스는 데이터 형식의 안정성을 보장하는 데 사용합니다.

ex) ArrayList<string> <- 제네릭스

 

- 연습 문제 -

1. 다음 중 틀린 것을 모두 고르시오.

  1. Kotlin은 게터와 세터를 반드시 구현해야 한다.
  2. Kotlin은 switch()~문을 지원한다.
  3. Kotlin은 null 객체를 사용하는 경우를 잘 보호한다.
  4. Kotlin은 데이터 형식을 선언하지 않고도 변수를 선언할 수 있다.

답 : 3, 4  (1 - 게터와 세터를 구현하지 않습니다. 2 - when문을 지원합니다.)

 

2. Kotlin에서 사용되는 데이터 형식 중에서 정수형에 대해 설명하시오.

정수형 Byte 1byte를 사용하며 -128 ~ +127까지 입력
Short 2byte를 사용하며 -32768 ~ +32767까지 입력
Int 4byte를 사용하며 -21억 ~ +21억까지 입력
Long 8byte를 사용하며 매우 큰 정수까지 입력

 

3. 다음은 점수에 따라서 A ~ F로 학점을 나누는 프로그램이다. 빈칸을 채우시오.

fun main() {
     var count : Int = 85
     
     when(count) --- (1) {
          in 90 .. 100 -> println("A학점")
          in 80 .. 89 -> println("B학점")
          in 70 .. 79 -> println("C학점")
          in 60 .. 69 -> println("D학점")
          else -> println("F학점") ---(2)
     }
}

 

4. 다음과 같이 3X3 이차원 배열을 선언하고, for 문을 이용하여 값을 대입하는 코드를 작성하시오.

이차원배열: array[3][3]

100 [0][0] 110 [0][1] 120 [0][2]
130 [1][0] 140 [1][1] 150 [1][2]
160 [2][0] 170 [2][1] 180 [2][2]

 

<답>
var array : Array<IntArray> = Array<IntArray>(3, {IntArray(3)})
for (i in 0 .. array.size - 1) {
    for (j in 0 .. array[0].size - 1) {
        array[i][j] = 100 + ((i * 3) + j) * 10
    }
}

(무조건적인 정답은 없습니다. ㅎㅎ)

 

5. 다음 코드가 출력하는 값을 차례로 쓰시오.

var data : Int = 100

fun func1(para: Int) {
    var data : Int = para
    data++
    println(data)
}

fun main() {
    var data : Int = 200
    data++
    println(data)
    func1(data)
}

 

<답>
201
202
---- >>> 기존의 println(data)는 지역변수 200에 data++ 증감연산을 하여 201이 되어 출력됩니다.
func1(data)로 201을 파라미터로 넘긴 후 해당 파라미터를 var data에 대입하고 data++를 통한 증감연산으로
201에서 1 증가한 202를 출력합니다. (전역변수 var data : Int = 100은 속임수죠 :)))

 

6. 다음은 객체지향과 관련된 내용이다. 틀린 것을 모두 고르고 그 이유를 설명하시오.

  1. 클래스를 정의할 때 생성자는 반드시 명시적으로 정의해야 한다.
  2. 메소드 오버로딩은 한 클래스 내에서 메소드의 이름이 같아도 파라미터의 개수가 다르면 같은 이름의 메소드를 여러 개 선언할 수 있는 것을 말한다.
  3. 정적 필드는 static 예약어를 사용한다.
  4. 정적 메소드는 인스턴스를 생성하지 않고도 사용이 가능하다.
  5. Kotlin은 클래스의 다중 상속이 가능하지만 되도록 사용하지 않는 것이 좋다.
  6. 추상 클래스는 클래스 이름 앞에 abstract를 붙여서 정의한다.
  7. 추상 메소드를 포함하는 클래스는 반드시 추상 클래스로 정의해야 한다.
  8. 추상 메소드도 필요하다면 메소드 본체를 만들고 메소드에서 작동할 내용을 코딩할 수 있다.
  9. 인터페이스를 사용할 때는 interface 예약어를 사용한다.

답 : 1, 3, 5, 8

1. 클래스 정의 시 생성자를 명시적으로 정의하지 않아도 기본 생성자가 자동 생성된다.
3. 정적 필드는 정적 메소드, 상수 필드와 더불어 클래스 내부에 companion object { ... }안에 일반적인 방식으로 선언 및 정의하면 된다.
5. Kotlin은 자바와 마찬가지로 클래스 간 단일 상속만 가능하며 인터페이스의 경우는 클래스와 달리 다중 구현이 가능하다.
8. 추상 메소드는 이를 상속하는 서브 클래스의 재정의를 강제하는데 이에 따라 메소드 내용은 작성하지 않는다.

 

7. [예제 3-7]의 Car 클래스를 상속받은 Truck 클래스를 다음 조건에 맞게 생성하시오.

  • 배기량 변수 cc를 추가한다(정수형).
  • 트럭의 생산 연도 상수 필드 YEAR를 선언하고, 값을 2023으로 한다.
  • 배기량 값을 파라미터로 받는 생성자를 추가한다.
  • upSpeed(int) 메소드를 최대 속도 150으로 오버라이딩한다.
[예제 3-7]
class Car {
    var color : String = ""
    var speed : Int = 0

    fun upSpeed(value : Int) {
        if (speed + value >= 200)
           speed = 200
        else
           speed += value
    }

    fun downSpeed(value : Int) {
       if (speed - value <= 0 )
          speed = 0
       else
          speed -= value
    }
}

 

[답]
open class Car {
    var color : String = ""
    var speed : Int = 0

    open fun upSpeed(value : Int) {
        if (speed + value >= 200)
           speed = 200
        else
           speed += value
    }

    fun downSpeed(value : Int) {
       if (speed - value <= 0 )
          speed = 0
       else
          speed -= value
    }
}

class Truck : Car {
    var cc : Int = 0

    companion object {
       const val YEAR = 2023
    }
    

    constructor (cc : Int) {
       this.cc = cc
    }

    override fun upSpeed(value: Int) {
       if (speed + value >= 150)
           speed = 150
       else
           speed += value
    }
}

 

8. 다음은 클래스와 제네릭스를 활용하여 10+20+30의 합을 구하는 코드이다. 빈칸을 채우시오.

class MyClass(value: Int) {
    var value : Int = 0
    init {
       this.value = value
    }
}

fun main() {
    var c1 : MyClass = MyClass(10)
    var c2 : MyClass = MyClass(20)
    var c3 : MyClass = MyClass(30)

    var myList = ArrayList<MyClass>(3) --- (1)

    myList.add(c1)
    myList.add(c2)
    myList.add(c3)
    var hap = 0

    for (i in 0 until myList.size) {
       hap += myList[i].value --- (2)
    }
    
    println(hap)
}

 

이상 마치겠습니다! :))