ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • 코틀린 기초 문법 ①
    KOTLIN 2022. 9. 26. 19:26

     

     

    요즘 코드가 Java -> Kotlin으로 넘어가고 있고, 사용이 많고  동기 분들을 보니 모두 코틀린 공부를 하고 계셔서 Kotlin 공부를 시작했다. 

    노션에 따로 적으면서 공부하긴 했지만, 블로그에도 함께 적으면 좋을 것 같아서 옮긴다.

    참고한 강의 및 책은 맨 밑에 함께 적어두었다!

     

     

     

     

    함수와 변수 

    코틀린에서의 변수

    • var : 변경 가능한 참조를 저장하는 변수
    • val : 변경 불가능한 참조를 저장하는 변수
    var a = 13
    a = 15
    
    val b = 13
    // 변경이 불가능하기 때문에 다음의 구문은 실행될 수 없다 
    // b = 15 
    

     

    코틀린에서의 함수

    • fun 키워드 사용
    • 파라미터 → ‘변수명 : 타입’
    • 리턴 타입 → 파라미터 뒤의 ’ : 타입’
    • void인 경우 (리턴값이 없는 경우) 리턴타입은 생략 가능
    fun add(a: Int, b: Int): Int {
        return a + b
    }
    
    • 식이 본문인 함수
    fun add(a: Int, b: Int): Int = a + b
    
    // 식이 본문인 함수의 경우 리턴 타입이 명확하기 때문에 생략이 가능하다 
    fun add(a: Int, b: Int) = a + b
    

     

     

     

     

     

     

     

    문자열

    코틀린에서 문자열 템플릿

    val a = "jay"
    val b = "ho"
    
    println("hihi ${a + b}")
    // hihi jayho
    println("hihi $a $b")
    // hihi jay ho
    println("hihi ${1 == 0}")
    // hihi false
    println("hihi s\\$name")
    // hihi s$name

     

    .length

    • 문자열의 길이를 반환
    "hello world".length //11

     

    .toUpperCase(), .toLowerCase()

    • .toUpperCase() : 대문자로 변환
    • .toLowerCase() : 소문자로 변환
    val s = "Hello World"
    
    println(s.toUpperCase())     // HELLO WORLD
    println(s.toLowerCase())     // hello world

     

    .joinToString()

    • 대상을 합쳐서 문자열로 반환
    • prefix, postfix, separator를 이용하여 값을 꾸며줄 수 있음

    예제 참고 사이트 : https://kotlinlang.org/api/latest/jvm/stdlib/kotlin.collections/join-to-string.html

     

    joinToString - Kotlin Programming Language

     

    kotlinlang.org

    val numbers = listOf(1, 2, 3, 4, 5, 6)
    println(numbers.joinToString()) // 1, 2, 3, 4, 5, 6
    println(numbers.joinToString(prefix = "[", postfix = "]")) // [1, 2, 3, 4, 5, 6]
    println(numbers.joinToString(prefix = "<", postfix = ">", separator = "•")) // <1•2•3•4•5•6>
    
    val chars = charArrayOf('a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q')
    println(chars.joinToString(limit = 5, truncated = "...!") { it.uppercaseChar().toString() }) // A, B, C, D, E, ...!

     

    .subString(i .. j)

    • 시작 ~ 끝 위치 만큼 문자열을 빼내어 나타냄
      val s = "kotlin"
      println(s.substring(0 until 5)) // kotli

     

    .isNullOrEmpty(), .isNullOrBlank()

    • 문자열이 null 이거나 빈값(””)인 경우를 확인
    • 문자열이 null 이거나 빈칸(” “)인 경우를 확인 (스페이스, tap 등 확인)
    var empty = ""
    var blank = "  "
    
    println(empty.isNullOrEmpty()) // true
    println(empty.isNullOrBlank()) // true
    println(blank.isNullOrBlank()) // true
    println(blank.isNullOrEmpty()) // false

     

    .startsWith(), .endsWith()

    • 문자열이 해당 문자열로 시작하는지 확인
    • 문자열이 해당 문자열로 끝나는지 확인
    var s = "kotlin"
    println(s.startsWith("k")) // true
    println(s.endsWith("n")) // true

     

    .contains()

    • 문자열이 다음의 문자열을 포함하는지 확인
    val s = "hello world"
    println(s.contains("d")) // true

     

     

     

     

     

     

     

    클래스 

    기본 생성자를 가진 클래스

    • 코틀린의 기본 가시성 → public
    • 기본 생성자(주 생성자)를 통해서 간단하게 작성이 가능
    • 생성자의 기본값은 = 을 이용해서 설정이 가능 (기본값으로 name : "Anonymous")
    • 변경이 불가능한 프로퍼티는 val, 변경이 가능한 프로퍼티는 var로 선언
    • 클래스 생성시 생성자를 정의하지 않으면 컴파일러가 자동으로 아무일도 하지 않는 인자가 없는 디폴트 생성자를 만듦
    class Human(val name: String = "Anonymous") {
    
    		fun eatingCake() {
            println("$name is eating cake.")
        }
    }

     

    Init

    • constructor보다 init이 먼저 실행 (init-> constructor 순으로)
    class Human(val name: String = "Anonymous") {
    
        init {
            println("New human has been born!!")
        }
    
        fun eatingCake() {
            println("$name is eating cake.")
        }
    }
    

     

    부 생성자 (sub constructor)

    • 다른 파라미터의 constructor를 만들고 싶다면 주 생성자(this(name))을 상속받아 작성
    class Human(val name: String = "Anonymous") {
    
        constructor(name: String, age: Int) : this(name){
            println("My name is $name, $age years old.")
        }
    
        init {
            println("New human has been born!!")
        }
    
        fun eatingCake() {
            println("$name is eating cake.")
        }
    }
    

     

    내부클래스(Inner class) 및 중첩클래스 (Nested class)

    • 강하게 연관된 도우미 클래스를 캡슐화 하거나 코드 정의를 사용처에 가까이 둘 때 사용

    - 내부 클래스

    • 클래스 안의 다른 클래스 선언
    • 외부 클래스에 대한 참조를 저장
    • 외부 클래스 객체가 존재해야만 생성 및 사용이 가능 (외부 클래스 함수 및 속성 사용 가능)
    • 내부 클래스에서 외부 클래스에 참조하려면 this@Outer 사용
    class Outer {
        inner class Inner {
            fun getOuterReference(): Outer = this@Outer
        }
    }

    -중첩클래스

    • 외부 클래스에 대한 참조를 저장하지 않음
    • 명시적 요청이 없는 한 바깥쪽 클래스 인스턴스에 대한 접근 권한이 없음 (독립적 존재, 외부 클래스 함수 및 속성 사용 불가)
    • 자바에서의 static class(정적 클래스)와 비슷

     

    Enum 클래스 

    • 코틀린에서 enum은 소프트 키워드 → class앞에서 특별한 의미를 지니지만 다른 곳에서는 이름에 사용 가능
    • enum 클래스 안에 프로퍼티나 메서드 정의가 가능
    • 각 enum에서 상수를 정의할 때는 그 상수에 해당하는 프로퍼티를 지정해야 함
    • enum 클래스 안에서 함수를 정의하는 경우 enum 상수 목록과 메서드 정의 사이에 세미콜론이 필수
    enum class Color(var r: Int, val g: Int, val b: Int) {
        RED(255, 0, 0),
        ORANGE(255, 165, 0),
        BLUE(0, 0, 255);
    
        fun rgb() = (r * 256 + g) * 256 + b
    }
    

     

     

    Data클래스

    • 클래스 앞에 data 표시를 이용하여 정의
    • toString, equals, hashCode를 알아서 오버라이딩
    data class Ticket(val name: String, val seatNumber: Int, val companyName:String, val date: String)
    
    fun main() {
        var ticket = Ticket("joyce", 8, "KoreanAir", "2022-12-20")
        var secondTicket = Ticket("joyce", 8, "KoreanAir", "2022-12-20")
    
    		println(ticket)
    		// Ticket(name=joyce, seatNumber=8, companyName=KoreanAir, date=2022-12-20)
    		println(ticket == secondTicket)
    		// true
    }
    
    • 추가로 copy(), componentX() 메서드를 가짐
    • copy() : 객체를 복사하면서 일부 프로퍼티의 변경을 가능하게 함
    • componentX() : 배열 또는 리스트에서 속성을 순서대로 반환
    data class Data(val name: String, val id: Int)
    val list = listOf(Data("a", 1), Data("b", 2), Data("c", 3))
      for ((a, b) in list) {
          println("$a $b")
      }
    // a 1
    // b 2
    // c 3
    

     

     

     

     

     

     

     

     

     

    상속
    • 기본적으로 final로 상속을 제한 (특별히 하위 클래스에서 오버라이드하게 의도한 클래스가 아니라면 제한)
    • ‘클래스 이름 : 상속할 클래스 ‘ 의 형태
    • 클래스의 경우 하나만 확장이 가능하지만 인터페이스의 경우 원하는 만큼 인터페이스 상속 가능
    • 클래스의 경우 기본 생성자를 받아 상위 클래스 이름 옆에 괄호() 가 들어가지만 인터페이스의 경우 상위 클래스 이름 옆에 아무 기호가 없음
    • open : 상속을 허락함을 의미
    • sealed : 상위 클래스를 상속한 하위 클래스 정의를 제한
    • final override : 하위의 클래스가 메소드를 다시 오버라이딩 하지 못하도록 제한

    클래스 상속

    open class Human(val name: String = "Anonymous") {
    
        constructor(name: String, age: Int) : this(name){
    				println("My name is $name, $age years old.")
        }
    
        init {
    				println("New human has been born!!")
        }
    
        fun eatingCake() {
    				println("$name is eating cake. This is so YUMMY!")
        }
    
        open fun singASong() {
    				println("$name is singing. lalala~")
        }
    }
    
    class Korean : Human() {
    
    // 부모의 메소드를 오버라이딩하면서 하위 클래스가 다시 오버라이딩 하지 못하도록 ㅈ
        final override fun singASong() {
            super.singASong()
    				println("$name is singing. 라라라~")
        }
    }
    
    fun main() {
        var human = Human("joyce")
        var stranger = Human()
    // init 메서드 실행 
    // New human has been born!!
    // New human has been born!!
    
        human.eatingCake()
    // joyce is eating cake. This is so YUMMY!
    
    		println("This human name is ${human.name}")
    // This human name is joyce
    		println("This stranger name is ${stranger.name}")
    // default 값을 이용한 출력 발생 
    // This stranger name is Anonymous
    
        val mom = Human("Kuri", 50)
    // init 메서드 및 부생성자 작동 
    // New human has been born!!
    // My name is Kuri, 50 years old.
    		println("This human name is ${mom.name}")
    // This human name is Kuri
    
        mom.singASong()
    // Kuri is singing. lalala~
    
        val daughter = Korean()
    // New human has been born!!
        daughter.singASong()
    // 상위 클래스의 singing(super상속) 및 override 한 메서드가 실행됨 
    // Anonymous is singing. lalala~
    // Anonymous is singing. 라라라~
    
    }
    
    
    • 서브클래스(Child)는 슈퍼클래스(Parent)에 존재하는 속성과 같은 이름의 속성을 가질 수 없음
    • 서브 클래스가 생성될 때는 반드시 슈퍼클래스의 생성자까지 호출 되어야 함
    class Parent (name: String, age: Int, gender: String) {
    	// ...
    }
    
    // 부모의 gender 파트를 추가하여 생성자 호출 
    class Child (name: String, age: Int) : Parent(name, age, "male") {
    	// ...
    }
    
    • 상위클래스를 다른 방식으로 초기화 가능
    • 클래스에 주 생성자가 없다면 모든 부 생성자는 반드시 상위 클래스를 초기화 하거나 다른 생성자에게 생성을 위임해야 함
    // 부 생성자만 2개를 가지는 부모 클래스 
    open class View {
        constructor(ctx: Context) {}
        constructor(ctx: Context, attr: AttributeSet) {}
    }
    
    // 상위 클래스의 생성자에게 생성을 위임 
    class MyButton1 : View {
        constructor(ctx: Context) : super(ctx) {}
    
        constructor(ctx: Context, attr: AttributeSet) : super(ctx, attr) {}
    }
    
    // ctx만 받는 생성자는 자기 자신의 생성자에게 생성을 위임하고, 두 파라미터를 받는 생성자가 상위 클래스에게 위임 
    class MyButton2 : View {
        constructor(ctx: Context) : this(ctx, MY_STYLE) {}
        constructor(ctx: Context, attr: AttributeSet) : super(ctx, attr) {}
    }
    

     

     

    인터페이스 상속

    • default 없이 디폴트 구현이 있는 메서드를 정의 가능
    • 여러개의 인터페이스를 상속받는 경우 상위 인터페이스들에 중복되는는 메서드가 존재하면 하위 에서 이를 대체할 오버라이드 메서드를 제공해야 함 (안하면 컴파일러 오류 발생)
    • 상위 타입의 구현을 호출할 때에는 super<상위 타입>.메서드()의 형식으로 구현
    interface Clickable {
        fun showOff() = println("I'm clickable!")
    }
    
    interface Focusable {
        fun showOff() = println("I'm focusable!")
    }
    
    class Button : Clickable, Focusable {
        override fun showOff() {
            super<Clickable>.showOff()
            super<Focusable>.showOff()
        }
    }
    

     

     

     

    추상클래스 상속

    • 추상 클래스의 경우 하위클래스에서 추상멤버를 오버라이드해야 하므로 상속에 항상 열려있음
    • 추상 클래스는 인스턴스화 불가
    • 추상 클래스에 속하더라도 비추상 함수는 기본적으로 파이널
    abstract class Animated {
    
    // 오버라이딩에 열려있음(abstract)
        abstract fun animated()
    
    // 오버라이딩이 제한됨(기본 final)
        fun stopAnimating() {
            // ...
        }
    }
    

     

     

     

    sealed 클래스의 하위 클래스(중첩 클래스)

    • 상위 클래스에 sealed 변경자 사용시 상위 클래스를 상속한 하위 클래스(중첩 클래스로 구현) 정의를 제한
    • sealed 표시된 클래스는 자동으로 open
    // 상위 클래스를 sealed로 봉인 
    sealed class Expr {
    // 중첩 클래스로 하위 클래스 정의 
        class Num(val value: Int) : Expr()
        class Sum(val left: Expr, val right: Expr) : Expr()
    }
    
    // 기존의 when 구문에 else 처리가 필요했던 것과 다르게 디폴트 분기 처리가 필요 없음 
    fun eval(e: Expr): Int = when (e) {
        is Expr.Num -> e.value
        is Expr.Sum -> eval(e.left) + eval(e.right)
    }
    

     

     

     

     

     

     

     

     

     

     

     

    when
    • 자바의 switch 구문의 역할
    • 코틀린의 비교 연산자 중 when에서는 등호 부등호 사용이 제한
    • when의 인자는 Any 타입으로 아무 객체나 사용이 가능
    • when에 아무 인자도 없기 위해서는 각 분기의 조건이 불리언 결과를 계산하는 식이어야 함
    fun checkNum(score: Int) {
    // score에 따라서 결과가 달라진다 
        when (score) {
            0 -> println("This is zero")
            1 -> println("This is one")
            else -> println("I don't know")
        }
    
    // 값을 정의할 때의 when 
        var b = when (score) {
            1 -> 2
            2 -> 3
            4 -> 7
            else -> 10
        }
    
    // 범위내의 여부를 확인하는 when 
        when (score) {
            in 0..2 -> println("min num")
            in 3..6 -> println("mid num")
    				else -> println("I don't know")
        }
    }
    

     

     

     

     

     

     

     

    while, for
    • 반복문 (iteration) 이 필요한 경우 사용됨

    while 과 do-while

    • while : 조건이 참이 경우 본문을 반복 실행
    • do-while : 맨 처음 무조건 본문을 한번 실행하고 조건이 참인 경우 반복 실행
    while (조건) {
    	// ...
    }
    
    do {
    	// ...
    } while (조건)

     

    for 문

    • 시작 숫자 .. 끝 숫자 : 시작 숫자 ≤ x ≤ 끝 숫자의 범위를 나타냄
    • step : 증가 값을 조절
    • downTo : 역방향 수열을 생성
    • until : 시작 숫자 ≤ x < 끝 숫자의 범위를 나타냄
    • in : 컬렉션에 대한 이터레이션, 범위의 원소 검사를 위해 사용
    • 맵에서 키와 값을 이용한 iteration 가능
    • Collection.withIndex를 이용하면 파이썬의 enumerate처럼 인덱스, 값을 사용할 수 있음
    • 중첩 for문의 경우 loop를 이용해서 중첩문을 한번에 빠져나올 수 있음
    fun forExample() {
    
        var sum = 0
        for (i in 1..10) {
            sum += i
        }
        print(sum)
    // 55
    
        for (i in 1..10 step 2) {
            print(i)
        }
    // 1 3 5 7 9
    
        for (i in 10 downTo 1) {
            print(i)
        }
    // 10 9 8 7 6 5 4 3 2 1
    
        *for (i in 1 until 10) {
            print(i)
        }*
    // 1 2 3 4 5 6 7 8 9
    
        val students = arrayListOf("joyce", "james", "jenny", "jennifer")
        for (name in students) {
            print("$name")
        }
    // joyce james jenny jennifer
    
        for ((index, name) in students.withIndex()) {
            println("${index + 1}번째 학생은 ${name}입니다")
        }
    // 1번째 학생은 joyce입니다
    // 2번째 학생은 james입니다
    // 3번째 학생은 jenny입니다
    // 4번째 학생은 jennifer입니다
    
        loop@ for (i in 1 until 3) {
            for (j in 1 until 5) {
                if (i == 2 && j == 3) break@loop
                print(j)
            }
        }
    }

     

     

     

     

     

     

     

    캐스팅

    스마트 캐스팅

    • 어떤 변수가 원하는 타입인지 일단 is로 검사하면 그 변수가 원하는 타입으로 선언된 것 처럼 사용이 가능
    • 실제로는 컴파일러가 캐스팅을 수행
    • 단, 원하는 타입(호환되는 타입)으로 명시적 타입 캐스팅을 하려면 as 키워드 사용
    if (e is Sum) { // 변수 e에 대해 스마트 캐스팅 발생 
    	return eval(e.right) + eval(e.left)
    }
    

     

    as 캐스팅

    • 어떤 값을 지정한 타입으로 캐스트 하는 것
    • 대상 값을 as로 지정한 타입으로 바꿀 수 없으면 ClassCastException이 발생 하기 때문에 as변환 전에 is로 타입 체크

     

     

     

     

     

     

     

     

     

    참고한 강의 
     

    [무료] 코틀린 3강으로 끝내기 feat. 안드로이드 개발 - 인프런 | 강의

    3강으로 짧게 끝내는 코틀린 문법! 코틀린 기초 문법과 안드로이드 기초 프로그래밍은 물론, 직접 안드로이드 앱도 만들어보세요!, - 강의 소개 | 인프런...

    www.inflearn.com

     

     

     

    참고한 책 
     

    Kotlin in Action - YES24

    코틀린이 안드로이드 공식 언어가 되면서 관심이 커졌다. 이 책은 코틀린 언어를 개발한 젯브레인의 코틀린 컴파일러 개발자들이 직접 쓴 일종의 공식 서적이라 할 수 있다. 코틀린 언어의 가장

    www.yes24.com

     

     

     

     

     

     

     

     

     

     

     

Designed by Tistory.