컴공과컴맹효묘의블로그

Kotlin 본문

컴퓨터/Android Studio

Kotlin

효묘 2022. 3. 1. 18:03
반응형

[코틀린]

Kotlin은 JetBrains에서 개발한 프로그래밍 언어. 2011에 처음 공개됨. 2017년 구글에서 안드로이드 공식 언어로 지정.

코틀린의 특징

  • 표현력과 간결함.
  • 안전한 코드. Null safety를 지원한다. 변수를 nullable과 not null로 구분해서 선언한다.
  • 상호 운용성. Kotlin은 Java와 100% 호환.
  • 구조화 동시성. Coroutines라는 기법으로 비동기 프로그래밍을 간소화 할 수 있다.

코틀린 파일 구성

패키지 경로가 다르다면 import문으로 불러와야하지만, 같다면 그럴 필요가 없다.

자바와는 다르게 파일명과 클래스명을 다르게 선언해도 된다.

예를 들어 Test.kt라는 코틀린 파일에 다음과 같은 내용이 작성되어있다면, Test클래스인 Test.classTest.kt의 최상위에 선언되어있는 변수 num과 함수 sumTestKt.class라는 파일에 자동적으로 포함된다.

// Test.kt
// 패키지문, 임포트문 생략

var num = 10

fun sum(x: Int, y: Iny): Int {
    return x + y
}

class Test {
    var name = "Alex"

    fun printName() {
        println("$name")
    }
}

[변수 선언]

코틀린은 valvar키워드로 선언한다.

val은 value의 줄임말로 초기 할당 후 바꿀 수 없는 변수다.

var은 variable의 줄임말로 언제든지 값을 바꿀 수 있다.

val name: String = "Hyomyo"
var num: Int = 10

코틀린의 타입 명시는 타입이 추론 가능할 때 생략 가능하다.

초깃값, 초깃값 미루기- lateinit, lazy

최상위에 선언된 변수 혹은, 클래스의 멤버 변수는 선언과 동시에 초깃값을 할당해야 한다.

lateinit

lateinit으로 초깃값 선언을 미룰 수 있다. 단, var키워드로 선언한 변수에서만 가능하다.

그리고, Int, Long, Short, Double, Float, Boolean, Byte타입은 lateinit이 불가능하다.

lazy

변수 선언문 뒤에 by lazy {..}로 선언한다. lazy를 적용한 변수를 처음 사용할 때 brace안의 구문이 실행되며, brace 마지막 줄이 변수 초깃값으로 할당된다.

데이터 타입

코틀린의 모든 변수는 객체다. 흔이 쓰는 정수 Int타입도 primitive type이 아니라 객체이다. 따라서 Int에도 null이 대입 될 수 있다.

fun function() {
    var num1: Int = 10
    var num2: Int? = null

    println(num1.toString())
}

효율성에 의문이 들 수 있지만 코틀린은 컴파일 할 때 Collection이나 Generic을 사용하는 경우 이외에는 Java의 int로 변환된다.

형 변환

자바와 다르게 기초 타입 객체끼리의 자동 형 변환이 없다. 변수에 .toLong()과 같이 표현해서 사용해야한다.

Int, Short, Long, Double, Float, Boolea, Byte- 기초 타입 객체

val num1: Int = 10
val num2: Short = 20
val num3: Long = 123456L
val num4: Double = 10.0
val num5: Float = 10.0f

val num6: 0b00101001
val num7: Boolean = true

val num8: Int = 0x0F // 16진수 리터럴 상수

Char, String- 문자와 문자열

Char는 문자 표현 타입이다. 문자를 작은 따옴표 '로 감싸서 표현. Number타입으로는 표현할 수 없다.

String은 문자열 표현 타입이다. 큰 따옴표"나 삼중 따옴표"""로 감싸서 사용된다.

문자열 템플릿 $

문자열 템플릿이 가능하다.

println("name: ${user.name} date: $date")

[Any- 모든 타입]

Any는 코틀린에서 최상위 클래스이다. Any 타입으로 선언한 변수는 모든 타입의 데이터를 할당할 수 있다.

Unit- 반환문이 없는 함수

Unit은 변수 선언 타입이 아닌 함수 반환문이 없음을 명시적으로 표현할 때 사용한다.

fun printQueue(): Unit {
    // ... 
}

Nothing- null이나 예외를 반환하는 함수

NothingUnit과 마찬가지로 함수에 반환 타입에 사용된다.

fun function1(): Nothing? {
    return null
}

fun function2(): Nothing {
    return Exception()
}

Nullable과 Not null

코틀린은 변수를 선언할 때 null을 대입할 수 있는지, null이 아닌지 명시를 해야한다.

var data1: Int = 10 // Not null. null을 대입할 수 없음
data1 = null // 에러

var data2: Int? = 10 // Nullable.
data2 = null

함수 선언

fun 함수명(매개변수명: 타입): 반환타입 {
    // ...
}

반환 타입을 생략하면 Unit타입이 자동으로 적용된다.

함수 매개변수에 var이나 val키워드를 작성할 수 없으며 자동으로 val타입이 적용된다.

기본값 default value를 설정할 수 있다.

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

매개변수를 지정해서 호출named parameter할 수 있다.

sum(b = 20, a = 12)

[컬렉션 타입 Colleciton type]

Collection type은 여러 개의 데이터를 표현하는 방법.
Array, List, Set, Map이 있다.

Array- 배열

코틀린의 배열은 Array클래스로 표현한다.

Array의 첫 번째 매개변수는 크기size를 지정하며 두 번째 매개변수는 초깃값init을 지정한다.

<init>(size: Int, init: (Int) -> T)

배열의 타입은 제너릭generic으로 표현한다.

val arr: Array<Int> = Array(3, { 0 })

두 번째 매개변수는 0을 반환하는 람다 함수이다.

배열의 제너릭이 기초 객체 타입이라면 IntArray, LongArray, ShortArray, DoubleArray, FloatArray, BooleanArray, ByteArray, CharArray를 이용할 수도 있다.

arrayOf()라는 함수를 사용해서 배열을 선언할 때 값을 할당할 수도 있다.

val arr = arrayOf<Int>(10, 20, 30) // 배열에 10, 20, 30 대입
println("size: ${arr.size}") // 3

arrayOf()함수도 기초 객체 타입을 대상으로 하는 intArrayOf(), BooleanArrayOf()... (생략)... 함수를 제공한다.

List, Set, Map

  • List: 데이터의 순서가 있고 값 중복 허용.
  • Set: 데이터의 순서가 없고 값 중복 불허. 수학의 집합과 같음.
  • Map: key와 value라는 데이터 순서쌍으로 이루어진 데이터 집합. 순서가 없고, key의 중복 불허.

Collection type의 mutable과 immutable

Collection 타입 클래스는 가변mutable과 불변immutable로 나뉜다.

immutable 클래스는 초기에 값을 할당하면 변경할 수 없다.

var list = listOf<Int>(10, 20, 30) // 불변immutable이다.
var mutableList = mutableListOf<Int>(10, 20, 30) // 가변mutable이다.

immutable 클래스 타입은 List, Set, Map

immutable 클래스 함수는 listOf(), setOf(), mapOf()

mutable 클래스 타입은 MutableList, MutableSet, MutableMap

mutable 클래스 함수는 mutableListOf(), mutableSetOf(), mutableMapOf()

Array 클래스는 항상 mutable이다. 참고

Map객체는 Pair객체를 이용하거나 key to value로 이용할 수 있다.

fun main() {
    var map = mapOf<String, String>(Pair("Pale", "Beer"), "Whisky" to "Spirits")
}

[조건문 if~else 와 when 그리고 표현식]

Kotlin의 if~else문은 표현식으로도 사용할 수 있다. 단, 표현식으로 사용하려면 else를 생략할 수 없다.

표현식의 반환값은 brace안의 각 마지막 문장이다.

fun main() {
    var num = 3
    var isPositive = if (var >= 0) {
        println("Positive")
        true
    } else {
        println("Negative")
        false
    }
}

whenswitch ~ case문 같은 역할을 한다.

fun main() {
    var data = 3
    when (data) {
        is String -> println("data is String type")
        0 -> println("data is 0")
        in 1..10 -> println("data is in 1..10") // 1이상 10이하
        else -> println("data is invalid")
    }
}

when(data)의 소괄호()를 생략해도 된다.

fun main() {
    var data = 3
    when {
        data < 0 -> println("data is negative")
        data >= 0 -> println("data is positive")
        else -> println("data is invalid")
    }
}

when문 또한 표현식으로도 사용 가능하다. 마찬가지로 else를 생략할 수 없다.

fun main() {
    var data = 3
    val result = when {
        data > 0 -> "data is positive"
        data < 0 -> "data is negative"
        data == 0 -> "data is 0"
        else -> "data is not a number"
    }
}

[반복문 for, while]

  • for (i in 1..10) { ... } // 1부터 10까지 1씩 증가
  • for (i in 1 until 10) { ... } // 1부터 9까지 1씩 증가
  • for (i in 2..10 step 2) { ... } // 2부터 10까지 2씩 증가
  • for (i in 10 downTo 1) { ... } // 10 부터 1까지 1씩 감소

collection.indices를 사용해서 컬렉션 타입의 데이터 개수만큼 반복할 수 있다.

collection.withIndex()함수를 사용하면 인덱스와 값을 동시에 가져올 수 있다.

fun main() {
    var data1 = arrayOf<Int> (1, 2, 3)
    var data2 = arrayOf<Int> (10, 20, 30)

    for (i in data1.indices) {
        print("$data[i] ")
    }
    println("")
    for ((index, value) in data2.withIndex()) {
        print("$index: $value")
    }
}

while은 조건이 참일때 {}영역을 반복 실행한다.

[객체지향 코틀린]

class키워드로 클래스를 선언한다.

class Test { }

객체 생성은 다음과 같이 한다. new키워드를 사용하지 않는다.

val user = User("myName", 82)

클래스의 멤버는 생성자, 변수, 함수, 클래스로 구성된다.

생성자의 키워드는 constructor이다.

주 생성자

생성자는 주 생성자보조 생성자로 나뉜다. 각각 따로 사용해도 되고 둘 다 동시에 사용해도 된다.

  • 주 생성자 class User consturctor() { }
  • 주 생성자 키워드 생략 class User() { }
  • 매개변수가 없을 때 생략 class User { }

주 생성자init영역에서 초기화를 한다. 단, 주 생성자의 매개변수는 지역변수이므로 클래스 내 다른 곳에서는 사용할 수 없다.

class User(name: String, userId: Int) {
    init {
        println("name: $name user_id: $userId")
    }

    fun function() {
        // name, userId 사용 불가
    }
}

클래스 멤버변수에 저장해서 사용 가능하다.

class User(name: String, countryNumber: Int) {
    var name: String
    var countryNumber: Int

    init {
        this.name = name
        this.countryNumber = Int
    }
}

val혹은 var 키워드를 이용해서 매개변수를 받는 동시에 멤버변수로 만들 수 있다.

class User(val name: String, val height: Double) { }

보조 생성자

counstuctor키워드로 생성하며 한 클래스에 여러 보조 생성자를 생성할 수 있다.

class User {
    consturctor(name: String) {

    }
    consturctor(name: String, num: Int) {

    }
}

주 생성자보조 생성자를 같이 선언하는 경우 보조 생성자에서 주 생성자를 어떤 식이로든 꼭 호출해야 한다.

호출하는 방법은 this키워드를 이용한다.

class User(name: String) {
    consturctor(name: String, num: Int): this(name) {

    }
    consturctor(name: String, num: Int, sex: String): this(name, num) {

    }
}

fun main() {
    val user = User("myName", 82, "male")
}

상속과 생성자

클래스를 선언할 때 다른 클래스를 참조해서 선언하는 것을 상속inheritance라고 한다.

다음은 Dog클래스가 Animal클래스를 상속하는 코드이다.

open키워드를 사용해 상속이 가능한지 결정한다.

open class Animal { // 상위 클래스 Animal

}

class Dog: Animal() { // 하위 클래스 Dog

}

하위 클래스에 보조 생성자만 있는 경우 다음과 같이 가능하다.

open class Animal(name: String) { // 상위 클래스 Animal

}

class Dog: Animal { // 하위 클래스 Dog
    constructor(name: String): super(name)
}

오버라이딩 Overriding

오버라이딩은 상위 클래스의 멤버 변수나 함수를 같은 이름으로 재정의 하는 것을 말한다.

자바는 @Override라는 annotaion 을 명시해서 사용하지만, 굳이 명시하지 않아도 overriding이 가능했다.

코틀린은 이러한 모호성을 없애기 위해 override라는 키워드를 이용해야만 overriding이 가능하다.

또한 상위 클래스의 멤버 변수나 함수가 open키워드로 작성되어야 한다.

open class Parents {
    open var num = 10
    open fun function() {
        println("Parents")
    }
}

class Child: Parents {
    override var num = 10
    override fun function() {
        println("Child")
    }
}

접근 제한자 visibility modifier

코틀린의 접근 제한자는 public, internal, protected, private가 있다.

접근 제한자 최상위 선언 클래스 멤버로 선언
public 모든 파일 가능 모든 클래스 가능
internal 같은 모듈 내에서 가능 같은 모듈 내에서 가능
protected 사용 불가 상속 관계의 하위 클래스에서 가능
private 파일 내부에서만 가능 클래스 내부에서만 가능

[클래스 종류]

data class

data키워드로 선언. VO(value-object) 를 편리하게 이용하도록 해줌.

data class DataClass( ... )

데이터 클래스는 객체의 equals()함수로 멤버 변수의 값을 비교해줌. 일반 클래스는 객체 자체를 비교함.

데이터 클래스는 toString()함수를 사용하면 객체의 데이터 정보들을 반환해줌.

object class

오브젝트 클래스는 익명 클래스 anonymous class를 사용할 목적으로 만들어졌다.

object키워드로 생성한다.

val obj = object {
    var name = "Hello"
    fun printName() {
        println("name: $name")
    }
}

fun main() {
    obj.name = "world" // 에러
    obj.printName() // 에러
}

오브젝트 클래스는 타입을 명시하지 않으면 최상위 클래스인 Any타입으로 취급한다. 따라서 name같은 멤버 변수에는 접근할 수 없다.

object: A { }처럼 선언하면 A라는 클래스를 상속, 혹은 A라는 인터페이스를 구현한 익명 클래스가 생성된다.

따라서 A객체의 멤버 변수나 메소드를 overriding할 수 있고 사용할 수도 있다.

companion class

컴패니언 클래스는 객체를 생성하지 않고 클래스에 접근할 때 사용한다. 자바의 static과 유사하지만 코틀린에서는 최상위에 변수나 함수를 선언할 수 있으므로 static이 필요하지 않다.

클래스 내부에 companion object { }로 선언한다.

class Test {
    companion object {
        var data = 1
        fun func() {
            println(data)
        }
    }
}

fun main() {
    Test.data = 10
    Test.func()
}

[람다 함수]

람다함수 혹은 람다식은 함수를 간단하게 정의할 때 사용되는 익명함수 anonymous function 정의 기법이다.

람다 함수는 다음과 같이 정의된다.

{ 매개변수 -> 함수 본문 }
val sum = {a: Int, b: Int -> a + b }
sum(a, b)

선언과 동시에 호출하는 방법이다.

{a: Int, b: Int -> a + b}(10, 20)

매개변수가 없으면 화살표를 생략해도 된다.

{-> println("Hello world!")}

{println("Hello world!")}

매개변수가 1개라면 다음과 같이 작성할 수 있다. 단, 타입 식별이 가능해야한다.

val some = {num: Int -> println(num)}
some(10)
// --- --- --- ---
val some: (Int) -> Unit = {println(it)}
some(10)

(Int)->Unit을 함수 타입이라고 한다. 이 때 it키워드를 사용하면 생략된 매개변수를 이용할 수 있다.

람다 함수 반환

람다함수는 마지막 문장을 반환한다. return키워드를 사용할 수 없다.

val func = {a: Int, b: Int -> 
    println("adding...")
    a + b
}

함수 타입 선언- typealias

매개변수와 반환타입을 표현할 수 있다. 예를 들어, Int타입 매개변수 두 개와 Int타입을 반환하는 함수는 (Int, Int)->Int으로 표현할 수 있다.

val func: (Int, Int) -> Int = {a: Int, b:Int -> a + b}

typealias를 이용해 함수 타입이나 데이터 타입을 선언할 수 있다.

typealias CustomInt = Int
typealias CustomFunc = (Int, Int) -> Boolean

fun main() {
    val data: CustomInt = 10
    val func: CustomFunc = {a: Int, b: Int -> a > b}
}

매개변수 타입을 유추할 수 있다면 생략 가능하다.

typealias CustomFunc = (Int, Int) -> Boolean

fun main() {
    val func: CustomFunc = {a, b -> a > b}
}

고차 함수 high order function

고차 함수란, 매개변수로 함수를 전달받거나, 반환값이 함수인 함수를 말한다.

fun function(arg: (Int) -> Boolean): () -> String {
    val result = if(arg(10)) {
        "valid"
    else {
        "invalid"
    }
    return {"result: $result"}
}

fun main() {
    val result = function({no -> no > 0})
    println(result())
}

[널null 안정성]

널 안정성이란 NullPointException이 발생하지 않도록 코드를 작성하는 것이다.

자바에서는 null 안정성을 고려해서 많은 코드를 짜야했지만 코틀린에서는 간단하게 null 안정성을 처리할 수 있다.

널 허용- ? 연산자

변수 타입 뒤에 ?를 붙이면 nullable이고 붙지 않으면 not null이다.

var name: String? = null
var hello: String = "Hello"

널 안정성 호출- ?. 연산자

?.연산자는 변수가 null이 아니면 멤버에 접근을 할 수 있고, null이면 null을 반환한다.

name?.length

엘비스- ?: 연산자

엘비스 연산자는 변수가 null이면 ?:뒤에 있는 값을, null이 아니면 변수를 반환한다.

var data: String? = "my name"
println("data: $data: ${data?.length ?: -1}") // data = my name: 7
data = null
println("data: $data: ${data?.length ?: -1}") // data = null: -1

예외 발생- !!연산자

!!는 객체가 널일 때 예외exception을 일으키는 연산자다.

data!!.length
반응형

'컴퓨터 > Android Studio' 카테고리의 다른 글

Android- 액션바ActionBar와 툴바Toobar  (0) 2022.03.03
Android- 플랫폼API, 제트팩 라이브러리  (0) 2022.03.03
Android- platfrom architecture  (0) 2022.03.03
Android- viewBinding  (0) 2022.03.02
Android- Permisson  (0) 2022.03.02
Comments