Kotlin을 배워보자 #3-추상클래스, 인터페이스, 코틀린 표준함수

이영준·2023년 3월 15일
0

코틀린 문법

목록 보기
3/4

📌Abstract Class

상속 받을 클래스에서 구현해야 할 프로퍼티 및 메서드를 기술한 클래스

  • 추상클래스는 객체 생성이 안된다
  • 하위 파생 클래스에서 구체적으로 구현한다
  • 기본적으로 open이 적용된다
// 추상 클래스, 주 생성자에는 비추상 프로퍼티 선언의 매개변수 3개가 있음
abstract class Vehicle(val name: String, val color: String, val weight: Double) {
    // 추상 프로퍼티 (반드시 하위 클래스에서 재정의해 초기화해야 함)
    abstract var maxSpeed: Double
    // 일반 프로퍼티 (초기 값인 상태를 저장할 수 있음)
    var year = "2018"
    // 추상 메서드 (반드시 하위 클래스에서 구현해야 함)
    abstract fun start()
    abstract fun stop()
    // 일반 메서드
    fun displaySpecs() {
        println("Name: $name, Color: $color, Weight: $weight, Year: $year, Max Speed: $maxSpeed") }
}
class Car(name: String, color: String, weight: Double, override var maxSpeed: Double) : Vehicle(name, color, weight) {
    override fun start() {
        println("Car Started") // 코드의 구현
    }
    override fun stop() {
        println("Car Stopped") // 코드의 구현
    }
}
class Motorcycle(name: String, color: String, weight: Double, override var maxSpeed: Double) : Vehicle(name, color, weight) {
    override fun start() {
        println("Bike Started") // 코드의 구현
    }
    override fun stop() {
        println("Bike Stopped") // 코드의 구현
    }
}
fun main() {
    val car = Car("SuperMatiz", "yellow", 1110.0, 270.0)
    val motor = Motorcycle("DreamBike", "red", 173.0, 100.0)
    car.year = "2013"
    car.displaySpecs()
    car.start()
    motor.displaySpecs()
    motor.start()
}

Name: SuperMatiz, Color: yellow, Weight: 1110.0, Year: 2013, Max Speed: 270.0
Car Started
Name: DreamBike, Color: red, Weight: 173.0, Year: 2018, Max Speed: 100.0
Bike Started

각각의 하위 클래스에서 오버라이딩 한 함수대로 출력이 된다

📌Interface

자바와 동일하게 구현부가 없는 method + default method의 집합으로

  • 자바와 달리 default 키워드는 없다
  • 애초에 상속을 목적으로 만들어진 클래스로 별도의 open 키워드가 필요없다.
  • 추상클래스와 달리 인터페이스에서는 프로퍼티의 상태 정보를 저장할 수 없다.
interface Clickable {
    fun click()
    fun showOff(){
        println("Clickable.showOff")
    }
}
interface Focusable{
    fun showOff(){
        println("Focusable.showOff")
    }
}
class Button : Clickable, Focusable {
    override fun click() {
        println("Button.click")
    }
    //showOff는 두 슈퍼에 모두 존재하므로, 동일하게 애매함을 없애기 위해서 반드시 선언.
    //super에 선언된 fun을 호출할때는 super<Clickable>.showOff()
    //java에서는 Clickable.super.showOff();
    override fun showOff() {
        println("Button.showOff")
    }
}

fun main() {
    var button = Button()
    button.click()
    button.showOff()
}

📌데이터 클래스

일반 클래스와 달리 소괄호로 프로퍼티를 선언하기만 한다.
자동으로 toString(), equals(), hashCode()를 만들어준다. (setter, getter 역시 자동생성)
즉, 자바의 DTO 클래스와 같은 역할을 하기 위해 사용한다.

data class Ticket(val comapyName : String, val name : String, var date : String, var seatNumber : Int)
class Ticket2(val comapyName : String, val name : String, var date : String, var seatNumber : Int)

//toString(), hanshCode(), equals(), copy()

fun main(){
    val tickets = Ticket("KoreanAir", "Lee", "2022-6-29",20)
    val tickets2 = Ticket2("KoreanAir", "Lee", "2022-6-29",20)
    println(tickets)
    println(tickets2)
}

결과:
Ticket(comapyName=KoreanAir, name=Lee,date=2022-6-29, seatNumber=20)
com.example.myapplication.Ticket2@2d6e8792

📌object class

object 키워드로 클래스를 만들면 싱글턴 클래스가 된다

  • 생성자를 갖지 않는다. (생성자가 private으로 선언되므로 생성자로 클래스를 선언할 수 없다는 의미)
  • 어느 클래스, 함수에서든 별도의 객체화 과정 없이 접근 가능
  • 프로그램이 실행되는 동안 저장된 데이터는 손실되지 않고, 프로그램이 종료되면 소멸
  • 안드로이드에서 액티비티, 프래그먼트를 구분하지 않고 데이터 전달을 할 수 있게 해줌
object MyObject {
    var name: String = ""
    var type: String = ""
    var age: Int = 0

    fun myType() {
        println("my name is $name and type is $type age is $age")
    }
}

fun main() {
    val name = "samsung"
    val type = "ENFP"
    val age = 23

    MyObject.name = name
    MyObject.type = type
    MyObject.age = age
    MyObject.myType()
    println("${MyObject.name} and ${MyObject.type} and ${MyObject.age}")
}

my name is samsung and type is ENFP age is 23
samsung and ENFP and 23

📌Enum Class

인자 없는 열거형 클래스

enum class SsafyHandsome {
    SSAFY, LOVE, PEACE
}

fun main() {
    val ssafyEnum: SsafyHandsome = SsafyHandsome.SSAFY
    println("${ssafyEnum.name} ... ${ssafyEnum.ordinal}")

    val ssafyEnum2: Array<SsafyHandsome> = SsafyHandsome.values()

    for (i in ssafyEnum2.indices) {
        println(ssafyEnum2[i].name)
    }
}

인자가 있는 열거형 클래스

enum class Human(val age: Int) {
    KIM(25), CHOI(21)
}

fun main() {
    val human: Human = Human.KIM
    println("${human.name}, ${human.age}, ${human.ordinal}")
}

📌Sealed Class

부모 클래스의 상속을 받는 자식 클래스의 종류를 제한하는 클래스이다.

  • sealed class에 정의된 하위 클래스 외의 다른 하위 클래스는 존재하지 않는다는 것을 컴파일러에게 알려준다.
  • sealed class 는 abstract 클래스로, 객체로 생성할 수 없다.
  • sealed class와 하위 클래스는 모두 동일한 파일에 정의되어야 한다,
  • 하위 클래스는 class, data class, object class로 정의할 수 있다.
package com.android.example.kotlinproject.kotlinadvanced

sealed class Color {
    object Red : Color()
    object Green : Color()
    object Blue : Color()
}

fun main() {
    val color: Color = Color.Red
//    val font = when (color) {
//        is Color.Red -> {
//            "Noto Sans"
//        }
//        is Color.Green -> {
//            "Open Sans"
//        }
//        // compile error!
//    }


    val font = when (color) {
        is Color.Red -> "Noto Sans"
        is Color.Green -> "Open Sans"
        is Color.Blue -> "sans-serif"
        // No error! 왜냐하면 Color형은 red,green,blue만 가진다고 sealed 클래스에서 알렸기 때문이다.
    }

    println("결정된 font는 : $font")
}

위 코드에서 when 절에서 else를 넣지 않아도 오류가 나지 않는다. 컴파일 타임에서 이미 color가 3개밖에 없다는 것을 sealed 클래스에서 인지시키고 있기 때문이다.

sealed class Expr
data class Const(val number: Double) : Expr()
data class Sum(val e1: Expr, val e2: Expr) : Expr()
object NotANumber : Expr()

다음처럼도 선언 가능

🔑Enum class vs Sealed Class

공통점

  • 둘 다 타입을 제한 할 때 사용

차이점

  • enum은 하나의 객체만 생성
  • sealed는 객체를 여러개 생성

📌Inner Class

코틀린에서는 단순히 클래스 안에 클래스를 정의하면 자바와 달리(자바는 내부클래스가 됨) 중첩 클래스가 된다. 이렇게 되면 바깥 클래스의 member에 접근할 수도 없다.

따라서 내부 클래스를 만들기 위해 inner 키워드로 클래스를 선언한다.

📌코틀린의 access modifier

  • private : 외부에서 접근 불가
  • protected : 상속 관계에 있을 때만 접근 가능
  • internal : 같은 모듈 내에서 접근 가능
  • public : 어드서든 접근 가능(기본값)

📌고차 함수

매개 변수나 함수의 반환 값으로 함수가 사용되는 함수

🔑 인자로 람다함수를 받기

인자로 람다함수를 받아서 정수값을 리턴하는 함수

private fun highOrderFunction(sum: (Int, Int) -> Int, a: Int, b: Int): Int = sum(a, b)
println(highOrderFunction({ x, y -> x + y }, 20, 30))

🔑인자로 일반함수를 받는 함수

ex) ::함수으로 일반함수 레퍼런스를 가져와 람다함수를 넣는 자리에 일반함수를 넣을 수 있다.

private fun sum(a: Int, b: Int): Int = a + b
private fun highOrderFunction(cal: (Int, Int) -> Int, a: Int, b: Int): Int = cal(a, b)
println(highOrderFunction(::sum, 20, 30))

🔑변수에 저장된 람다식 호출

val multiply:(Int, Int) -> Int = { x: Int, y: Int -> x * y }
    println("곱셈 결과 = ${multiply(8, 8)}")
    println("곱셈 결과 = ${highOrderFunction(multiply, 8,8)}")

🔑람다식 인자를 뒤로 빼기

println(highOrderFunction(20, 30, { x, y -> x + y })

와 같이 람다식이 마지막 인자라면

var result = highOrderFunction(20, 30){
            x, y -> x + y
    }
    println(result)
}

📌 표준함수

람다를 인자로 받아 동작하는 함수

🔑let

let()을 호출하는 객체의 람다식 안에 파라미터로 넘김
it을 통해 호출 객체에 접근
블럭의 결과값을 반환

var arr = arrayOf(1, 2, 3)
    var result = arr.let {
        println("${it[1] + it[2]}")
        it[1] + it[2]
    } //let 람다식 계산 결과를 리턴

5
5
더하기 결과: 6

🔑also

also()을 호출하는 객체의 람다식 안에 파라미터로 넘김
it을 통해 호출 객체에 접근
호출 객체 자체를 반환

var student2 = student.also {
        it.age = 15
        it.name = "kim"
    }

Student(name=kim, age=15)

🔑apply

apply()을 호출하는 객체의 람다식 안에 파라미터로 넘김
this를 통해 호출 객체에 접근
let, also와 달리 it이 아닌 this로 객체를 받음
this는 생각 가능

fun applyTest() {
    var student = Student2("Park", 11)

    var student2 = student.apply {
        age = 15 //this 생략
        name = "kim" //this 생략
    }
    println(student) //Student2(name=kim, age=15)
    println(student2) //Student2(name=kim, age=15)
}

🔑run

호출 객체를 파라미터를 넘기거나, 객체 없이 사용하는 방식 둘다 가능
블럭의 결과값을 반환
let, also와 달리 it이 아닌 this로 객체를 받음

fun runTest() {
    var a = 10
    var b = 15

    //객체없이 run 단독 사용
    var result = run {
        var c = a + b
        println(c) //25
        c
    }//더하기 작업 수행 후 결과 c 반환

    //객체에 run 사용.
    result = result.run {
        plus(5)
    }
    println(result) //30

25
30

🔑with

  • 인자로 받는 객체를 블럭에 파라미터로 전달
  • run 함수와 기능은 거의 동일
  • safe call 지원하지 않음 -> 종종 let과 함께 사용됨
fun withTest(){
    var people = People("Park", 15)

    var newAge = with(people){
        age = 20
        age
    }

    println(newAge) //20

}

profile
컴퓨터와 교육 그사이 어딘가

0개의 댓글