[Android] 초보자를 위한 Kotlin 200제 - 1

android

Posted by 동식이 블로그 on March 26, 2020

초보자를위한 Kotlin 200제(1)

1장. 코틀린 기초 문법

1. hello kotlin

  • 코틀린에서도 println이 사용됨

  • system.out. 이 생략

  • 코틀린은 클래스에서 완전히 독립된 함수를 가질 수 있다

    • 자바에서는 함수가 반드시 클래스에 묶여있다
    • 이를 패키지 레벨 함수라고 한다
  • 변수의 타입을 맨 끝에 적는다

    1
    
    fun main(args: Array<String>) {}
    

    == String[] args와 같다

  • 세미콜론을 적지 않는다

    • 코틀린에서는 한 문장을 한줄에 작성할 때 사용

2. 표현식

  • 하나의 값으로 수렴하는 수식 뭉치
  • 자바에서는 표현식이 단독으로 오는 것을 허용하지 않기 때문에 아래와 같은 코드는 오류를 일으킨다
1
2
3
fun Example() {
    53+62-126
}

3. 변수

  • 값을 저장해 놓는 공간
1
2
3
4
5
6
// 변수를 선언하고 그와 동시에 초기화
var total: Int = 0
val a: Int = 10 + 53 - 7
val b: Int = 32 + 45 - a

total = a + b
  • var
    • 일반변수
  • val
    • 불변 변수
    • 자바의 final

4. 리터럴

  • 변수의 값이 변하지 않는 데이터
1
val variable = 10 + 12 - 5
  • 리터럴에 타입이 있기 때문에 표현식의 결과 값에도 자연스럽게 타입이 생기게 된다
  • 뒤의 10 + 12 - 5는 표현식의 결과가 Int 이므로 variable 뒤에 :int가 생략되어 있는걸 알 수 있다

5. 산술연산자

1
2
3
val num: Double = 15 - 4 * 3 (x) // 자바에서는 3.0이라는 결과값이 나옴

val num2: Int = 15 - 4 * 3

6. 증감연산자

  • ++, --

7. 비트연산자

형태 의미 자바에서는 ?
15 and 7 15와 7을 비트 and 연산 15 & 7
5 or 2 5와 2를 비트 or 연산 5 | 2
15 xor 2 15와 2를 비트 xor 연산 15 ^ 2
32767.inv() 32767을 비트 단위로 반전(not) ~32767
1 shl 3 1을 왼쪽으로 3칸 시프트 1 « 3
8 shr 1 8을 오른쪽으로 1칸 시프트 8 » 1
-17 ushr 2 부호를 유지한채 -17을 2칸 시프트 -17 »> 2

8. 정수타입과 실수타입

  • 정수
    • Byte, Short, Int, Long
  • 실수
    • Float, Double
1
2
3
var e: Float = 67.6f
val f: Double = 658.456
e = (e + f).toFloat()

9. 실수 타입의 함정

1
2
3
4
5
6
7
println(0.1f + 0.1f + 0.1f)
println(0.1f + 0.1f + 0.1f + 0.1f + 0.1f + 0.1f + 0.1f + 0.1f + 0.1f + 0.1f)
println(0.1f * 10)

>>> 0.3
>>> 1.0000001
>>> 1.0

10. 문자타입

1
2
3
4
5
6
7
var ch: Char = 'A'
>>> A				1
ch = '\uAC00'
>>> 				2
ch = '한'
ch.toInt()
>>> 54620			3
  1. 문자 A가 Char 타입으로 ch에 저장

  2. 컴퓨터는 0,1만 알아 듣기 때문에 사실 1번에서 저장된 A도 A가 저장된게 아닌 A의 문자 코드가 저장된다

    AC00 에 해당하는 문자를 저장하고 있다

  3. Char 타입의 표현식에 .toInt()를 적으면 해당 문자의 유니코드 값을 갖는 Int 타입으로 변화한다

11, 12 문자열, 문자열 안에 표현식의 값을 집어넣기

1
2
3
val a = 20
val b = 30
println("a의 값: $a, b의 값: $b")

13. 타입 별명

1
2
3
typealias Number = Int

val a: Number = 10
  • 타입 이름이 너무 길 때 타입 이름을 줄이는 용도로 사용한다

14. 주석

1
2
3
4
5
// 한 줄 전체 주석
/* 구간 주석
해당 사이에 있는 부분은
모두 주석 처리
*/

15. 배정 연산자(Assignment Operator)

=

16. 문장

  • 독립적으로 실행할 수 있는 코틀린 코드 조각

17. 비교연산자

== / != 연산자는 자바에서의 equals 메서드를 호출한 것과 같다

18. 논리연산자

1
2
3
var check = (5>3) && (5>2)
check = (5>3) || (2>5)
check = !(5>3)
  • 단축 평가

19~22 조건문 if, if-else

23, 24. 조건문 when

  • 자바의 switch-case를 업그레이드
1
2
3
4
5
6
7
8
9
val score = 64

val grade: Char = when (score / 10) {
    6 -> println("D")
    7 -> println("C")
    8 -> println("B")
    9, 10 -> println("A")
    else -> println("F")
}

25~28 반복문 while, do while, continue, break

29. Label

  • 작업 흐름을 건너뛸 때 사용할 수 있다
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
var x = 0
var y = 0

outer@ while (x <= 20) {
    y = 0
    while (y <= 20) {
        if (x + y == 15 && x - y == 5) {
            break@outer
        }
        y += 1
    }
    x += 1
}
println("x: $x, y: $y")
>>> x: 10, y: 5

30,31 함수,매개변수와 인수

1
2
3
fun getAverage(a: Int,  b: Int): Double {
    return (a + b) / 2.0
}

32. Unit 타입

  • 반환값을 가지지 않는 함수
1
2
3
fun noReturnFunction(a: Int): Unit {
    println("a는 $a")
}
  • Unit 타입을 반환하는 함수는 return을 생략한다고 해도 암묵적으로 Unit 타입의 객체를 return 하도록 되어있다

33. 디폴트인수

1
2
3
4
5
6
7
8
9
10
11
12
fun getAverage(a: Int = 0, b: Int = 0, print: Boolean = false): Double {
    val result = (a + b) / 2.0
    if (print) {
        println(result)
    }
    return result
}

getAverage(89, 96)
getAverage(a = 66, print = true)
getAverage(print = true)
getAverage(print = true, a = 10, b = 30)

34. 가변인수

  • 개수가 정해지지 않은 매개변수를 만드는 방법
1
2
3
4
5
6
7
8
9
10
11
12
13
14
fun getSumOf(vararg numbers: Int): Int {
    val count = numbers.size
    var i = 0; var sum = 0
    
    while (i < count) {
        sum += numbers[i]
        i += 1
    }
    return sum
}

getSumOf(1,2,3,4,5,6,7)
getSumOf(32, 57, 12)
getSumOf()

35. 함수 오버로딩

  • 이름이 같은 함수를 여러개 선언하는 것
  • 코틀린에서는 함수 시그니쳐가 다르기만 하면 허용한다

36~38 지역변수 전역변수

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
val a = 5

fun main(args: Array<String>) {
    val a = 52
    println(a)
    
    printA()
    printA2()
    func()
}

fun printA() {
    val a = 17
    println(a)
}

fun printA2() {
    val a = 120
    println(a)
}

fun func() {
    println(a)
}

39. 지역함수

  • 블록 안에 선언된 함수를 말한다
  • 특정 함수에서만 사용될 때 사용
1
2
3
4
5
6
7
8
fun main(args: Array<String>) {
    fun printFomular(a: Int, b: Int) {
        println(a * b + a - b)
    }
    
    printFomular(73, 1)
    printFomular(4,6)
}

40. 메모리 스택영역

  • 스택이라는 영역에 지역변수가 저장된다
1
2
3
4
5
6
7
8
9
10
11
fun main(args: Array<String>) {
    val a = -36
    val result = absolute(a)
    println(result)
}

fun absolute(number: Int): Int {
    return if (number >= 0) {
        number
    } else - number
}

41, 42 소스파일 여러 개로 분리하기, 패키지

1
2
3
4
5
6
// Math.kt
package ex_package

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

fun abs(num: Int): Int = if (num >= 0) num else -num
1
2
3
4
5
6
7
8
9
// Ex_package.kt
package ex_package

fun main(args: Array<String>) {
    val a = 20
    val b = -30
    
    println(max(a, abs(b)))
}

43, 44 다른 패키지의 함수 호출하기, import

1
2
3
4
5
6
// Main.kt
package ex_func_in_another_package

fun main(args: Array<String>) {
    println(ex_package.max(30, 10))
}
1
2
3
4
5
6
7
8
9
10
// Main.kt
package ex_import

import ex_package.max
import ex_package.min as ex_min

fun main(args: Array<String>) {
    println(max(55,47))
    println(ex_min(3, 10))
}

45. 객체

  • 서로 연관있는 변수(속성)들을 묶어 놓은 데이터 덩어리를 뜻한다
1
2
3
4
5
6
7
8
9
10
11
package ex_object

fun main(args: Array<String>) {
    val person = object {
        val name: String = "홍길동"
        val age: Int = 36
    }
    
    println(person.name)
    println(person.age)
}
  • 프로퍼티 : 객체에 포함된 변수

46. 메모리의 힙 영역

1
2
3
4
5
6
7
8
9
10
11
package ex_memory_heap

fun main(agrs: Array<String>){
    val person = object {
        val name: String = "홍길동"
        val age: Int = 36
    }
    
    println(person.name)
    println(person.age)
}
  • 힙은 임의의 위치에 저장되며 객체는 힙 영역에 저장된다
  • 참조 변수와 참조 값

47. 클래스

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
class Person {
    var name: String = ""
    var age: Int = 0
}

fun main(args: Array<String>) {
    val person: Person
    person = Person()
    person.name = "홍길동"
    person.age = 36
    
    val person2 = Person()
    person2.name = "김미영"
    person2.age = 29
    
    val person3 = Person()
    person3.name = "JOhn"
    person3.age = 52
}
  • 인스턴스 : 클래스로부터 생성된 객체

48. 힙 영역의 존재 이유

  • 이렇게 힙 영역을 통해서 하나의 객체를 여러 참조 변수에서 공유하는 형태로 사용할 수 있어서 메모리 공간을 훨씬 절약할 수 있다
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
class Product {
    var name = ""
    var price = 0
}

fun main(agrs: Array<String>){
    val product: Product
    product = creatProduct()			// #1
    printProductInfo(product)			// #4
    processProduct(product)				// #6
    printProductInfo(product)			
}

fun createProduct(): Product{
    val apple = Product()				// #2
    apple.name = "Apple"
    apple.price = 1000					// #3
}

fun processProduct(product: Product) {  // #7
    product.price += 500				// #8
}

fun printProductInfo(product: Product) { // #5
    println("이름: ${product.name}")
    println("가격: ${product.price}")	
}

49, 50 문자열간 + 연산 시 주의점, 가비지 컬렉션

  • String 타입의 변수는 스택영역에 저장되지 않는다
  • 실제 문자열은 힙 영역에 생성된다
1
2
3
4
5
fun main(args: Array<String>) {
    var first = "Hello"
    var second = "World"
    first += second
}
1
2
3
4
5
6
7
8
9
10
fun main(args: Array<String>) {
    var result = ""
    var i = 1
    
    while (i <= 100) {
        result += "$i"
        i += 1
    }
    println(result)
}
  • 미아 객체들이 많이 생기게 된다(연결이 끊긴)
  • kotlinlang.org 에서는 가비지 컬렉션이 참조 회수 카운팅 방식을 취하고 있다고 한다
    • 다른 객체가 보유한 객체에 대한 강한 참조 수를 추적하는 것을 기반으로 한다

51. ===, !== 연산자

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
fun main(args: Array<String>) {
    var a = "one"
    var b = "one"
    
    prinln(a === b)
    >>> true
    
    b = "on"
    b += "e"
    println(a !== b)
    >>> true
    
    b = a
    println(a === b)
    >>> true
}
  • String 타입의 리터럴로 이루어진 표현식에 한해서 참조변수에 동일한 문자열을 저장하면 컴파일러는 매번 새로운 문자열을 생성하지 않고 하나의 문자열을 가리키도록 만든다

  • 코틀린의 === 연산자는 자바의 == 연산자와 같다

52. 멤버 함수

  • 클래스에 내장된 함수
1
2
3
4
5
6
7
8
9
10
11
class Building {
    var name = ""
    var date = ""
    var area = 0
    
    fun print() {
        println("이름: " + this.name)
        pritnln("건축일자: " + this.date)
        println("면적: ${this.area}m2")
    }
}

53. 프로퍼티와 멤버 함수의 매개변수 이름이 중복될 때

1
2
3
4
5
6
7
8
class AAA {
    var num = 15
    
    fun memberFunc(num: Int) {
        println(num)
        println(this.num)
    }
}

54. 생성자(Constructor)와 초기화(Initialier)블록

  • 생정자 : 객체를 초기화 하는 특수 멤버 함수
    • 객체를 생성하면서 원하는 값으로 한번에 초기화 하기 위해서
1
2
3
4
5
6
7
8
9
class Person constructor(name: String, age: Int) {
    val name: String
    val age: Int
    
    init {
        this.name = name
        this.age = age
    }
}

55. init 블록 나누어 쓰기

1
2
3
4
5
6
7
8
9
10
11
12
13
14
class Size(width: Int, height: Int) {
    val width = widht
    val height: Int
    
    init {
        this.height = height
    }
    
    val area: Int
    
    init {
        area = width * height
    }
}

56. 생성자와 프로퍼티 한번에 쓰기

1
class Car(val name: String, val speed: Int = 0)
  • 매개변수 앞에 val, var 키워드를 붙이면 동일한 이름의 프로퍼티가 같이 선언된다
  • 생성자 매개변수에 들어온 인수가 프로퍼티의 초기값이 된다

57. 보조 생성자(Secondary Constructor)

  • 클래스 내부에 오는 생성자, 여러 개가 올 수 있다
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
class Time(val second: Int) {
    init {
        println("Init 블록 실행중")
    }
    
    constructor(minute: Int, second: Int) : this(minute * 60 + second){
        println("보조 생성자 1 실행중")
    }
    
    constructor(hour: Int, minute: Int, second: Int) : this(hour * 60 + minute, second) {
        println("보조 생성자 2 실행중")
    }
    
    init {
        println("또 다른 init 블록 실행중")
    }
}

fun main(args: Array<String>) {
    println("${Time(15, 6).second}초")
    println("${Time(6, 3, 17).second}초")
}

>>> init 블록 실행중
>>>  다른 init 블록 실행중
>>> 보조 생성자 1 실행중
>>> 906

>>> init 블록 실행중
>>>  다른 init 블록 실행중
>>> 보조 생성자 1 실행중
>>> 보조 생성자 2 실행중
>>> 21797

58. 프로퍼티와 Getter / Setter

1
2
3
4
5
6
7
8
9
class Person {
    var age: Int = 0
    	get() {
            return field
        }
    	set(value) {
            field = if (value >= 0) value else 0
        }
}
  • 프로퍼티는 실제로 저장되는 공간(field), 저장된 값을 읽으려고 할 때 호출되는 Getter
  • 값을 저장하려고 할 떄 호출되는 Setter로 이루어져 있다
1
2
3
4
5
6
7
8
9
10
class Person {
    var age: Int = 0 {
        get() {
            return field
        }
        set(value) {
            field = value
        }
    }
}
  • 코틀린에서는 프로퍼티에 디폴트 getter/setter가 포함이 되어있기 때문에 따로 만들 필요가 없다
  • getter/setter의 동작을 커스터마이징 하고 싶다면 별도로 정의를 해야 한다

59. 연산자 오버로딩

  • 객체의 인스턴스간에 연산자를 사용했을 때 해당 연산자를 오버로딩한 멤버 함수를 호출한다
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
class Point(private var x: Int = 0, private var y: Int = 0) {
    operator fun plus(other: Point): Point {
        return Point(x + other.x, y + other.y)
    }

    operator fun minus(other: Point): Point {
        return Point(x - other.x, y - other.y)
    }

    operator fun times(number: Int): Point {
        return Point(x * number, y * number)
    }

    operator fun div(number: Int): Point {
        return Point(x / number , y / number)
    }

    fun print() {
        println("x: $x, y: $y")
    }
}

fun main(args: Array<String>) {
    val pt1 = Point(3,7)
    val pt2 = Point(2, -6)
    
    val pt3 = pt1 + pt2
    val pt4 = pt3 * 6
    val pt5 = pt4 / 3
    
    pt3.print()
    pt4.print()
    pt5.print()
}
  • 오버로딩이 가능한 연산자

    • 단항 연산자

      표현식 함수이름 컴파일 시 실제로 적용되는 형태 함수 반환 타입
      +a unaryPlus a.unaryPlus() 자유
      -a unaryMinus a.unrayMinus() 자유
      !a not a.not() 자유
    • 이항 연산자

      표현식 함수 이름 컴파일 시 실제로 적용되는 형태 매개변수 타입 함수 반환 타입
      a + b plus a.plus(b) 자유 자유
      a - b minus a.minus(b) 자유 자유
      a * b times a.times(b) 자유 자유
      a / b div a.div(b) 자유 자유
      a % b rem a.rem(b) 자유 자유
      a += b plusAssign a.plusAssign(b) 자유 자유
      a -= b minusAssign a.minusAssign(b) 자유 자유
      a *= b timesAssign a.timeAssign(b) 자유 자유
      a /= b divAssign a.divAssign(b) 자유 자유
      a %= b remAssign a.remAssign(b) 자유 자유
      a > b compareTo a.compareTo(b) > 0 자유 Int
      a < b compareTo a.compareTo(b) < 0 자유 Int
      a >= b compareTo a.compareTo(b) >= 0 자유 Int
      a <= b compareTo a.compareTo(b) <= 0 자유 Int
      a == b equals a?.equals(b) ?: (b === null) Any? Boolean
      a != b equals !(a?.equals(b) ?: (b === null)) Any? Boolean

60. 번호 붙은 접근 연산자(indexed) []

  • 객체의 일부 값을 추출해 낼 때 사용한다
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
class Person(var name: String, var birthday: String) {
    operator fun get(position: Int): String {
        return when (position) {
            0 -> name
            1 -> birthday
            else -> "알수 없음"
        }
    }

    operator fun set(position: Int, value: String) {
        when (position) {
            0 -> name = value
            1 -> birthday = value
        }
    }
}

var person = Person("Kotlin", "2016-02-15")
println(person[0])
println(person[1])
println(person[-1])

person[0] = "java"
println(person.name)
  • [] 연산자를 오버로딩하는 멤버함수 get과 set

61. 호출 연산자 ()

  • 함수를 호출하는 연산자도 코틀린에서는 오버로딩이 가능하다
1
2
3
4
5
6
7
8
9
10
11
12
13
class Product(val id: Int, val name: String) {
    operator fun invoke(value: Int) {
        println(value)
        println("id: $id\nname:$name")
    }
}

val product = Product(762443, "코틀린 200제")
product(108)

>>> 108
	id: 762443
	name: 코틀린200

62. in 연산자

  • 어떤 값이 객체에 포함되어 있는지 여부를 조사하는 역할
1
2
3
4
5
println('o' in "Kotlin")
println("in" !in "Kotlin")

>>> true
>>> false
  • 'o' in "kotlin" 은 컴파일 시 "Kotlin".contains('o') 로 번역된다
  • "in" !in "Kotlin" 은 컴파일 시 !"Kotlin".contains("in") 으로 번역된다

63. 멤버 함수의 중위 표기법

  • 피연산자 연산자 피연산자 순서로 표현식을 구성하는 방식
  • 멤버 함수의 매개변수가 하나뿐이면 함수 호출을 중위 표기법으로 할 수 있다
1
2
3
4
5
6
7
8
9
10
11
12
13
class Point(var x: Int = 0, var y: Int = 0) {
    // base를 원점으로 생각했을 때 좌표를 반환
    infix fun from(base: Point): Point {
        return Point(x - base.x, y - base.y)
    }
}

val pt = Point(3,6) from Point(1,1)
println(pt.x)
println(pt.y)

>>> 2
>>> 5
  • Point(3,6) from Point(1,1)Point(3,6).from(Point(1,1)) 이다

64. 상속(Inheritance)

  • 기존에 존재하는 클래스를 확장하여 새로운 클래스를 정의하는 기법

  • 코틀린은 자바와는 달리 클래스 선언이 기본적으로 final로 되어있기 때문에 상속을 위해 open 키워드를 붙여주여야 한다
  • 두 개 이상의 클래스는 동시에 상속이 불가능하다
1
2
3
4
5
6
open class Person(val name: String, val age: Int)

class Student(name: String, age: Int, val id: Int) : Person(name, age)

val person = Person("홍길동", 35)
val student = Student("김길동", 23, 20171217)
  • 상속을 할 때는 반드시 슈퍼클래스의 생성자를 호출해야 한다

65. 업캐스팅

  • 캐스팅 : 특정 타입을 다른 타입으로 변환하는 것
1
2
3
4
5
open class Person(val name: String, val age: Int)

class Student(name: String, age: Int, val id: Int) : Person(name, age)

val person: Person = Student("john", 32, 201721217)
  • 업캐스팅이란 서브클래스의 인스턴스를 슈퍼클래스 타입으로 가리키는 것을 말한다
    • person의 참조 변수는 Student의 인스턴스를 가리키고 있다
    • 하지만 타입이 Person이기 때문에 name과 age 프로퍼티 밖에 접근하지 못한다
  • 슈퍼클래스 타입은 항상 슈퍼클래스 자체서브클래스의 인스턴스만 가리킬 수 있다

66, 67 오버라이딩, 프로퍼티 오버라이딩

  • 슈퍼클래스의 멤버 함수의 동작을 덮어쓰기 하는 것
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
open class AAA {
    open val number: Int = 0
    open fun func() = println("AAA")
}

open class BBB : AAA() {
    override var number: Int = 0
    final override fun func() {
        super.func()
        println("BBB")
    }
}


class CCC : BBB() {
    override fun func()
    // 에러
}

AAA().func()
BBB().func()

>>> AAA
>>> AAA
>>> BBB

68. 다형성(Polymorphism)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
open class AAA {
    open fun hello() = println("AAA")
}

class BBB : AAA() {
    override fun hello() = println("BBB")
}

val one = AAA()
val two = BBB()
val three: AAA = two

one.hello()
two.hello()
three.hello()

>>> AAA
>>> BBB
>>> BBB
  • 멤버 함수를 호출할 때, 참조 변수가 실제로 가리키고 있는 객체의 멤버 함수가 호출된다
    • 호출은 참조변수.hello()형태의 한가지 이지만 문맥에 따라서 호출되는 함수가 다르다
  • 참고 도서

초보자를 위한 Kotlin 200제